mirror of
https://github.com/complexcaresolutions/dak.c2s.git
synced 2026-03-17 18:23:42 +00:00
feat: add disclosure request UI and field visibility for dak_mitarbeiter
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f2219b487b
commit
62a56f3fc9
2 changed files with 98 additions and 20 deletions
|
|
@ -22,6 +22,7 @@ import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||||
import { CASE_SECTIONS } from './cases/fieldConfig'
|
import { CASE_SECTIONS } from './cases/fieldConfig'
|
||||||
import { useInlineEdit } from './cases/useInlineEdit'
|
import { useInlineEdit } from './cases/useInlineEdit'
|
||||||
import { EditableField } from './cases/EditableField'
|
import { EditableField } from './cases/EditableField'
|
||||||
|
import { requestDisclosure } from '@/services/disclosureService'
|
||||||
|
|
||||||
const FALLGRUPPEN_LABELS: Record<string, string> = {
|
const FALLGRUPPEN_LABELS: Record<string, string> = {
|
||||||
onko: 'Onkologie',
|
onko: 'Onkologie',
|
||||||
|
|
@ -396,6 +397,18 @@ function CaseDetail({
|
||||||
<StatusBadges c={caseData} />
|
<StatusBadges c={caseData} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Disclosure status / request */}
|
||||||
|
{!isAdmin && caseData.disclosure_granted && caseData.disclosure_expires_at && (
|
||||||
|
<Alert>
|
||||||
|
<AlertDescription>
|
||||||
|
Personendaten sichtbar bis {formatDateTime(caseData.disclosure_expires_at)}
|
||||||
|
</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
{!isAdmin && !caseData.disclosure_granted && (
|
||||||
|
<DisclosureRequestButton caseId={caseData.id} />
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Static metadata (always read-only) */}
|
{/* Static metadata (always read-only) */}
|
||||||
<div className="grid grid-cols-2 gap-3 text-sm">
|
<div className="grid grid-cols-2 gap-3 text-sm">
|
||||||
<ReadOnlyField label="Fall-ID" value={caseData.fall_id} />
|
<ReadOnlyField label="Fall-ID" value={caseData.fall_id} />
|
||||||
|
|
@ -405,23 +418,30 @@ function CaseDetail({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Editable sections from config */}
|
{/* Editable sections from config */}
|
||||||
{CASE_SECTIONS.map((section) => (
|
{CASE_SECTIONS.map((section) => {
|
||||||
<div key={section.title} className="border-t pt-3 space-y-3">
|
const visibleFields = section.fields.filter((field) => {
|
||||||
<h3 className="text-sm font-semibold">{section.title}</h3>
|
if (field.visibleTo === 'admin' && !isAdmin && !caseData.disclosure_granted) return false
|
||||||
<div className="grid grid-cols-2 gap-3 text-sm">
|
return true
|
||||||
{section.fields.map((field) => (
|
})
|
||||||
<div key={field.key} className={field.colSpan === 2 ? 'col-span-2' : ''}>
|
if (visibleFields.length === 0) return null
|
||||||
<EditableField
|
return (
|
||||||
field={field}
|
<div key={section.title} className="border-t pt-3 space-y-3">
|
||||||
value={editing ? formValues[field.key] : caseData[field.key]}
|
<h3 className="text-sm font-semibold">{section.title}</h3>
|
||||||
editable={canEditField(field, isAdmin)}
|
<div className="grid grid-cols-2 gap-3 text-sm">
|
||||||
onChange={setFieldValue}
|
{visibleFields.map((field) => (
|
||||||
/>
|
<div key={field.key} className={field.colSpan === 2 ? 'col-span-2' : ''}>
|
||||||
</div>
|
<EditableField
|
||||||
))}
|
field={field}
|
||||||
|
value={editing ? formValues[field.key] : caseData[field.key]}
|
||||||
|
editable={canEditField(field, isAdmin)}
|
||||||
|
onChange={setFieldValue}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
))}
|
})}
|
||||||
|
|
||||||
{/* ICD section — own endpoint with validation */}
|
{/* ICD section — own endpoint with validation */}
|
||||||
<div className="border-t pt-3 space-y-2">
|
<div className="border-t pt-3 space-y-2">
|
||||||
|
|
@ -474,6 +494,63 @@ function CaseDetail({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DisclosureRequestButton({ caseId }: { caseId: number }) {
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const [reason, setReason] = useState('')
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [error, setError] = useState('')
|
||||||
|
const [success, setSuccess] = useState(false)
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
if (!reason.trim()) return
|
||||||
|
setLoading(true)
|
||||||
|
setError('')
|
||||||
|
try {
|
||||||
|
await requestDisclosure(caseId, reason.trim())
|
||||||
|
setSuccess(true)
|
||||||
|
setOpen(false)
|
||||||
|
} catch (err: any) {
|
||||||
|
const detail = err.response?.data?.detail
|
||||||
|
setError(typeof detail === 'string' ? detail : 'Fehler beim Senden der Anfrage')
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
return <p className="text-sm text-muted-foreground">Freigabe-Anfrage gesendet.</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button size="sm" variant="outline" onClick={() => setOpen(true)}>
|
||||||
|
Personendaten anfordern
|
||||||
|
</Button>
|
||||||
|
{open && (
|
||||||
|
<div className="space-y-2 border rounded-lg p-3">
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Begründung für die Einsicht in Personendaten:
|
||||||
|
</p>
|
||||||
|
<Input
|
||||||
|
value={reason}
|
||||||
|
onChange={(e) => setReason(e.target.value)}
|
||||||
|
placeholder="z.B. KVNR-Fehler, Identifikation nötig"
|
||||||
|
/>
|
||||||
|
{error && <p className="text-sm text-red-600">{error}</p>}
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button size="sm" onClick={submit} disabled={loading || !reason.trim()}>
|
||||||
|
{loading ? 'Senden...' : 'Anfrage senden'}
|
||||||
|
</Button>
|
||||||
|
<Button size="sm" variant="outline" onClick={() => { setOpen(false); setError('') }}>
|
||||||
|
Abbrechen
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function ReadOnlyField({ label, value }: { label: string; value: string | null | undefined }) {
|
function ReadOnlyField({ label, value }: { label: string; value: string | null | undefined }) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ export interface FieldConfig {
|
||||||
type: FieldType
|
type: FieldType
|
||||||
colSpan?: 1 | 2
|
colSpan?: 1 | 2
|
||||||
editableBy: 'admin' | 'all'
|
editableBy: 'admin' | 'all'
|
||||||
|
visibleTo?: 'admin' | 'all'
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
options?: { value: string; label: string }[]
|
options?: { value: string; label: string }[]
|
||||||
}
|
}
|
||||||
|
|
@ -28,10 +29,10 @@ export const CASE_SECTIONS: SectionConfig[] = [
|
||||||
{
|
{
|
||||||
title: 'Persönliche Daten',
|
title: 'Persönliche Daten',
|
||||||
fields: [
|
fields: [
|
||||||
{ key: 'anrede', label: 'Anrede', type: 'select', editableBy: 'admin', options: ANREDE_OPTIONS },
|
{ key: 'anrede', label: 'Anrede', type: 'select', editableBy: 'admin', visibleTo: 'admin', options: ANREDE_OPTIONS },
|
||||||
{ key: 'vorname', label: 'Vorname', type: 'text', editableBy: 'admin', placeholder: 'Vorname' },
|
{ key: 'vorname', label: 'Vorname', type: 'text', editableBy: 'admin', visibleTo: 'admin', placeholder: 'Vorname' },
|
||||||
{ key: 'nachname', label: 'Nachname', type: 'text', editableBy: 'admin', placeholder: 'Nachname' },
|
{ key: 'nachname', label: 'Nachname', type: 'text', editableBy: 'admin', visibleTo: 'admin', placeholder: 'Nachname' },
|
||||||
{ key: 'geburtsdatum', label: 'Geburtsdatum', type: 'date', editableBy: 'admin' },
|
{ key: 'geburtsdatum', label: 'Geburtsdatum', type: 'date', editableBy: 'admin', visibleTo: 'admin' },
|
||||||
{ key: 'kvnr', label: 'KVNR', type: 'text', editableBy: 'all', placeholder: 'z.B. A123456789' },
|
{ key: 'kvnr', label: 'KVNR', type: 'text', editableBy: 'all', placeholder: 'z.B. A123456789' },
|
||||||
{ key: 'versicherung', label: 'Versicherung', type: 'text', editableBy: 'admin', placeholder: 'z.B. DAK' },
|
{ key: 'versicherung', label: 'Versicherung', type: 'text', editableBy: 'admin', placeholder: 'z.B. DAK' },
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue