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.
95 lines
3.1 KiB
TypeScript
95 lines
3.1 KiB
TypeScript
'use client'
|
|
|
|
import { useActionState, useEffect, useState } from 'react'
|
|
import { exportText } from '@/lib/i18n/export'
|
|
import { useStream } from '@/components/realtime/useStream'
|
|
import { requestExportAction, type ExportState } from '@/app/(app)/export/actions'
|
|
|
|
/**
|
|
* Export anfordern und auf das Ergebnis warten.
|
|
*
|
|
* Kein Fortschrittsbalken, der eine Zahl erfindet: der Auftrag hat drei
|
|
* Zustände, und die stehen da. Fertig meldet der Ereignisstrom, dann steht
|
|
* der Knopf zum Herunterladen bereit - automatisch losladen wäre unhöflich,
|
|
* wenn jemand die Seite inzwischen wieder verlassen hat und zurückkommt.
|
|
*/
|
|
export function ExportPanel({
|
|
target,
|
|
topic,
|
|
}: {
|
|
target: { kind: 'CASE'; caseNumber: string } | { kind: 'DOCUMENT'; documentNumber: string }
|
|
/** Eigenes Nutzerthema: der Export geht nur den Auftraggeber an. */
|
|
topic: string
|
|
}) {
|
|
const [state, action, pending] = useActionState<ExportState | null, FormData>(
|
|
requestExportAction,
|
|
null,
|
|
)
|
|
const [fertig, setFertig] = useState<string | null>(null)
|
|
const [gescheitert, setGescheitert] = useState(false)
|
|
|
|
useStream([topic], {
|
|
'export.ready': (data) => {
|
|
const ereignis = data as { id: string }
|
|
if (state?.jobId && ereignis.id !== state.jobId) return
|
|
setFertig(ereignis.id)
|
|
},
|
|
})
|
|
|
|
// Nach der Freigabe eines wartenden Auftrags kommt das Ereignis später; bis
|
|
// dahin bleibt der Hinweis stehen.
|
|
useEffect(() => {
|
|
if (state?.status === 'READY' && state.jobId) setFertig(state.jobId)
|
|
if (state?.status === 'FAILED') setGescheitert(true)
|
|
}, [state])
|
|
|
|
const wartetAufFreigabe = state?.status === 'PENDING_APPROVAL'
|
|
|
|
return (
|
|
<div className="border border-line p-4">
|
|
<form action={action}>
|
|
<input type="hidden" name="kind" value={target.kind} />
|
|
{target.kind === 'CASE' ? (
|
|
<input type="hidden" name="caseNumber" value={target.caseNumber} />
|
|
) : (
|
|
<input type="hidden" name="documentNumber" value={target.documentNumber} />
|
|
)}
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={pending || Boolean(state?.jobId)}
|
|
className="btn btn-sm btn-primary"
|
|
>
|
|
{pending ? exportText.preparing : exportText.asPdf}
|
|
</button>
|
|
</form>
|
|
|
|
<div className="mt-3 text-2xs">
|
|
{state?.error ? <p className="text-stamp-4">{state.error}</p> : null}
|
|
|
|
{wartetAufFreigabe ? (
|
|
<div>
|
|
<p className="font-condensed text-stamp-3">{exportText.waitingApproval}</p>
|
|
<p className="mt-1 text-text-dim">{exportText.waitingApprovalHint}</p>
|
|
</div>
|
|
) : null}
|
|
|
|
{state?.jobId && !fertig && !wartetAufFreigabe && !gescheitert ? (
|
|
<p className="text-text-dim">{exportText.preparing}</p>
|
|
) : null}
|
|
|
|
{gescheitert ? <p className="text-stamp-4">{exportText.failed}</p> : null}
|
|
|
|
{fertig ? (
|
|
<a
|
|
href={`/api/exports/${fertig}`}
|
|
className="border border-line bg-raised px-3 py-1 font-condensed text-accent"
|
|
>
|
|
{exportText.download}
|
|
</a>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|