feat: AcroForm-Fill via Claude CLI, Multi-Source, Kanagawa, Docker-Deploy
Komplettes Rework der AI-Studio-Vorlage zu einem produktiven Werkzeug fuer
deutsche AcroForm-Formulare (Reha-Antraege, Arzt-Befundberichte):
- Backend: Express spawnt headless Claude CLI ('claude -p --output-format json'
via stdin-Pipe). Prompt enthaelt die Feldnamen als Ziel-Schema plus die
Arbeitsregeln (Stichwortstil, feste Zeichen-Kaestchen ohne Leerzeichen,
Vordrucke respektieren, keine geratenen Werte, nur medizinisch).
- PDF-Handling: pdfjs-dist statt pdf-lib — pdf-lib scheitert an verschluesselten
Object-Streams in DRV-Formularen. annotationStorage + saveDocument, kein
Flatten. Worker-Patch zur Laufzeit forciert Auto-Size und schwarze Schrift.
- Multi-Source-Upload: beliebig viele PDFs/Bilder + optional Freitext.
- Design: Kanagawa Design System (Preset aus ../kanagawa-design-system),
Tailwind lokal gebaut statt CDN, Dark/Light-Toggle, Progress-Indicator.
- Deployment: Multi-Stage-Dockerfile, docker-compose in matrix_default-Netz,
Claude-Credentials vom Host per Volume. PLAN.md + AGENTS.md (Alex-Schema).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d6cab4aeb5
commit
3c669fb003
28 changed files with 6756 additions and 934 deletions
48
services/api.ts
Normal file
48
services/api.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import type { FileData, FormResponse } from '../types';
|
||||
import type { PdfFieldInfo } from './pdfService';
|
||||
|
||||
export async function processDocuments(
|
||||
formFile: FileData,
|
||||
sourceFiles: FileData[],
|
||||
sourceText: string,
|
||||
pdfFields: PdfFieldInfo[]
|
||||
): Promise<FormResponse> {
|
||||
if (pdfFields.length === 0) {
|
||||
throw new Error(
|
||||
'Das Ziel-PDF enthält keine AcroForm-Felder. ' +
|
||||
'Nur Formulare mit interaktiven Feldern werden unterstützt.'
|
||||
);
|
||||
}
|
||||
if (sourceFiles.length === 0 && sourceText.trim().length === 0) {
|
||||
throw new Error('Mindestens ein Quelldokument oder Text wird benötigt.');
|
||||
}
|
||||
|
||||
const body = new FormData();
|
||||
body.append('form', formFile.file, formFile.file.name);
|
||||
for (const f of sourceFiles) {
|
||||
body.append('sources', f.file, f.file.name);
|
||||
}
|
||||
if (sourceText.trim().length > 0) {
|
||||
body.append('sourceText', sourceText);
|
||||
}
|
||||
body.append('fields', JSON.stringify(pdfFields));
|
||||
|
||||
const res = await fetch('/api/process', {
|
||||
method: 'POST',
|
||||
body,
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
let message = `Server antwortete mit ${res.status}`;
|
||||
try {
|
||||
const data = await res.json();
|
||||
if (data?.error) message = data.error;
|
||||
if (data?.details) message += ` — ${data.details}`;
|
||||
} catch {
|
||||
// fall through
|
||||
}
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return (await res.json()) as FormResponse;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue