diff --git a/docs/new-company-setup-guide.md b/docs/new-company-setup-guide.md new file mode 100644 index 0000000..c9da18b --- /dev/null +++ b/docs/new-company-setup-guide.md @@ -0,0 +1,403 @@ +# מדריך הקמת חברה חדשה — היטלי השבחה (CMPA) + +> נוצר: 2026-04-15 +> מטרה: תיעוד מפורט של התהליך להקמת קורפוס אימון והגדרת חברה בשתי המערכות + +--- + +## רקע + +המערכת שלנו בנויה מ-**2 חברות** (boards) ב-Paperclip, שמייצגות את שני תחומי העבודה העיקריים: + +| # | חברה | קוד | Prefix | סוגי תיקים | סטטוס קורפוס | +|---|-------|------|--------|------------|---------------| +| 1 | רישוי ובנייה | CMP | `42a7acd0...` | 1xxx | 24 החלטות אימון, ניתוח סגנון מלא | +| 2 | היטלי השבחה + פיצויים | CMPA | `8639e837...` | 8xxx, 9xxx | **ריק — אין אף החלטת אימון** | + +**המצב היום**: חברת CMPA כבר קיימת ב-Paperclip ומופתה בקוד (ניתוב אוטומטי לפי מספר תיק). אבל אין לה **קורפוס אימון** — המערכת לא מכירה את הסגנון של דפנה בהחלטות היטל השבחה ולא יכולה לחפש תקדימים. + +**מה שצריך לעשות**: להעלות את ההחלטות, לעבד אותן, ולהריץ ניתוח סגנון — בדיוק כמו שנעשה עם 24 ההחלטות של רישוי ובנייה. + +--- + +## שתי המערכות — הגדרת תפקידים + +### מערכת 1: עוזר משפטי (Legal-AI) + +**תפקיד**: מערכת הידע, הניתוח והניסוח — מחזיקה את כל התוכן המשפטי ומספקת כלים לכתיבת החלטות. + +**מה חי רק במערכת הזו**: + +| רכיב | תיאור | טבלת DB | +|-------|--------|---------| +| תיקים (Cases) | מספר תיק, כותרת, סטטוס, צדדים | `cases` | +| מסמכי מקור | כתבי ערר, תגובות, פרוטוקולים (PDF/DOCX) | `documents` + filesystem | +| חלקים סמנטיים (Chunks) | embeddings לחיפוש RAG (Voyage AI, 1024 ממדים) | `document_chunks` + pgvector | +| קורפוס אימון | החלטות קודמות של דפנה — גרסאות מנוקות | `style_corpus` | +| דפוסי סגנון | ביטויי מעבר, נוסחאות פתיחה/סיום, מבנה ניתוח | `style_patterns` | +| בלוקי החלטה | 12 בלוקים (מבנה ההחלטה) + פסקאות | `decision_blocks`, `decision_paragraphs` | +| טענות צדדים | טענות שחולצו מכתבי טענות | `claims` | +| תקדימים (פסיקה) | ספריית case law + embeddings | `case_law`, `case_law_embeddings` | +| חקיקה | סעיפי חוק שאוזכרו | `statutory_provisions` | +| הערות יו"ר | feedback של דפנה על טיוטות | `chair_feedback` | +| לקחים | תובנות שחולצו מ-feedback | `lessons_learned` | +| צ'קליסטים | רשימות בדיקה לבלוק דיון (לפי סוג ערר) | hardcoded ב-`lessons.py` | +| מיפוי חברות | קישור appeal_subtype ← company_id | `tag_company_mappings` | + +**שירותי הליבה**: +- **RAG** — חיפוש סמנטי בתקדימים ובמסמכי מקור, מסונן לפי `appeal_subtype` +- **Proofreading** — ניקוי מסמכי נבו מ-artifacts +- **Style Analysis** — ניתוח קורפוס וחילוץ דפוסי כתיבה +- **Decision Drafting** — ייצור טיוטות לפי ארכיטקטורת 12 בלוקים +- **DOCX Export** — מסמך מעוצב מוכן להגשה + +--- + +### מערכת 2: Paperclip + +**תפקיד**: מערכת התזמור והסוכנים — מנהלת את תהליך העבודה, מפעילה סוכני AI, ומספקת ממשק Kanban. + +**מה חי רק במערכת הזו**: + +| רכיב | תיאור | טבלת DB | +|-------|--------|---------| +| חברות (Companies) | CMP (רישוי), CMPA (היטלי השבחה) — boards נפרדים | `companies` | +| פרויקטים | כרטיס Kanban לכל תיק | `projects` | +| Issues | משימות עבודה (CMP-123, CMPA-456) | `issues` | +| תגובות | דיון בין סוכנים ומשתמשים | `issue_comments` | +| סוכנים (Agents) | CEO, Researcher, Writer — Claude Code agents | מערכת agents | +| SOUL.md | הנחיות לכל סוכן | קונפיגורציית agent | +| Skills | workflows לשימוש חוזר (SKILL.md) | `company_skills` + filesystem | +| Plugin state | נתוני plugin (case_number ← issue) | `plugin_state` | + +**תפקידי הליבה**: +- **תזמור** — CEO agent מקבל בקשות, מנתב לסוכן המתאים +- **ניהול משימות** — Kanban board עם issues, מעקב סטטוס +- **הפעלת סוכנים** — wakeup mechanism, heartbeat cycle +- **ממשק דיון** — comments על issues (משתמש ← agent ← agent) + +--- + +### תהליכי גומלין — מי מדבר עם מי + +``` +┌──────────────────────────────────────────────────────────────────────────┐ +│ תהליכי גומלין │ +│ │ +│ LEGAL-AI PAPERCLIP │ +│ ════════ ═════════ │ +│ │ +│ ┌─────────┐ יצירת project+issue ┌─────────┐ │ +│ │ Cases │ ─────── DB insert ──────→ │Projects │ │ +│ │ │ ─────── DB insert ──────→ │ Issues │ │ +│ └─────────┘ └─────────┘ │ +│ │ +│ ┌─────────┐ wakeup signal ┌─────────┐ │ +│ │Workflow │ ─────── HTTP POST ───────→ │ CEO │ │ +│ │ Start │ (issueId + mutation) │ Agent │ │ +│ └─────────┘ └─────────┘ │ +│ │ +│ ┌─────────┐ קריאת case_number ┌─────────┐ │ +│ │ Data │ ←──── plugin_state ────── │ Plugin │ │ +│ │ (API) │ ←──── HTTP GET/POST ───── │legal-ai │ │ +│ └─────────┘ (תקדימים, טענות, סגנון) └─────────┘ │ +│ │ +│ ┌─────────┐ skill sync ┌─────────┐ │ +│ │ Skills │ ──── DB + filesystem ────→ │company_ │ │ +│ │ (disk) │ │ skills │ │ +│ └─────────┘ └─────────┘ │ +│ │ +│ ┌─────────┐ שאילתת חברות ┌─────────┐ │ +│ │Settings │ ←──── DB query ────────── │companies│ │ +│ │ UI │ │ table │ │ +│ └─────────┘ └─────────┘ │ +└──────────────────────────────────────────────────────────────────────────┘ +``` + +#### כיוון 1: Legal-AI → Paperclip (יצירה ושליטה) + +| פעולה | מנגנון | מתי | +|-------|--------|-----| +| יצירת Project | DB insert ישיר ב-Paperclip | יצירת תיק חדש | +| יצירת Issue | DB insert ישיר ב-Paperclip | יצירת תיק / התחלת workflow | +| קישור case ← issue | DB insert ב-`plugin_state` | יצירת project | +| הערת אימות | DB insert ב-`issue_comments` | אחרי יצירת project | +| הפעלת CEO | **HTTP POST** ל-`/api/agents/{id}/wakeup` | התחלת workflow | +| סנכרון skill | DB insert/update ב-`company_skills` | התקנת/עדכון skill | + +#### כיוון 2: Paperclip → Legal-AI (שאילתות וקריאות חזרה) + +| פעולה | מנגנון | מתי | +|-------|--------|-----| +| קריאת case_number | plugin קורא `plugin_state` | סוכן מקבל issue | +| שליפת מסמכים | HTTP GET/POST ל-API של legal-ai | סוכן עובד על תיק | +| חיפוש תקדימים | HTTP ל-`/api/precedents/search` | researcher מחפש | +| קריאת style guide | HTTP ל-MCP / API | writer כותב טיוטה | +| רשימת חברות | DB query ישיר מ-`companies` | UI הגדרות | + +#### החוליה המקשרת: `plugin_state` + +``` +plugin_state: + plugin_id = "53461b5a..." (marcusgroup.legal-ai) + scope_kind = "issue" + scope_id = "{issue-uuid}" + state_key = "legal-case-number" + value_json = "\"1234\"" +``` + +זו ה"כתובת" שמאפשרת לסוכן Paperclip לדעת איזה תיק ב-Legal-AI שייך ל-issue שהוא עובד עליו. + +--- + +### מצב קיים לכל חברה + +#### CMP — רישוי ובנייה (מוכן לעבודה) + +**ב-Legal-AI**: +- 24 החלטות אימון בקורפוס +- ניתוח סגנון מלא (דפוסים, ביטויים, יחסי אורך) +- content checklists ל-3 סוגי משנה (substantive, threshold, property) +- RAG פעיל עם chunks + embeddings + +**ב-Paperclip**: +- חברה CMP פעילה +- סוכנים מוגדרים ופעילים +- Plugin פעיל +- Skills מותקנים + +#### CMPA — היטלי השבחה (דורש הקמה) + +**ב-Legal-AI**: +- appeal_subtype `betterment_levy` מוגדר בקוד +- ניתוב אוטומטי (8xxx → CMPA) עובד +- **חסר**: 0 החלטות אימון, 0 style patterns, 0 chunks, אין content checklist + +**ב-Paperclip**: +- חברה CMPA קיימת +- **לוודא**: סוכנים מקושרים, plugin פעיל, skills מותקנים + +--- + +## התהליך המלא — צעד אחר צעד + +### שלב 1: הכנת הקבצים + +**מיקום**: הנח את כל קבצי ה-DOCX בתיקייה נגישה (למשל `~/Downloads/hitlei-hashbacha/`) + +**בדיקות מקדימות**: +1. וודא שכל הקבצים בפורמט DOCX או PDF +2. וודא שהשמות כוללים מספר תיק (לצורך metadata) +3. ספור כמה החלטות יש — זה ישפיע על זמן העיבוד + +**דגשים**: +- ההחלטות מגיעות מנבו — יש להן watermarks, headers, footnotes שצריך לנקות +- מערכת ה-proofreading שלנו מטפלת בזה אוטומטית + +--- + +### שלב 2: העלאה — 3 נתיבים אפשריים + +#### נתיב א: ממשק Web (מומלץ להעלאה המונית) + +``` +כתובת: https://legal-ai.nautilus.marcusgroup.org +נתיב: /api/training/upload +``` + +**מה קורה מאחורי הקלעים**: +1. הקובץ נשמר כ-temp file +2. **Proofreading** — ניקוי אוטומטי של תוספות נבו: + - הסרת watermarks ("ספרות:", "חקיקה שאוזכרה:") + - הסרת headers/footers של עמודים + - הסרת קודי נבו inline + - הסרת URLs וזכויות יוצרים +3. **שמירת גרסה מנוקה** → `data/training/proofread/{filename}.md` +4. **שמירת מקור** → `data/training/{filename}.docx` +5. **הוספה ל-DB** → טבלת `style_corpus` עם metadata +6. **חיתוך לחלקים** → chunks סמנטיים +7. **יצירת embeddings** → Voyage AI → וקטורים 1024 ממדים +8. **שמירה ב-RAG** → טבלת `document_chunks` (עם practice_area + appeal_subtype) + +#### נתיב ב: MCP Tool (מ-Claude Code) + +``` +tool: document_upload_training +params: + file_path: "/path/to/file.docx" + decision_number: "ARAR-24-8001" + decision_date: "2024-06-15" + subject_categories: ["היטל השבחה"] + title: "שם ההחלטה" + practice_area: "appeals_committee" + appeal_subtype: "betterment_levy" +``` + +#### נתיב ג: Skill Command (אינטראקטיבי) + +``` +/upload-training +``` +עונים על שאלות: נתיב קובץ, מספר החלטה, תאריך, קטגוריות. + +--- + +### שלב 3: ביקורת (Proofreading QA) + +**קריטי**: לפני שממשיכים לניתוח — **לבדוק כל החלטה שהועלתה**. + +**מה לבדוק**: +- [ ] הטקסט המנוקה (`data/training/proofread/`) קריא ושלם +- [ ] לא נחתכו חלקים מהותיים +- [ ] ה-metadata נכון (מספר תיק, תאריך, קטגוריה) +- [ ] אין שאריות של artifacts מנבו +- [ ] appeal_subtype = `betterment_levy` (ולא `building_permit`) + +**כלי בדיקה**: +``` +GET /api/training/status — סטטוס העלאה ועיבוד +``` + +--- + +### שלב 4: ניתוח סגנון (Style Analysis) + +אחרי שכל ההחלטות הועלו ונבדקו, מריצים ניתוח סגנון: + +``` +POST /api/training/analyze-style +``` + +**מה קורה**: +1. שליפת כל ההחלטות מ-`style_corpus` (לפי practice_area/subtype) +2. בדיקת תקציב tokens: + - עד 900K tokens → pass יחיד (הכל ל-Claude בבת אחת) + - מעל 900K → multi-pass (כל החלטה בנפרד + סינתזה) +3. **חילוץ דפוסים** באמצעות Claude: + - נוסחאות פתיחה + - ביטויי מעבר + - סגנון ציטוט פסיקה + - מבנה ניתוח + - נוסחאות סיום + - ביטויים אופייניים + - זרימת טיעון + - טיפול בראיות +4. שמירה בטבלת `style_patterns` עם תדירות, הקשר, ודוגמאות + +**תוצר**: מדריך סגנון מבוסס-נתונים ספציפי להיטלי השבחה. + +--- + +### שלב 5: ניתוח קורפוס (Corpus Analysis) + +בדומה ל-`docs/corpus-analysis.md` שנבנה עבור רישוי ובנייה, צריך ליצור ניתוח מקביל: + +**מה לנתח**: +- הרכב הקורפוס: כמה החלטות, תוצאות (קבלה/דחייה/חלקית) +- אורך פרק דיון טיפוסי +- נושאים ייחודיים להיטלי השבחה: + - שומות (שומה מוסכמת, שומה אחרת, שמאי מכריע) + - תכנית משביחה — זיהוי, פרשנות + - מועד השבחה / "מועד אישור התכנית" + - חישוב עליית ערך (לפני/אחרי) + - פטורים (ס' 19 לתוספת השלישית) + - שיעור היטל + - דיני ראיות שמאיים +- ביטויי מעבר ייחודיים +- סגנון דיון — "קר ומקצועי" (לפי CLAUDE.md) +- השוואה לרישוי ובנייה (מה שונה) + +**תוצר**: מסמך `docs/corpus-analysis-betterment.md` + +--- + +### שלב 6: עדכון Content Checklists + +הקובץ `lessons.py` מכיל צ'קליסטים לבלוק י (דיון) לפי סוג ערר. + +**מה צריך**: +- ליצור `CONTENT_CHECKLISTS["betterment_levy"]` עם נושאים ייחודיים +- נושאים צפויים: שומות, תכנית משביחה, מועד, חישוב, פטורים, ראיות שמאיות +- הצ'קליסט ייבנה מתוך ניתוח הקורפוס (שלב 5) + +--- + +### שלב 7: אימות Paperclip + +לוודא שחברת CMPA מוגדרת נכון: + +**בדיקות**: +- [ ] חברה CMPA קיימת ופעילה ב-Paperclip DB +- [ ] Issue prefix = CMPA +- [ ] Plugin `legal-ai` פעיל בחברה +- [ ] סוכנים (CEO, researcher, writer) מוגדרים +- [ ] tag_company_mappings נכון ב-legal-ai DB: + - `betterment_levy` → `8639e837...` + - `compensation_197` → `8639e837...` +- [ ] יצירת תיק 8xxx מנותבת נכון + +**כלי בדיקה**: +``` +GET /api/settings/tag-mappings +GET /api/paperclip/companies +``` + +--- + +## סיכום — סדר פעולות + +| # | שלב | מה | כלי | זמן משוער | +|---|------|----|------|-----------| +| 1 | הכנה | איסוף קבצי DOCX, בדיקת פורמט | ידני | — | +| 2 | העלאה | העלאת כל ההחלטות + proofreading אוטומטי | Web API / MCP | דקות לכל החלטה | +| 3 | ביקורת | בדיקת כל טקסט מנוקה + metadata | ידני / Claude | כמה שעות | +| 4 | ניתוח סגנון | חילוץ דפוסים מהקורפוס | API analyze-style | ~30 דק | +| 5 | ניתוח קורפוס | מפת תוכן + נושאים + השוואה | Claude + מסמך | כמה שעות | +| 6 | צ'קליסט | יצירת content checklist להיטלי השבחה | עדכון קוד | — | +| 7 | אימות Paperclip | בדיקת הגדרות חברה + ניתוב | API / DB | — | + +--- + +## הערות חשובות + +### ההבדל בין רישוי ובנייה להיטלי השבחה (מ-CLAUDE.md) + +| מאפיין | רישוי ובנייה (1xxx) | היטלי השבחה (8xxx) | +|---------|---------------------|-------------------| +| טון | חם יחסית | קר ומקצועי | +| תוכן | הקשר תכנוני רחב, אלמנטים אנושיים | יבש, ללא רגשות | +| נושאי דיון | תכניות, חניה, קווי בניין, שכנים | שומות, חישובי השבחה, פטורים | +| פסיקה | ס' 152, הלכת שפר, דיני הקלה | ס' 196-198, תוספת שלישית, שמאי מכריע | + +### סינון RAG לפי סוג +כל ה-chunks נשמרים עם `appeal_subtype`, כך שחיפוש סמנטי בתיק היטל השבחה ימצא רק תקדימים רלוונטיים מהתחום — לא יערבב עם רישוי ובנייה. + +### ניתוח סגנון נפרד +ייתכן שנצטרך **מדריך סגנון נפרד** להיטלי השבחה, כי הטון שונה מהותית. הניתוח בשלב 4 יחשוף את ההבדלים. + +--- + +## סוכנים — שיתוף בין החברות + +### עיקרון: אותם סוכנים, הקשר שונה + +**אין צורך בסוכנים נפרדים** לכל חברה. הסוכנים (CEO, researcher, writer) עובדים לפי **מתודולוגיה** — ארכיטקטורת 12 בלוקים, CREAC, מבחן השופט — שחלה על כל סוגי העררים. + +**מה שמשתנה אוטומטית לפי `appeal_subtype`**: + +| רכיב | מקור | מנגנון הפרדה | +|-------|------|--------------| +| Style patterns | טבלת `style_patterns` | ניתוח סגנון נפרד per-subtype | +| Content checklists | `lessons.py` | key שונה: `building_permit` vs `betterment_levy` | +| תקדימים (RAG) | טבלת `document_chunks` | סינון לפי `appeal_subtype` בחיפוש | +| טון | style guide + patterns | דפוסים שונים מהקורפוס | + +**למה שיתוף סוכנים עדיף**: +1. שיפור במתודולוגיה חל אוטומטית על שני התחומים +2. אין כפילות בתחזוקת סוכנים +3. ההפרדה היא **ברמת הנתונים**, לא ברמת הלוגיקה + +**מה כן צריך לוודא**: +- [ ] הסוכנים ב-Paperclip מקושרים לשתי החברות (CMP + CMPA) +- [ ] כש-issue נפתח ב-CMPA, הסוכנים מופעלים באותו אופן +- [ ] ה-context שהסוכן מקבל כולל את ה-`appeal_subtype` הנכון diff --git a/web-ui/src/app/cases/[caseNumber]/page.tsx b/web-ui/src/app/cases/[caseNumber]/page.tsx index 7573f81..88aceaf 100644 --- a/web-ui/src/app/cases/[caseNumber]/page.tsx +++ b/web-ui/src/app/cases/[caseNumber]/page.tsx @@ -14,6 +14,8 @@ import { StatusGuide } from "@/components/cases/status-guide"; import { StatusChanger } from "@/components/cases/status-changer"; import { DocumentsPanel } from "@/components/cases/documents-panel"; import { DraftsPanel } from "@/components/cases/drafts-panel"; +import { AgentActivityFeed } from "@/components/cases/agent-activity-feed"; +import { AgentStatusWidget } from "@/components/cases/agent-status-widget"; import { UploadSheet } from "@/components/documents/upload-sheet"; import { expectedOutcomes } from "@/lib/schemas/case"; import { useCase, useStartWorkflow } from "@/lib/api/cases"; @@ -86,6 +88,9 @@ export default function CaseDetailPage({ טיוטות והערות + + סוכנים + @@ -153,12 +158,17 @@ export default function CaseDetailPage({ status={data?.status} /> + + + + - + +

