Add upload endpoint for updated analysis files
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 34s

PUT /api/cases/{n}/research/analysis/upload accepts a markdown file and
validates: UTF-8 encoding, parseable structure, at least one threshold
or issue section, matching case number. Backs up existing file before
replacing. UI adds "העלה ניתוח מעודכן" button with status feedback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 19:34:06 +00:00
parent 9da8dd2c4f
commit b248e1414d
2 changed files with 172 additions and 19 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { use } from "react";
import { use, useRef, useState } from "react";
import Link from "next/link";
import { AppShell } from "@/components/app-shell";
import { Card, CardContent } from "@/components/ui/card";
@@ -25,6 +25,91 @@ function ProseSection({ title, content }: { title: string; content?: string }) {
);
}
function AnalysisActions({
caseNumber,
hasAnalysis,
onUploaded,
}: {
caseNumber: string;
hasAnalysis: boolean;
onUploaded: () => void;
}) {
const fileRef = useRef<HTMLInputElement>(null);
const [uploading, setUploading] = useState(false);
const [uploadMsg, setUploadMsg] = useState<{ ok: boolean; text: string } | null>(null);
async function handleUpload(file: File) {
setUploading(true);
setUploadMsg(null);
try {
const form = new FormData();
form.append("file", file);
const res = await fetch(`/api/cases/${caseNumber}/research/analysis/upload`, {
method: "PUT",
body: form,
});
const data = await res.json();
if (!res.ok) {
setUploadMsg({ ok: false, text: data.detail || "שגיאה בהעלאה" });
return;
}
setUploadMsg({
ok: true,
text: `הקובץ הועלה בהצלחה — ${data.sections.threshold_claims} טענות סף, ${data.sections.issues} סוגיות`,
});
onUploaded();
} catch {
setUploadMsg({ ok: false, text: "שגיאת רשת" });
} finally {
setUploading(false);
if (fileRef.current) fileRef.current.value = "";
}
}
return (
<div className="flex items-center gap-2 flex-wrap">
{uploadMsg && (
<span className={`text-xs ${uploadMsg.ok ? "text-green-700" : "text-red-600"}`}>
{uploadMsg.text}
</span>
)}
<input
ref={fileRef}
type="file"
accept=".md"
className="hidden"
onChange={(e) => {
const f = e.target.files?.[0];
if (f) handleUpload(f);
}}
/>
<Button
variant="outline"
disabled={uploading}
onClick={() => fileRef.current?.click()}
>
{uploading ? "מעלה..." : "העלה ניתוח מעודכן"}
</Button>
{hasAnalysis && (
<Button
variant="outline"
onClick={() => {
const a = document.createElement("a");
a.href = `/api/cases/${caseNumber}/research/analysis/download`;
a.download = `analysis-${caseNumber}.md`;
a.click();
}}
>
הורד ניתוח
</Button>
)}
<Button asChild variant="outline">
<Link href={`/cases/${caseNumber}`}>חזרה לתיק</Link>
</Button>
</div>
);
}
export default function ComposePage({
params,
}: {
@@ -78,24 +163,7 @@ export default function ComposePage({
</p>
)}
</div>
<div className="flex items-center gap-2">
{analysis.data && (
<Button
variant="outline"
onClick={() => {
const a = document.createElement("a");
a.href = `/api/cases/${caseNumber}/research/analysis/download`;
a.download = `analysis-${caseNumber}.md`;
a.click();
}}
>
הורד ניתוח
</Button>
)}
<Button asChild variant="outline">
<Link href={`/cases/${caseNumber}`}>חזרה לתיק</Link>
</Button>
</div>
<AnalysisActions caseNumber={caseNumber} hasAnalysis={!!analysis.data} onUploaded={() => analysis.refetch()} />
</div>
<div className="h-[2px] bg-gradient-to-l from-transparent via-gold to-transparent" />