Files
Harlekin e0b03ea336
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Gedankenstriche durch Bindestriche ersetzt
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die
README; an keiner Stelle Bedeutung, nur Zeichensetzung.
2026-09-04 08:54:52 +02:00

137 lines
4.4 KiB
TypeScript

'use client'
import { useState, useTransition } from 'react'
import Link from 'next/link'
import { commonText } from '@/lib/i18n/common'
import { participantText } from '@/lib/i18n/cases'
import { PARTICIPANT_ROLES } from '@/lib/cases/terms'
import { AddPanel, PanelSelect } from '@/components/AddPanel'
import { addParticipantAction, removeParticipantAction } from '../actions'
type Beteiligter = {
id: string
role: string
userId: string | null
displayName: string | null
personnelFileId: string | null
}
/**
* Beteiligte einer Akte.
*
* Der Hinweis über dem Formular ist wichtig und steht deshalb dort und nicht
* in einer Hilfeseite: wer hier eingetragen wird, liest die Akte unabhängig
* von seiner Freigabestufe.
*/
export function Participants({
caseNumber,
participants,
canEdit,
members,
files,
}: {
caseNumber: string
participants: Beteiligter[]
canEdit: boolean
members: { id: string; displayName: string }[]
files: { id: string; displayName: string }[]
}) {
const [role, setRole] = useState<string>('INVESTIGATOR')
const [target, setTarget] = useState('')
const [fehler, setFehler] = useState<string | null>(null)
const [pending, startTransition] = useTransition()
return (
<div>
{participants.length === 0 ? (
<p className="text-text-dim">{participantText.empty}</p>
) : (
<ul className="border-t border-line">
{participants.map((person) => (
<li
key={person.id}
className="record-row border-b border-line py-2"
>
<span className="record-label">
{participantText.roles[person.role] ?? person.role}
</span>
<span className="record-title text-sm">
{person.personnelFileId ? (
<Link href={`/personalakten/${person.personnelFileId}`}>
{person.displayName ?? commonText.unknown}
</Link>
) : (
(person.displayName ?? commonText.unknown)
)}
</span>
{canEdit ? (
<button
type="button"
disabled={pending}
onClick={() =>
startTransition(async () => {
const ergebnis = await removeParticipantAction({
caseNumber,
participantId: person.id,
})
if (!ergebnis.ok) setFehler(ergebnis.error)
})
}
className="btn btn-sm"
>
{participantText.remove}
</button>
) : null}
</li>
))}
</ul>
)}
{fehler ? <p className="mt-2 font-condensed text-2xs text-stamp-4">{fehler}</p> : null}
{canEdit ? (
<AddPanel
label={participantText.add}
hint={participantText.hint}
disabled={!target}
onSubmit={async () => {
const [art, id] = target.split(':')
const ergebnis = await addParticipantAction({
caseNumber,
role,
...(art === 'user' ? { userId: id } : { personnelFileId: id }),
})
if (ergebnis.ok) setTarget('')
return ergebnis
}}
>
<PanelSelect value={role} onChange={setRole} label={participantText.role}>
{PARTICIPANT_ROLES.map((value) => (
<option key={value} value={value}>
{participantText.roles[value] ?? value}
</option>
))}
</PanelSelect>
<PanelSelect value={target} onChange={setTarget} label={participantText.person}>
<option value="">-</option>
<optgroup label={participantText.pickUser}>
{members.map((member) => (
<option key={member.id} value={`user:${member.id}`}>
{member.displayName}
</option>
))}
</optgroup>
<optgroup label={participantText.pickPersonnel}>
{files.map((file) => (
<option key={file.id} value={`file:${file.id}`}>
{file.displayName}
</option>
))}
</optgroup>
</PanelSelect>
</AddPanel>
) : null}
</div>
)
}