import { useCallback, useEffect, useMemo, useState } from 'react'; import CredentialsSection from './CredentialsSection'; import AccountLogin from './AccountLogin'; import ProjectsSection from './ProjectsSection'; import { AccountsSection, PasswordSection } from './AccountControls'; import { AccountRequestError as AdminRequestError, accountRequest as request } from './account-api'; import type { components } from '../generated/api'; const adminTabs = [ { id: 'projects', label: 'Projects' }, { id: 'ingest-review', label: 'Ingest review' }, { id: 'users', label: 'Users' }, { id: 'credentials', label: 'Credentials' }, { id: 'logs', label: 'Logs' }, { id: 'settings', label: 'Admin Settings' }, ] as const; type AdminTab = typeof adminTabs[number]['id']; type Credential = components['schemas']['CredentialSummary']; type Activity = components['schemas']['AdminEvent']; type Project = components['schemas']['ProjectRecord']; type IngestReview = { review_id: string; project_id: string; run_id: string; source_revision_id: string; extraction_id: string; locator: { kind: string; block_id: string; start: number; end: number; page?: number | null }; excerpt: string; lens: string; summary: string; relevance_rationale: string; jev_relation_probability?: number | null; jev_lens?: string | null; jev_lens_confidence?: number | null; jev_model?: string | null; jev_facet_scores?: { facet: 'tags' | 'audience' | 'use_for' | 'domain'; value: string; probability: number }[]; jev_facet_resolutions?: { facet: 'tags' | 'audience' | 'use_for' | 'domain'; value: string; fit_probability: number; selected_label?: string | null; selected_new_label: boolean; new_probability: number; choice_confidence: number; rank: number; added_to_project: boolean; option_probabilities: { label_id?: string | null; label: string; probability: number }[]; }[]; relevance: 'borderline' | 'low'; status: string; created_at: string; }; function IngestReviewSection({ csrf, projects, onError, onMessage }: { csrf: string; projects: Project[]; onError: (message: string) => void; onMessage: (message: string) => void; }) { const [projectId, setProjectId] = useState(''); const [items, setItems] = useState([]); const [guidance, setGuidance] = useState>({}); const [busyId, setBusyId] = useState(''); const [rationales, setRationales] = useState>({}); const [saveGuidance, setSaveGuidance] = useState>({}); const refresh = useCallback(async () => { if (!projectId) { setItems([]); return; } try { const rows = await request(`/v1/ingest-reviews?project_id=${encodeURIComponent(projectId)}&status=needs-user-review&limit=100`, csrf); setItems(rows); } catch (failure) { onError(failure instanceof Error ? failure.message : 'Could not load review queue.'); } }, [projectId, csrf, onError]); useEffect(() => { void refresh(); }, [refresh]); async function decide(item: IngestReview, decision: 'include' | 'exclude' | 'defer') { const rationale = rationales[item.review_id]?.trim(); if (!rationale) { onError('Add a short reason for this decision.'); return; } const wantsGuidance = saveGuidance[item.review_id] ?? false; const guidanceRule = guidance[item.review_id]?.trim(); if (wantsGuidance && !guidanceRule) { onError('Write the reusable guidance rule to save it.'); return; } setBusyId(item.review_id); onError(''); try { await request(`/v1/ingest-reviews/${encodeURIComponent(item.review_id)}/decision`, csrf, { method: 'POST', body: JSON.stringify({ decision_id: crypto.randomUUID(), review_id: item.review_id, decision, rationale, save_as_guidance: wantsGuidance, ...(wantsGuidance ? { guidance_rule: guidanceRule } : {}) }), }); onMessage(decision === 'include' ? 'Relevance confirmed. Feedback saved; knowledge changes still use the normal proposal flow.' : decision === 'exclude' ? 'Marked not relevant. Feedback saved.' : 'Feedback deferred.'); await refresh(); } catch (failure) { onError(failure instanceof Error ? failure.message : 'Could not save review decision.'); } finally { setBusyId(''); } } return

Ingest review

These flags are recorded alongside normal ingestion and do not hold up proposals or publication. Your decision helps tune future project triage.

If configured, Jev sends the project context and excerpt to OpenRouter for relevance, lens and candidate facet suggestions. These signals are advisory.

{!projectId ?

Select a project to view its review flags.

: items.length === 0 ?

No review flags need feedback.

:
    {items.map(item =>
  1. {item.lens.replaceAll('-', ' ')} · {item.relevance} relevance · {item.summary}

    {item.excerpt}

    {item.relevance_rationale}

    {item.jev_relation_probability != null ?

    Jev relevance: {Math.round(item.jev_relation_probability * 100)}% · lens: {item.jev_lens ?? 'unknown'} ({item.jev_lens_confidence != null ? `${Math.round(item.jev_lens_confidence * 100)}% confidence` : 'no confidence score'}) · {item.jev_model}

    :

    Jev signal unavailable or not configured.

    } {!!item.jev_facet_scores?.length &&

    Facet suggestions: {item.jev_facet_scores.map(score => `${score.facet.replace('_', ' ')} “${score.value}” (${Math.round(score.probability * 100)}%)`).join(' · ')}

    } {!!item.jev_facet_resolutions?.length &&

    Label decisions: {item.jev_facet_resolutions.map(label => `${label.facet.replace('_', ' ')} “${label.value}” → ${label.added_to_project ? 'added as a new project label' : label.selected_label ? `reused “${label.selected_label}”` : label.selected_new_label ? 'new label suggested, not added' : 'no label selected'} (fit ${Math.round(label.fit_probability * 100)}%, new ${Math.round(label.new_probability * 100)}%, confidence ${Math.round(label.choice_confidence * 100)}%, top-N rank ${label.rank}; options: ${label.option_probabilities.map(option => `${option.label} ${Math.round(option.probability * 100)}%`).join(', ')})`).join(' · ')}

    }

    Source {item.source_revision_id} · {item.locator.kind.replaceAll('_', ' ')} {item.locator.page ? `· page ${item.locator.page}` : ''} · run {item.run_id}