שלב בתהליך

diff --git a/web-ui/src/components/cases/agent-activity-feed.tsx b/web-ui/src/components/cases/agent-activity-feed.tsx new file mode 100644 index 0000000..540913e --- /dev/null +++ b/web-ui/src/components/cases/agent-activity-feed.tsx @@ -0,0 +1,267 @@ +"use client"; + +import { useRef, useState, useEffect } from "react"; +import { Button } from "@/components/ui/button"; +import { Textarea } from "@/components/ui/textarea"; +import { Badge } from "@/components/ui/badge"; +import { Markdown } from "@/components/ui/markdown"; +import { useAgentActivity, useSendComment } from "@/lib/api/agents"; +import type { PaperclipComment } from "@/lib/api/agents"; +import { toast } from "sonner"; +import { + Bot, + User, + Send, + Loader2, + MessageSquare, + Clock, +} from "lucide-react"; + +/* ── Role → color mapping ────────────────────────────────────── */ + +const ROLE_COLORS: Record = { + ceo: "bg-blue-100 text-blue-800 border-blue-200", + researcher: "bg-purple-100 text-purple-800 border-purple-200", + engineer: "bg-emerald-100 text-emerald-800 border-emerald-200", + qa: "bg-amber-100 text-amber-800 border-amber-200", +}; + +const ROLE_DOT: Record = { + ceo: "bg-blue-500", + researcher: "bg-purple-500", + engineer: "bg-emerald-500", + qa: "bg-amber-500", +}; + +function roleColor(role: string | null) { + return ROLE_COLORS[role ?? ""] ?? "bg-gray-100 text-gray-700 border-gray-200"; +} + +function roleDot(role: string | null) { + return ROLE_DOT[role ?? ""] ?? "bg-gray-400"; +} + +/* ── Time formatting ─────────────────────────────────────────── */ + +function timeAgo(iso: string | null): string { + if (!iso) return ""; + const diff = Date.now() - new Date(iso).getTime(); + const mins = Math.floor(diff / 60_000); + if (mins < 1) return "עכשיו"; + if (mins < 60) return `לפני ${mins} דק׳`; + const hours = Math.floor(mins / 60); + if (hours < 24) return `לפני ${hours} שע׳`; + const days = Math.floor(hours / 24); + return `לפני ${days} ימים`; +} + +/* ── Issue identifier → find matching identifier ─────────────── */ + +function issueIdentifier( + comment: PaperclipComment, + issueMap: Map, +): string { + return issueMap.get(comment.issue_id) ?? ""; +} + +/* ── Comment card ────────────────────────────────────────────── */ + +function CommentCard({ + comment, + issueMap, +}: { + comment: PaperclipComment; + issueMap: Map; +}) { + const isAgent = !!comment.author_agent_id; + const label = isAgent ? comment.agent_name ?? "סוכן" : "חיים"; + const identifier = issueIdentifier(comment, issueMap); + + return ( +
+ {/* Avatar */} +
+ {isAgent ? ( +
+ +
+ ) : ( +
+ +
+ )} +
+ + {/* Content */} +
+ {/* Header */} +
+ {label} + {isAgent && comment.agent_role && ( + + + {comment.agent_role} + + )} + {identifier && ( + + {identifier} + + )} + + + {timeAgo(comment.created_at)} + +
+ + {/* Body */} +
+ +
+
+
+ ); +} + +/* ── Main Feed ───────────────────────────────────────────────── */ + +export function AgentActivityFeed({ + caseNumber, +}: { + caseNumber: string; +}) { + const { data, isLoading, error } = useAgentActivity(caseNumber); + const sendComment = useSendComment(caseNumber); + const [body, setBody] = useState(""); + const endRef = useRef(null); + + // Build issue_id → identifier map + const issueMap = new Map(); + if (data?.issues) { + for (const iss of data.issues) { + issueMap.set(iss.id, iss.identifier); + } + } + + // Auto-scroll on new comments + const commentCount = data?.comments?.length ?? 0; + useEffect(() => { + endRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [commentCount]); + + const handleSend = () => { + if (!body.trim()) return; + sendComment.mutate( + { body: body.trim() }, + { + onSuccess: (res) => { + setBody(""); + toast.success(`נשלח ל-${res.issue_identifier}`); + }, + onError: () => toast.error("שגיאה בשליחת ההודעה"), + }, + ); + }; + + // ── Empty / loading states ── + + if (isLoading) { + return ( +
+ + טוען פעילות סוכנים... +
+ ); + } + + if (error) { + return ( +
+ שגיאה בטעינת פעילות סוכנים +
+ ); + } + + if (!data?.issues?.length) { + return ( +
+ +

+ התהליך טרם הופעל. לחץ "התחל תהליך" בלשונית סקירה. +

+
+ ); + } + + const comments = data.comments ?? []; + + return ( +
+ {/* Issue summary bar */} +
+ {data.issues.map((iss) => ( + + {iss.identifier} — {iss.status} + + ))} +
+ + {/* Comments stream */} +
+ {comments.length === 0 ? ( +
+ + הסוכנים התחילו לעבוד, ממתין לדיווח ראשון... +
+ ) : ( + comments.map((c) => ( + + )) + )} +
+
+ + {/* Comment input */} +
+