feat: add 'caselaw' (פסיקה) kind alongside חוק/תקנות/חוזרים

Court rulings get their own kind so users can filter search results
to "פסיקה" specifically and upload case-law PDFs from the ניהול tab.
PHP validators in Controller + Service accept the new kind; the search
dropdown and upload kind dropdown gain a "פסיקה" option; kindHe in
index.js maps caselaw → "פסיקה" so jobs list, search results, and
browse view all label it consistently.

Backend: depends on shira-hermes commit 16eeeff (kind validators +
chunker fallback) and migration 003_caselaw_kind.sql (already applied
on dev — alters CHECK constraints on kb_source.kind and
kb_ingest_job.kind to include 'caselaw').

Refs Task Master #18

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-25 17:01:12 +00:00
parent 6781ac4e37
commit e7b21919c2
7 changed files with 43 additions and 17 deletions
@@ -36,6 +36,7 @@
<option value="law">חוק</option>
<option value="regulation">תקנות</option>
<option value="circular">חוזרים</option>
<option value="caselaw">פסיקה</option>
</select>
<button type="button" class="btn btn-primary" data-action="submit">חפש</button>
<button type="button" class="btn btn-default" data-action="clearResults"
@@ -88,6 +89,7 @@
<option value="law">חוק</option>
<option value="regulation">תקנות</option>
<option value="circular">חוזר</option>
<option value="caselaw">פסיקה</option>
</select>
</div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;">
@@ -247,7 +247,7 @@ define('modules/knowledge-base/views/kb/index', ['view'], function (Dep) {
done: '<span class="label label-success">הושלם</span>',
failed: '<span class="label label-danger">נכשל</span>',
};
const kindHe = {law: 'חוק', regulation: 'תקנות', circular: 'חוזר'};
const kindHe = {law: 'חוק', regulation: 'תקנות', circular: 'חוזר', caselaw: 'פסיקה'};
const rows = items.map(j => {
const when = j.completed_at || j.started_at || j.created_at || '';
const whenShort = String(when).slice(0, 19).replace('T', ' ');
@@ -813,7 +813,7 @@ define('modules/knowledge-base/views/kb/index', ['view'], function (Dep) {
return;
}
const initialIdx = (typeof selectedIdx === 'number' && hits[selectedIdx]) ? selectedIdx : 0;
const kindHe = {law: 'חוק', regulation: 'תקנה', circular: 'חוזר'};
const kindHe = {law: 'חוק', regulation: 'תקנה', circular: 'חוזר', caselaw: 'פסיקה'};
const self = this;
const isPdfBacked = h => h && (h.kind === 'regulation' || h.kind === 'circular');
@@ -1112,7 +1112,7 @@ define('modules/knowledge-base/views/kb/index', ['view'], function (Dep) {
answerHtml = '<div style="white-space:pre-wrap;">' + this.escape(text || '') + '</div>';
}
const kindHe = {law: 'חוק', regulation: 'תקנה', circular: 'חוזר'};
const kindHe = {law: 'חוק', regulation: 'תקנה', circular: 'חוזר', caselaw: 'פסיקה'};
// Dedup sources by source_id (first occurrence = most relevant),
// but remember every page that came up per source so users can
@@ -1252,8 +1252,8 @@ define('modules/knowledge-base/views/kb/index', ['view'], function (Dep) {
$list.html('<div class="text-muted">אין מקורות.</div>');
return;
}
const kindHe = {law: 'חוק', regulation: 'תקנות', circular: 'חוזרים'};
const grouped = {law: [], regulation: [], circular: []};
const kindHe = {law: 'חוק', regulation: 'תקנות', circular: 'חוזרים', caselaw: 'פסיקה'};
const grouped = {law: [], regulation: [], circular: [], caselaw: []};
this._sources.forEach(s => {
if (grouped[s.kind]) grouped[s.kind].push(s);
});