fix: build speed, observer AI details, round tracker empty state
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
- Disable typedRoutes and skip TS in build (run tsc separately) — build drops from ~9min to ~36s - Expand optimizePackageImports for sonner, date-fns, recharts, motion, zod - Docker: mount .next/cache as build cache for faster rebuilds - Observer filtering panel: fix AI reasoning extraction (nested under rule ID) and show confidence, quality score, spam risk, override reason - Round User Tracker: show empty state message instead of disappearing when selected round has no passed projects yet Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,29 @@ import {
|
||||
import { Filter, ChevronDown, ChevronUp, ChevronLeft, ChevronRight } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
type AIScreeningData = {
|
||||
meetsCriteria?: boolean
|
||||
confidence?: number
|
||||
reasoning?: string
|
||||
qualityScore?: number
|
||||
spamRisk?: boolean
|
||||
}
|
||||
|
||||
function parseAIData(json: unknown): AIScreeningData | null {
|
||||
if (!json || typeof json !== 'object') return null
|
||||
const obj = json as Record<string, unknown>
|
||||
// aiScreeningJson is nested under rule ID: { [ruleId]: { outcome, confidence, ... } }
|
||||
if (!('outcome' in obj) && !('reasoning' in obj)) {
|
||||
const keys = Object.keys(obj)
|
||||
if (keys.length > 0) {
|
||||
const inner = obj[keys[0]]
|
||||
if (inner && typeof inner === 'object') return inner as AIScreeningData
|
||||
}
|
||||
return null
|
||||
}
|
||||
return obj as unknown as AIScreeningData
|
||||
}
|
||||
|
||||
export function FilteringPanel({ roundId }: { roundId: string }) {
|
||||
const [outcomeFilter, setOutcomeFilter] = useState<string>('ALL')
|
||||
const [page, setPage] = useState(1)
|
||||
@@ -199,17 +222,39 @@ export function FilteringPanel({ roundId }: { roundId: string }) {
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{expandedId === r.id && (
|
||||
<div className="px-4 pb-3 pt-0">
|
||||
<div className="rounded bg-muted/50 p-3 text-xs leading-relaxed text-muted-foreground">
|
||||
{(() => {
|
||||
const screening = r.aiScreeningJson as Record<string, unknown> | null
|
||||
const reasoning = (screening?.reasoning ?? screening?.explanation ?? r.overrideReason ?? 'No details available') as string
|
||||
return reasoning
|
||||
})()}
|
||||
{expandedId === r.id && (() => {
|
||||
const ai = parseAIData(r.aiScreeningJson)
|
||||
return (
|
||||
<div className="px-4 pb-3 pt-0 space-y-2">
|
||||
{ai?.confidence != null && (
|
||||
<div className="flex items-center gap-3 text-xs">
|
||||
{ai.confidence != null && (
|
||||
<span className="text-muted-foreground">
|
||||
Confidence: <strong>{Math.round(ai.confidence * 100)}%</strong>
|
||||
</span>
|
||||
)}
|
||||
{ai.qualityScore != null && (
|
||||
<span className="text-muted-foreground">
|
||||
Quality: <strong>{ai.qualityScore}/10</strong>
|
||||
</span>
|
||||
)}
|
||||
{ai.spamRisk && (
|
||||
<Badge variant="destructive" className="text-[10px] px-1.5 py-0">Spam Risk</Badge>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="rounded bg-muted/50 border p-3 text-xs leading-relaxed text-muted-foreground whitespace-pre-wrap">
|
||||
{ai?.reasoning || 'No AI reasoning available'}
|
||||
</div>
|
||||
{r.overrideReason && (
|
||||
<div className="rounded bg-amber-50 border border-amber-200 p-3 text-xs">
|
||||
<span className="font-medium text-amber-800">Override: </span>
|
||||
{r.overrideReason}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)
|
||||
})()}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user