'use client' import { useState, useEffect } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import type { ScoringRule } from '@/lib/types' import { fetchScoringRules, createScoringRule, updateScoringRule, deleteScoringRule } from '@/lib/api/scoring-rules' import { fetchConfig, saveConfig } from '@/lib/api/config' import { cn } from '@/lib/utils' export default function ScoringRulesPanel() { const qc = useQueryClient() const { data: rules = [], isLoading } = useQuery({ queryKey: ['scoring-rules'], queryFn: fetchScoringRules, }) const [scoringEnabled, setScoringEnabled] = useState(true) const [toggleLoading, setToggleLoading] = useState(false) useEffect(() => { fetchConfig().then(cfg => { setScoringEnabled((cfg['scoring_enabled'] ?? 'true') !== 'false') }) }, []) const handleScoringToggle = async () => { setToggleLoading(true) const newVal = !scoringEnabled await saveConfig({ scoring_enabled: String(newVal) }) setScoringEnabled(newVal) setToggleLoading(false) } const [newSignal, setNewSignal] = useState('') const [newDelta, setNewDelta] = useState('') const [newNotes, setNewNotes] = useState('') const [error, setError] = useState('') const [editId, setEditId] = useState(null) const [editSignal, setEditSignal] = useState('') const [editDelta, setEditDelta] = useState('') const invalidate = () => qc.invalidateQueries({ queryKey: ['scoring-rules'] }) const createMut = useMutation({ mutationFn: () => createScoringRule(newSignal.trim(), parseInt(newDelta), newNotes.trim() || undefined), onSuccess: () => { setNewSignal(''); setNewDelta(''); setNewNotes(''); setError(''); invalidate() }, onError: (e: Error) => setError(e.message), }) const updateMut = useMutation({ mutationFn: () => updateScoringRule(editId!, { signal: editSignal.trim(), delta: parseInt(editDelta) }), onSuccess: () => { setEditId(null); invalidate() }, }) const deleteMut = useMutation({ mutationFn: (id: number) => deleteScoringRule(id), onSuccess: invalidate, }) const positives = rules.filter(r => r.delta > 0) const negatives = rules.filter(r => r.delta < 0) return (
{/* Header */}

Scoring Rules

Token signals that boost (+) or penalise (−) lot scores

Scoring
{/* AI-first banner */} {!scoringEnabled && (
AI-first mode active
Score signals are disabled. The AI description on each target is the sole judge. Set an AI Description on every target and enable AI Filter in Settings.
)}
{/* Add new */}
{ e.preventDefault(); if (newSignal && newDelta) createMut.mutate() }} >
setNewSignal(e.target.value)} />
setNewDelta(e.target.value)} />
setNewNotes(e.target.value)} />
{error && {error}}
{isLoading &&

Loading…

} {/* Rules tables */} {positives.length > 0 && (
Boosts
{positives.map(r => ( updateMut.mutate()} onDelete={() => deleteMut.mutate(r.id)} /> ))}
SignalDeltaNotes
)} {negatives.length > 0 && (
Penalties
{negatives.map(r => ( updateMut.mutate()} onDelete={() => deleteMut.mutate(r.id)} /> ))}
SignalDeltaNotes
)}
) } function RuleRow({ rule, editId, editSignal, editDelta, setEditId, setEditSignal, setEditDelta, onSave, onDelete }: { rule: ScoringRule; editId: number | null editSignal: string; editDelta: string setEditId: (id: number | null) => void; setEditSignal: (s: string) => void; setEditDelta: (s: string) => void onSave: () => void; onDelete: () => void }) { const isEditing = editId === rule.id if (isEditing) return ( setEditSignal(e.target.value)} /> setEditDelta(e.target.value)} /> {rule.notes || '—'} ) return ( 0 ? 'text-g-green' : 'text-g-red')}> {rule.delta > 0 ? '+' : ''}{rule.delta} {rule.notes || '—'} ) }