New "KI" tab on case detail page with three sub-panels: - KI-Strategie: one-click strategic analysis with next steps, risks, timeline - KI-Entwurf: document drafting with template selection, language, instructions - Aehnliche Faelle: UPC similar case search with relevance scores Components: CaseStrategy, DocumentDrafter, SimilarCaseFinder Types: StrategyRecommendation, DocumentDraft, SimilarCase, etc.
199 lines
6.4 KiB
TypeScript
199 lines
6.4 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useMutation } from "@tanstack/react-query";
|
|
import { api } from "@/lib/api";
|
|
import type { DocumentDraft, DraftDocumentRequest } from "@/lib/types";
|
|
import { FileText, Loader2, Copy, Check, Download } from "lucide-react";
|
|
|
|
const TEMPLATES = {
|
|
klageschrift: "Klageschrift",
|
|
klageerwiderung: "Klageerwiderung",
|
|
abmahnung: "Abmahnung",
|
|
schriftsatz: "Schriftsatz",
|
|
berufung: "Berufungsschrift",
|
|
antrag: "Antrag",
|
|
stellungnahme: "Stellungnahme",
|
|
gutachten: "Gutachten",
|
|
vertrag: "Vertrag",
|
|
vollmacht: "Vollmacht",
|
|
upc_claim: "UPC Statement of Claim",
|
|
upc_defence: "UPC Statement of Defence",
|
|
upc_counterclaim: "UPC Counterclaim for Revocation",
|
|
upc_injunction: "UPC Provisional Measures",
|
|
} as const;
|
|
|
|
const LANGUAGES = [
|
|
{ value: "de", label: "Deutsch" },
|
|
{ value: "en", label: "English" },
|
|
{ value: "fr", label: "Francais" },
|
|
] as const;
|
|
|
|
const inputClass =
|
|
"w-full rounded-md border border-neutral-200 bg-white px-3 py-2 text-sm text-neutral-900 outline-none transition-colors focus:border-neutral-400 focus:ring-1 focus:ring-neutral-400";
|
|
|
|
interface DocumentDrafterProps {
|
|
caseId: string;
|
|
}
|
|
|
|
export function DocumentDrafter({ caseId }: DocumentDrafterProps) {
|
|
const [templateType, setTemplateType] = useState("");
|
|
const [instructions, setInstructions] = useState("");
|
|
const [language, setLanguage] = useState("de");
|
|
const [copied, setCopied] = useState(false);
|
|
|
|
const mutation = useMutation({
|
|
mutationFn: (req: DraftDocumentRequest) =>
|
|
api.post<DocumentDraft>("/ai/draft-document", req),
|
|
});
|
|
|
|
function handleSubmit(e: React.FormEvent) {
|
|
e.preventDefault();
|
|
if (!templateType) return;
|
|
mutation.mutate({
|
|
case_id: caseId,
|
|
template_type: templateType,
|
|
instructions,
|
|
language,
|
|
});
|
|
}
|
|
|
|
function handleCopy() {
|
|
if (mutation.data?.content) {
|
|
navigator.clipboard.writeText(mutation.data.content);
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 2000);
|
|
}
|
|
}
|
|
|
|
function handleDownload() {
|
|
if (!mutation.data?.content) return;
|
|
const blob = new Blob([mutation.data.content], { type: "text/plain;charset=utf-8" });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = `${templateType}_entwurf.txt`;
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<form onSubmit={handleSubmit} className="space-y-3">
|
|
<div>
|
|
<label className="mb-1 block text-xs font-medium text-neutral-500">
|
|
Dokumenttyp
|
|
</label>
|
|
<select
|
|
value={templateType}
|
|
onChange={(e) => setTemplateType(e.target.value)}
|
|
className={inputClass}
|
|
disabled={mutation.isPending}
|
|
>
|
|
<option value="">Dokumenttyp waehlen...</option>
|
|
{Object.entries(TEMPLATES).map(([key, label]) => (
|
|
<option key={key} value={key}>
|
|
{label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1 block text-xs font-medium text-neutral-500">
|
|
Sprache
|
|
</label>
|
|
<select
|
|
value={language}
|
|
onChange={(e) => setLanguage(e.target.value)}
|
|
className={inputClass}
|
|
disabled={mutation.isPending}
|
|
>
|
|
{LANGUAGES.map((lang) => (
|
|
<option key={lang.value} value={lang.value}>
|
|
{lang.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1 block text-xs font-medium text-neutral-500">
|
|
Anweisungen (optional)
|
|
</label>
|
|
<textarea
|
|
value={instructions}
|
|
onChange={(e) => setInstructions(e.target.value)}
|
|
placeholder="z.B. 'Fokus auf Patentanspruch 1, besonders die technischen Merkmale...'"
|
|
rows={3}
|
|
className={inputClass}
|
|
disabled={mutation.isPending}
|
|
/>
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={!templateType || mutation.isPending}
|
|
className="inline-flex items-center gap-2 rounded-md bg-neutral-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-neutral-800 disabled:opacity-50"
|
|
>
|
|
{mutation.isPending ? (
|
|
<>
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
Dokument wird erstellt...
|
|
</>
|
|
) : (
|
|
<>
|
|
<FileText className="h-4 w-4" />
|
|
KI-Entwurf erstellen
|
|
</>
|
|
)}
|
|
</button>
|
|
</form>
|
|
|
|
{mutation.isError && (
|
|
<div className="rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
|
|
Fehler beim Erstellen des Entwurfs. Bitte versuchen Sie es erneut.
|
|
</div>
|
|
)}
|
|
|
|
{mutation.data && (
|
|
<div className="space-y-3">
|
|
<div className="flex items-center justify-between">
|
|
<h4 className="text-sm font-medium text-neutral-900">
|
|
{mutation.data.title}
|
|
</h4>
|
|
<div className="flex items-center gap-2">
|
|
<button
|
|
onClick={handleCopy}
|
|
className="inline-flex items-center gap-1 rounded-md border border-neutral-200 px-2.5 py-1.5 text-xs font-medium text-neutral-600 transition-colors hover:bg-neutral-50"
|
|
>
|
|
{copied ? (
|
|
<>
|
|
<Check className="h-3.5 w-3.5 text-emerald-500" />
|
|
Kopiert
|
|
</>
|
|
) : (
|
|
<>
|
|
<Copy className="h-3.5 w-3.5" />
|
|
Kopieren
|
|
</>
|
|
)}
|
|
</button>
|
|
<button
|
|
onClick={handleDownload}
|
|
className="inline-flex items-center gap-1 rounded-md border border-neutral-200 px-2.5 py-1.5 text-xs font-medium text-neutral-600 transition-colors hover:bg-neutral-50"
|
|
>
|
|
<Download className="h-3.5 w-3.5" />
|
|
Download
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<pre className="max-h-[600px] overflow-auto whitespace-pre-wrap rounded-md border border-neutral-200 bg-neutral-50 p-4 text-sm text-neutral-800">
|
|
{mutation.data.content}
|
|
</pre>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|