Add unified upload UI with Din Leumi support and auto metadata extraction
Upload interface now supports three targets: - Ezer Mishpati case documents - Ezer Mishpati training corpus - Din Leumi NI court decisions (new) Din Leumi uploads auto-extract metadata (court, judge, date, parties, topics, outcome) using Claude API, eliminating manual form filling. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>עוזר משפטי — העלאת מסמכים</title>
|
||||
<title>העלאת מסמכים משפטיים</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
@@ -234,8 +234,8 @@ header p { opacity: 0.7; margin-top: 4px; font-size: 0.9em; }
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>עוזר משפטי — העלאת מסמכים</h1>
|
||||
<p>העלאה, סיווג ועיבוד מסמכים משפטיים</p>
|
||||
<h1>העלאת מסמכים משפטיים</h1>
|
||||
<p>העלאה, סיווג ועיבוד מסמכים — עזר משפטי | דין לאומי</p>
|
||||
</header>
|
||||
|
||||
<!-- Upload Zone -->
|
||||
@@ -363,8 +363,9 @@ async function loadPending() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="radio-group">
|
||||
<label><input type="radio" name="cat_${esc(f.filename)}" value="training" onchange="showFields(this)"> החלטה קודמת (אימון)</label>
|
||||
<label><input type="radio" name="cat_${esc(f.filename)}" value="case" onchange="showFields(this)"> מסמך תיק</label>
|
||||
<label><input type="radio" name="cat_${esc(f.filename)}" value="case" onchange="showFields(this)"> עזר משפטי — מסמך תיק</label>
|
||||
<label><input type="radio" name="cat_${esc(f.filename)}" value="training" onchange="showFields(this)"> עזר משפטי — החלטה (אימון)</label>
|
||||
<label><input type="radio" name="cat_${esc(f.filename)}" value="dinleumi" onchange="showFields(this)"> דין לאומי — פסק דין בל"ל</label>
|
||||
</div>
|
||||
<div class="conditional case-fields" id="case_${esc(f.filename)}">
|
||||
<div class="form-row">
|
||||
@@ -407,6 +408,9 @@ async function loadPending() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="conditional dinleumi-fields" id="dinleumi_${esc(f.filename)}">
|
||||
<p style="font-size:0.85em;color:#636e72;margin:4px 0 8px">המטאדאטא (בית משפט, שופט, תאריך, נושא, תוצאה) תחולץ אוטומטית מתוך פסק הדין</p>
|
||||
</div>
|
||||
<div style="margin-top:12px">
|
||||
<div class="form-group" style="max-width:300px;margin-bottom:8px">
|
||||
<label>כותרת (אופציונלי)</label>
|
||||
@@ -420,11 +424,11 @@ async function loadPending() {
|
||||
|
||||
function showFields(radio) {
|
||||
const container = radio.closest('.pending-file');
|
||||
const filename = container.dataset.filename;
|
||||
const val = radio.value;
|
||||
|
||||
container.querySelector('.case-fields').classList.toggle('active', val === 'case');
|
||||
container.querySelector('.training-fields').classList.toggle('active', val === 'training');
|
||||
container.querySelector('.dinleumi-fields').classList.toggle('active', val === 'dinleumi');
|
||||
container.querySelector('.process-btn').disabled = false;
|
||||
}
|
||||
|
||||
@@ -439,20 +443,30 @@ async function classifyFile(filename) {
|
||||
const category = container.querySelector('input[type="radio"]:checked')?.value;
|
||||
if (!category) return toast('יש לבחור סיווג', 'error');
|
||||
|
||||
const body = {
|
||||
filename,
|
||||
category,
|
||||
title: container.querySelector('.doc-title').value,
|
||||
};
|
||||
let endpoint = API + '/classify';
|
||||
let body;
|
||||
|
||||
if (category === 'case') {
|
||||
body.case_number = container.querySelector('.case-select').value;
|
||||
body.doc_type = container.querySelector('.doctype-select').value;
|
||||
if (!body.case_number) return toast('יש לבחור תיק', 'error');
|
||||
if (category === 'dinleumi') {
|
||||
endpoint = API + '/classify-dinleumi';
|
||||
body = {
|
||||
filename,
|
||||
title: container.querySelector('.doc-title').value,
|
||||
};
|
||||
} else {
|
||||
body.decision_number = container.querySelector('.decision-number').value;
|
||||
body.decision_date = container.querySelector('.decision-date').value;
|
||||
body.subject_categories = Array.from(container.querySelectorAll('.subject-grid input:checked')).map(cb => cb.value);
|
||||
body = {
|
||||
filename,
|
||||
category,
|
||||
title: container.querySelector('.doc-title').value,
|
||||
};
|
||||
if (category === 'case') {
|
||||
body.case_number = container.querySelector('.case-select').value;
|
||||
body.doc_type = container.querySelector('.doctype-select').value;
|
||||
if (!body.case_number) return toast('יש לבחור תיק', 'error');
|
||||
} else {
|
||||
body.decision_number = container.querySelector('.decision-number').value;
|
||||
body.decision_date = container.querySelector('.decision-date').value;
|
||||
body.subject_categories = Array.from(container.querySelectorAll('.subject-grid input:checked')).map(cb => cb.value);
|
||||
}
|
||||
}
|
||||
|
||||
// Disable button
|
||||
@@ -460,7 +474,7 @@ async function classifyFile(filename) {
|
||||
container.querySelector('.process-btn').textContent = 'מעבד...';
|
||||
|
||||
try {
|
||||
const res = await fetch(API + '/classify', {
|
||||
const res = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
@@ -501,6 +515,7 @@ function trackTask(taskId, displayName) {
|
||||
|
||||
const STEP_LABELS = {
|
||||
extracting: 'מחלץ טקסט',
|
||||
extracting_metadata: 'מחלץ מטאדאטא מפסק הדין',
|
||||
corpus: 'מוסיף לקורפוס',
|
||||
chunking: 'מפצל לקטעים',
|
||||
embedding: 'יוצר embeddings',
|
||||
@@ -530,7 +545,19 @@ function trackTask(taskId, displayName) {
|
||||
div.classList.add('done');
|
||||
es.close();
|
||||
const r = data.result || {};
|
||||
if (r.chunks !== undefined) {
|
||||
const m = data.metadata;
|
||||
if (m && data.system === 'din-leumi') {
|
||||
// Show extracted metadata for din-leumi
|
||||
let metaHtml = `הושלם — ${r.chunks || 0} קטעים, ${r.pages || '?'} עמודים`;
|
||||
const parts = [];
|
||||
if (m.court) parts.push(m.court);
|
||||
if (m.judge) parts.push('שופט: ' + m.judge);
|
||||
if (m.decision_date) parts.push(m.decision_date);
|
||||
if (m.outcome) parts.push({accepted:'התקבלה',rejected:'נדחתה',partial:'חלקית',remanded:'הוחזרה'}[m.outcome] || m.outcome);
|
||||
if (parts.length) metaHtml += '<br><small style="color:#636e72">' + esc(parts.join(' | ')) + '</small>';
|
||||
if (m.topics && m.topics.length) metaHtml += '<br><small style="color:#0984e3">' + m.topics.map(t => esc(t)).join(', ') + '</small>';
|
||||
statusEl.innerHTML = metaHtml;
|
||||
} else if (r.chunks !== undefined) {
|
||||
statusEl.textContent = `הושלם — ${r.chunks} קטעים, ${r.pages || '?'} עמודים`;
|
||||
}
|
||||
toast('העיבוד הושלם: ' + esc(displayName), 'success');
|
||||
|
||||
Reference in New Issue
Block a user