Files
AfiS-Aktensystem/web/app/(app)/dokumente/[documentNumber]/DocumentActions.tsx
T
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

165 lines
5.1 KiB
TypeScript

'use client'
import { useState, useTransition } from 'react'
import { caseText } from '@/lib/i18n/cases'
import { actionErrorsText, clearanceText, commonText } from '@/lib/i18n/common'
import { documentText } from '@/lib/i18n/documents'
import {
finalizeDocumentAction,
reclassifyDocumentAction,
retractDocumentAction,
} from '../actions'
/**
* Ausfertigen, Zurückziehen, Umklassifizieren.
*
* Wie beim Fall bewusst **nicht** optimistisch: alle drei sind folgenreich.
* Das Ausfertigen fragt nach, das Zurückziehen verlangt eine Begründung - es
* gibt kein Löschen, der Grabstein bleibt stehen.
*/
export function DocumentActions({
documentNumber,
classification,
canFinalize,
canRetract,
canReclassify,
levels,
}: {
documentNumber: string
classification: number
canFinalize: boolean
canRetract: boolean
canReclassify: boolean
levels: { level: number; name: string }[]
}) {
const [reason, setReason] = useState('')
const [retracting, setRetracting] = useState(false)
const [nextLevel, setNextLevel] = useState(classification)
const [levelReason, setLevelReason] = useState('')
const [error, setError] = useState<string | null>(null)
const [pending, startTransition] = useTransition()
if (!canFinalize && !canRetract && !canReclassify) return null
const lowering = nextLevel < classification
function run(action: () => Promise<{ ok: boolean; error?: string }>) {
setError(null)
startTransition(async () => {
const result = await action()
if (!result.ok) setError(result.error ?? actionErrorsText.default!)
else setRetracting(false)
})
}
return (
<div className="mt-6 flex flex-wrap gap-x-10 gap-y-5 border-t border-line pt-5 text-xs">
{canFinalize ? (
<div>
<button
type="button"
disabled={pending}
onClick={() => {
if (!confirm(documentText.finalizeHint)) return
run(() => finalizeDocumentAction({ documentNumber }))
}}
className="btn btn-primary"
>
{documentText.finalize}
</button>
</div>
) : null}
{canReclassify ? (
<div>
<p className="font-condensed text-text-dim">{documentText.reclassify}</p>
<div className="mt-1.5 flex flex-wrap items-center gap-2">
<select
value={nextLevel}
onChange={(event) => setNextLevel(Number(event.target.value))}
aria-label={clearanceText.classification}
className="field"
>
{levels.map((level) => (
<option key={level.level} value={level.level}>
{level.level} · {level.name}
</option>
))}
</select>
<button
type="button"
disabled={pending || nextLevel === classification}
onClick={() =>
run(() =>
reclassifyDocumentAction({
documentNumber,
to: nextLevel,
reason: levelReason,
}),
)
}
className="btn btn-primary"
>
{commonText.save}
</button>
</div>
{lowering ? (
<textarea
value={levelReason}
onChange={(event) => setLevelReason(event.target.value)}
rows={2}
placeholder={caseText.reclassifyReason}
aria-label={caseText.reclassifyReason}
className="mt-2 block w-full max-w-sm field text-2xs"
/>
) : null}
</div>
) : null}
{canRetract ? (
<div>
{retracting ? (
<div>
<textarea
value={reason}
onChange={(event) => setReason(event.target.value)}
rows={2}
placeholder={documentText.retractReason}
aria-label={documentText.retractReason}
className="block w-full max-w-sm field text-2xs"
/>
<div className="mt-2 flex gap-3">
<button
type="button"
disabled={pending || reason.trim().length < 10}
onClick={() => run(() => retractDocumentAction({ documentNumber, reason }))}
className="btn btn-sm text-stamp-4"
>
{documentText.retract}
</button>
<button
type="button"
onClick={() => setRetracting(false)}
className="btn btn-sm"
>
{commonText.cancel}
</button>
</div>
</div>
) : (
<button
type="button"
onClick={() => setRetracting(true)}
className="btn btn-sm"
>
{documentText.retract}
</button>
)}
</div>
) : null}
{error ? <p className="w-full font-condensed text-2xs text-stamp-4">{error}</p> : null}
</div>
)
}