Merge pull request 'fix(compose): wire staged-pipeline indicators to live learning-status' (#336) from worktree-compose-learning-indicators into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 44s
G12 Leak-Guard / leak-guard (push) Successful in 4s
Lint — undefined names / undefined-names (push) Successful in 10s

This commit was merged in pull request #336.
This commit is contained in:
2026-06-28 17:55:02 +00:00

View File

@@ -14,6 +14,10 @@ import { DecisionBlocksPanel } from "@/components/cases/decision-blocks-panel";
import { Markdown } from "@/components/ui/markdown"; import { Markdown } from "@/components/ui/markdown";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { useCase, type CaseStatus } from "@/lib/api/cases"; import { useCase, type CaseStatus } from "@/lib/api/cases";
import {
useCaseLearningStatus,
type CaseLearningStatus,
} from "@/lib/api/learning";
import { useResearchAnalysis } from "@/lib/api/research"; import { useResearchAnalysis } from "@/lib/api/research";
import { useCasePrecedents } from "@/lib/api/precedents"; import { useCasePrecedents } from "@/lib/api/precedents";
import { APPEAL_SUBTYPES } from "@/lib/practice-area"; import { APPEAL_SUBTYPES } from "@/lib/practice-area";
@@ -52,6 +56,45 @@ function subtypeLabel(subtype?: string | null): string | null {
return APPEAL_SUBTYPES.find((s) => s.value === subtype)?.label ?? null; return APPEAL_SUBTYPES.find((s) => s.value === subtype)?.label ?? null;
} }
// ── Staged-pipeline indicator text (mockup 03) — derived from the live
// learning-status, same source as the drafts-panel LearningStatusBadges. ──────
function voiceLearningText(s?: CaseLearningStatus): string {
if (!s?.final_uploaded) return "ממתין להעלאת הסופי";
const v = s.voice_learning;
if (v.outcome === "succeeded") {
const bits = [`${v.lessons_count} לקחים הופקו`];
if (v.lessons_proposed > 0) bits.push(`${v.lessons_proposed} הוצעו לאישור`);
return `✓ הושלם · ${bits.join(" · ")}`;
}
if (v.outcome === "failed") return v.error ? `✗ נכשל — ${v.error}` : "✗ נכשל";
return "ממתין להרצה";
}
function halachaExtractionText(s?: CaseLearningStatus): string {
if (!s?.final_uploaded) return "ממתין להעלאת הסופי";
const h = s.halacha_extraction;
if (!h.enrolled_in_corpus)
return h.not_enrolled_reason ?? "לא נכנס לקורפוס-הפסיקה";
switch (h.status) {
case "completed":
return `✓ הושלם · חולצו ${h.halachot_count} · ${h.approved} אושרו · ${h.rejected} נדחו`;
case "processing":
return "רץ עכשיו…";
case "pending":
case "busy":
return "בתור";
case "partial":
return `חלקי · חולצו ${h.halachot_count}`;
case "failed":
case "extraction_failed":
return "✗ נכשל";
case "no_chunks":
return "אין טקסט לחילוץ";
default:
return "ממתין להרצה";
}
}
function ProseSection({ title, content }: { title: string; content?: string }) { function ProseSection({ title, content }: { title: string; content?: string }) {
if (!content?.trim()) return null; if (!content?.trim()) return null;
return ( return (
@@ -77,6 +120,7 @@ function FinishRail({
const fileRef = useRef<HTMLInputElement>(null); const fileRef = useRef<HTMLInputElement>(null);
const [uploading, setUploading] = useState(false); const [uploading, setUploading] = useState(false);
const [uploadMsg, setUploadMsg] = useState<{ ok: boolean; text: string } | null>(null); const [uploadMsg, setUploadMsg] = useState<{ ok: boolean; text: string } | null>(null);
const learning = useCaseLearningStatus(caseNumber);
async function handleUpload(file: File) { async function handleUpload(file: File) {
setUploading(true); setUploading(true);
@@ -168,10 +212,10 @@ function FinishRail({
{/* mockup 03: stage indicators — informational pointers, not actions */} {/* mockup 03: stage indicators — informational pointers, not actions */}
<div className="mt-3 space-y-0"> <div className="mt-3 space-y-0">
<div className="text-[0.78rem] text-ink-muted pt-2 border-t border-rule-soft"> <div className="text-[0.78rem] text-ink-muted pt-2 border-t border-rule-soft">
<b className="text-navy">הרץ למידת-קול</b> ממתין להעלאת הסופי <b className="text-navy">הרץ למידת-קול</b> {voiceLearningText(learning.data)}
</div> </div>
<div className="text-[0.78rem] text-ink-muted pt-2 mt-2 border-t border-rule-soft"> <div className="text-[0.78rem] text-ink-muted pt-2 mt-2 border-t border-rule-soft">
<b className="text-navy">הרץ אימות-הלכות</b> ממתין להעלאת הסופי <b className="text-navy">הרץ אימות-הלכות</b> {halachaExtractionText(learning.data)}
</div> </div>
</div> </div>