Merge pull request 'refactor(positions): קיפול "עמדות וטענות", איזון שורה תחתונה, שם-כרטיס מדויק (#226)' (#397) from worktree-positions-collapse-balance into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 43s
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 #397.
This commit is contained in:
2026-07-06 09:35:44 +00:00
2 changed files with 34 additions and 69 deletions

View File

@@ -157,11 +157,28 @@ export default function CaseDetailPage({
issues, with the aggregated by-party arguments collapsible below
(merged from the deleted /compose editor, mockup 18f). */}
<TabsContent value="arguments" className="mt-0 space-y-4">
<Card className="bg-surface border-rule shadow-sm">
<CardContent className="px-6 py-5">
<PositionsPanel caseNumber={caseNumber} />
</CardContent>
</Card>
{/* עמדות וטענות — collapsed by default (chair preference #226),
mirroring the two accordions below. */}
<Accordion type="single" collapsible>
<AccordionItem
value="positions"
className="overflow-hidden rounded-xl border border-rule bg-surface shadow-sm"
>
<AccordionTrigger className="px-6 py-4 hover:no-underline">
<span className="flex flex-1 flex-col items-start">
<span className="text-navy text-[0.95rem] font-bold">
עמדות וטענות
</span>
<span className="text-ink-muted text-[0.78rem]">
סוגיות-המחלוקת ועמדות הצדדים · עורך עמדת-היו״ר מזין את בלוק י׳
</span>
</span>
</AccordionTrigger>
<AccordionContent className="px-6 pb-5 pt-0">
<PositionsPanel caseNumber={caseNumber} />
</AccordionContent>
</AccordionItem>
</Accordion>
<Accordion type="single" collapsible>
<AccordionItem
value="byparty"

View File

@@ -8,10 +8,6 @@ import { SubsectionCard } from "@/components/compose/subsection-card";
import { PrecedentsSection } from "@/components/compose/precedents-section";
import { Markdown } from "@/components/ui/markdown";
import { useCase } from "@/lib/api/cases";
import {
useCaseLearningStatus,
type CaseLearningStatus,
} from "@/lib/api/learning";
import { useResearchAnalysis } from "@/lib/api/research";
import { useCasePrecedents } from "@/lib/api/precedents";
@@ -24,45 +20,6 @@ import { useCasePrecedents } from "@/lib/api/precedents";
* verification moved to their own top-level tabs; /compose was deleted.
*/
// ── Staged-pipeline indicator text — 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 }) {
if (!content?.trim()) return null;
return (
@@ -75,7 +32,9 @@ function ProseSection({ title, content }: { title: string; content?: string }) {
);
}
// ── "השלמה והעברה" rail card — DOCX export, analysis upload/download (real) ──
// ── "ייצוא ועדכון הניתוח" rail card — round-trips the analysis-and-research.md
// research analysis: export DOCX, upload an updated version, download the raw MD.
// (Post-final learning status lives on the drafts tab, not here — #226.) ──────
function FinishRail({
caseNumber,
hasAnalysis,
@@ -88,7 +47,6 @@ function FinishRail({
const fileRef = useRef<HTMLInputElement>(null);
const [uploading, setUploading] = useState(false);
const [uploadMsg, setUploadMsg] = useState<{ ok: boolean; text: string } | null>(null);
const learning = useCaseLearningStatus(caseNumber);
async function handleUpload(file: File) {
setUploading(true);
@@ -119,9 +77,12 @@ function FinishRail({
}
return (
<Card className="bg-surface border-rule shadow-sm">
<Card className="bg-surface border-rule shadow-sm h-full">
<CardContent className="px-4 py-4">
<h3 className="text-navy text-[0.9rem] font-semibold mb-3">השלמה והעברה</h3>
<h3 className="text-navy text-[0.9rem] font-semibold mb-1">ייצוא ועדכון הניתוח</h3>
<p className="text-[0.72rem] text-ink-muted mb-3">
פעולות על קובץ ניתוח-המחקר (analysis-and-research.md).
</p>
<input
ref={fileRef}
@@ -176,16 +137,6 @@ function FinishRail({
{uploadMsg.text}
</p>
)}
{/* stage indicators — informational pointers, not actions */}
<div className="mt-3 space-y-0">
<div className="text-[0.78rem] text-ink-muted pt-2 border-t border-rule-soft">
<b className="text-navy">הרץ למידת-קול</b> {voiceLearningText(learning.data)}
</div>
<div className="text-[0.78rem] text-ink-muted pt-2 mt-2 border-t border-rule-soft">
<b className="text-navy">הרץ אימות-הלכות</b> {halachaExtractionText(learning.data)}
</div>
</div>
</CardContent>
</Card>
);
@@ -214,10 +165,6 @@ export function PositionsPanel({ caseNumber }: { caseNumber: string }) {
return (
<div className="space-y-4">
<p className="text-ink-muted text-[0.78rem]">
סוגיות-המחלוקת ועמדות הצדדים. עורך עמדת-היו״ר נשמר אוטומטית ומזין את בלוק י׳ (דיון והכרעה).
</p>
{analysis.isPending ? (
<Card className="bg-surface border-rule shadow-sm">
<CardContent className="px-6 py-5 space-y-3">
@@ -322,9 +269,10 @@ export function PositionsPanel({ caseNumber }: { caseNumber: string }) {
</div>
) : null}
{/* case-level פסיקה מצורפת + finish/export gates (INV-IA3: not removed) */}
<div className="grid gap-4 lg:grid-cols-2 items-start pt-2">
<Card className="bg-surface border-rule shadow-sm">
{/* case-level פסיקה מצורפת + analysis export/update (INV-IA3: not removed).
items-stretch keeps both cards the same height (#226). */}
<div className="grid gap-4 lg:grid-cols-2 items-stretch pt-2">
<Card className="bg-surface border-rule shadow-sm h-full">
<CardContent className="px-5 py-4">
<h3 className="text-navy text-[0.9rem] font-semibold mb-1">פסיקה מצורפת</h3>
<p className="text-[0.72rem] text-ink-muted mb-3">