@@ -49,6 +49,17 @@ function cleanCitation(s: string | null | undefined): string {
return s . replace ( /[ - - ]/g , "" ) . trim ( ) ;
}
// Source separation (פסקי-דין מול החלטות ועדת-ערר) for convenient tagging.
function sourceLabel ( s : string | null ) : string {
return s === "court_ruling" ? "פסק-דין"
: s === "appeals_committee" ? "ועדת ערר" : "אחר" ;
}
const SOURCE_FILTERS : { value : "all" | "court_ruling" | "appeals_committee" ; label : string } [ ] = [
{ value : "all" , label : "הכל" } ,
{ value : "court_ruling" , label : "פסקי דין" } ,
{ value : "appeals_committee" , label : "ועדת ערר" } ,
] ;
function isTagged ( it : GoldsetItem ) : boolean {
// Fully tagged only when ALL THREE answers are set — otherwise, in
// "hide tagged" mode, a card would vanish the moment is_holding is clicked,
@@ -201,6 +212,12 @@ function TagCard({
>
< div className = "flex items-center gap-2 text-[0.72rem] text-ink-muted flex-wrap" >
< span className = "font-semibold text-navy" > { cleanCitation ( it . case_number ) } < / span >
< Badge variant = "outline"
className = { ` text-[0.65rem] ${ it . source_type === "court_ruling"
? "bg-navy-soft/30 text-navy border-navy/30"
: "bg-gold-wash text-gold-deep border-gold/40" } ` } >
{ sourceLabel ( it . source_type ) }
< / Badge >
< Badge variant = "outline" className = "text-[0.65rem]" > מ כ ו נ ה : { it . rule_type } < / Badge >
{ it . confidence != null && (
< Badge variant = "outline" className = "text-[0.65rem] tabular-nums" > ב י ט ח ו ן { it . confidence . toFixed ( 2 ) } < / Badge >
@@ -282,13 +299,24 @@ export function GoldsetPanel() {
const createSample = useCreateGoldsetSample ( batch ) ;
const [ focusedId , setFocusedId ] = useState < string | null > ( null ) ;
const [ hideTagged , setHideTagged ] = useState ( false ) ;
const [ sourceFilter , setSourceFilter ] =
useState < "all" | "court_ruling" | "appeals_committee" > ( "all" ) ;
const items = useMemo ( ( ) = > data ? . items ? ? [ ] , [ data ] ) ;
const taggedCount = items . filter ( isTagged ) . length ;
const visible = useMemo (
( ) = > ( hideTagged ? items . filter( ( i ) = > ! isTagged ( i ) ) : items ) ,
[ items , hideTagged ] ,
) ;
const sourceCounts = useMemo ( ( ) = > ( {
court_ruling : items. filter( ( i ) = > i . source_type === "court_ruling" ) . length ,
appeals_committee : items.filter ( ( i ) = > i . source_type === "appeals_committee" ) . length ,
} ) , [ items ] );
const visible = useMemo ( ( ) = > {
let v = items ;
if ( sourceFilter !== "all" ) v = v . filter ( ( i ) = > i . source_type === sourceFilter ) ;
if ( hideTagged ) v = v . filter ( ( i ) = > ! isTagged ( i ) ) ;
// group-sort: כל פסקי-הדין יחד, ואז כל החלטות ועדת-הערר (הפרדה ברורה).
const order = ( s : string | null ) = >
s === "court_ruling" ? 0 : s === "appeals_committee" ? 1 : 2 ;
return [ . . . v ] . sort ( ( a , b ) = > order ( a . source_type ) - order ( b . source_type ) ) ;
} , [ items , hideTagged , sourceFilter ] ) ;
const focused = focusedId ? visible . find ( ( i ) = > i . id === focusedId ) ? ? null : null ;
@@ -363,6 +391,20 @@ export function GoldsetPanel() {
< div className = "space-y-4" >
< ScorePanel batch = { batch } / >
{ /* source separation — פסקי-דין מול החלטות ועדת-ערר */ }
< div className = "flex items-center gap-1 rounded-lg border border-rule p-0.5 bg-rule-soft/30 w-fit" >
{ SOURCE_FILTERS . map ( ( s ) = > (
< Button key = { s . value } size = "sm"
variant = { sourceFilter === s . value ? "default" : "ghost" }
className = { sourceFilter === s . value ? "bg-gold text-navy hover:bg-gold-deep" : "" }
onClick = { ( ) = > setSourceFilter ( s . value ) } >
{ s . label }
{ s . value === "court_ruling" && ` ( ${ sourceCounts . court_ruling } ) ` }
{ s . value === "appeals_committee" && ` ( ${ sourceCounts . appeals_committee } ) ` }
< / Button >
) ) }
< / div >
< div className = "flex items-center gap-3 flex-wrap text-sm" >
< span className = "text-navy font-semibold tabular-nums" > { taggedCount } / { items . length } ת ו י ג ו < / span >
< div className = "h-2 w-40 rounded-full bg-rule-soft overflow-hidden" >