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
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die README; an keiner Stelle Bedeutung, nur Zeichensetzung.
132 lines
3.7 KiB
TypeScript
132 lines
3.7 KiB
TypeScript
'use client'
|
|
|
|
import { useActionState, useState } from 'react'
|
|
import { clearanceText, commonText } from '@/lib/i18n/common'
|
|
import { documentText } from '@/lib/i18n/documents'
|
|
import { createDocumentAction, type ActionResult } from '@/app/(app)/dokumente/actions'
|
|
|
|
/**
|
|
* Dokument anlegen, an einer Fallakte oder an einer Personalakte.
|
|
*
|
|
* Die Vorlagenliste kommt vom Server und enthält nur, was an dieser Stelle
|
|
* erlaubt ist. Geprüft wird das trotzdem noch einmal beim Anlegen - die
|
|
* Auswahl im Formular ist eine Bequemlichkeit, keine Sicherung.
|
|
*/
|
|
export function NewDocument({
|
|
templates,
|
|
levels,
|
|
defaultLevel,
|
|
caseNumber,
|
|
personnelFileId,
|
|
}: {
|
|
templates: { key: string; name: string; description: string | null; layout: string }[]
|
|
levels: { level: number; name: string }[]
|
|
defaultLevel: number
|
|
caseNumber?: string
|
|
personnelFileId?: string
|
|
}) {
|
|
const [open, setOpen] = useState(false)
|
|
const [state, action, pending] = useActionState<ActionResult | null, FormData>(
|
|
createDocumentAction,
|
|
null,
|
|
)
|
|
|
|
if (templates.length === 0) return null
|
|
|
|
if (!open) {
|
|
return (
|
|
<button
|
|
type="button"
|
|
onClick={() => setOpen(true)}
|
|
className="btn btn-sm btn-primary"
|
|
>
|
|
{documentText.create}
|
|
</button>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<form action={action} className="border border-line p-4">
|
|
{caseNumber ? <input type="hidden" name="caseNumber" value={caseNumber} /> : null}
|
|
{personnelFileId ? (
|
|
<input type="hidden" name="personnelFileId" value={personnelFileId} />
|
|
) : null}
|
|
|
|
<p className="font-condensed text-xs text-text-dim">{documentText.createTitle}</p>
|
|
|
|
<div className="mt-3 grid gap-4 sm:grid-cols-2">
|
|
<div>
|
|
<label htmlFor="templateKey" className="field-label">
|
|
{documentText.kind}
|
|
</label>
|
|
<select
|
|
id="templateKey"
|
|
name="templateKey"
|
|
required
|
|
className="mt-1 block w-full field"
|
|
>
|
|
{templates.map((template) => (
|
|
<option key={template.key} value={template.key}>
|
|
{template.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label htmlFor="classification" className="field-label">
|
|
{clearanceText.classification}
|
|
</label>
|
|
<select
|
|
id="classification"
|
|
name="classification"
|
|
defaultValue={defaultLevel}
|
|
className="mt-1 block w-full field"
|
|
>
|
|
{levels.map((level) => (
|
|
<option key={level.level} value={level.level}>
|
|
{level.level} · {level.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="sm:col-span-2">
|
|
<label htmlFor="doc-title" className="field-label">
|
|
{documentText.title}
|
|
</label>
|
|
<input
|
|
id="doc-title"
|
|
name="title"
|
|
maxLength={200}
|
|
className="mt-1 block w-full field"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="mt-2 text-2xs text-text-dim">{documentText.createHint}</p>
|
|
|
|
{state && !state.ok ? (
|
|
<p className="mt-2 font-condensed text-2xs text-stamp-4">{state.error}</p>
|
|
) : null}
|
|
|
|
<div className="mt-3 flex gap-3">
|
|
<button
|
|
type="submit"
|
|
disabled={pending}
|
|
className="btn btn-sm btn-primary"
|
|
>
|
|
{pending ? commonText.loading : documentText.create}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => setOpen(false)}
|
|
className="btn btn-sm"
|
|
>
|
|
{commonText.cancel}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
)
|
|
}
|