/** * Primary "Run the ranker" control panel. * * Three orthogonal ways to feed candidates to the API: * 1. Upload your own `.json/.jsonl/.jsonl.gz` (drag-and-drop). * 2. Click "Feed sample candidates" → uses the default fixture. * 3. Use the `prefilled` prop from the parent (e.g. resume parser). * * Schema-validation errors are NOT caught here - they bubble up to * App.tsx so it can render the dedicated panel. */ import React, { useEffect, useState } from "react"; import { useDropzone } from "react-dropzone"; import { motion, AnimatePresence } from "framer-motion"; import { postRank, SchemaValidationError } from "../api"; import type { RankResponse } from "../types"; interface Props { onResult: (r: RankResponse) => void; onError: (msg: string) => void; onSchemaError: (err: SchemaValidationError) => void; prefilled?: File | null; } export const RunPanel: React.FC = ({ onResult, onError, onSchemaError, prefilled, }) => { const [candidates, setCandidates] = useState(null); const [jd, setJd] = useState(null); const [topK, setTopK] = useState(10); const [busy, setBusy] = useState(false); const [mode, setMode] = useState<"upload" | "sample">("upload"); useEffect(() => { if (prefilled) { setCandidates(prefilled); setMode("upload"); } }, [prefilled]); async function runUpload() { if (!candidates) return; await runRequest({ candidates, jd, topK }); } async function runSample() { setMode("sample"); await runRequest({ useSample: true, jd, topK }); } async function runRequest(opts: { candidates?: File | null; jd?: File | null; topK?: number; useSample?: boolean; }) { setBusy(true); try { const res = await postRank(opts); onResult(res); } catch (e) { if (e instanceof SchemaValidationError) { onSchemaError(e); } else { onError(e instanceof Error ? e.message : String(e)); } } finally { setBusy(false); } } // NOTE: many browsers report `.jsonl` as `application/octet-stream` and // some report `.gz` as `application/x-gzip`. Listing the extensions under // a wildcard MIME key tells react-dropzone to match by extension first, // which is what we want. const candidateDrop = useDropzone({ onDrop: (files) => files[0] && setCandidates(files[0]), multiple: false, accept: { "application/json": [".json"], "application/x-ndjson": [".jsonl"], "application/octet-stream": [".jsonl", ".gz"], "application/gzip": [".gz"], "application/x-gzip": [".gz"], }, // 600 MB - the official candidates.jsonl is ~480 MB. maxSize: 600 * 1024 * 1024, }); // JD upload — accept text, markdown, .docx and .pdf. The official Redrob // bundle ships `job_description.docx`, so this must Just Work. const jdDrop = useDropzone({ onDrop: (files) => files[0] && setJd(files[0]), multiple: false, accept: { "text/plain": [".txt", ".md"], "text/markdown": [".md"], "application/vnd.openxmlformats-officedocument.wordprocessingml.document": [".docx"], "application/pdf": [".pdf"], "application/octet-stream": [".docx", ".pdf", ".txt", ".md"], }, maxSize: 10 * 1024 * 1024, }); return (

Run the ranker

Drop your candidates file, or click "Feed sample candidates" to explore with the default 50-row fixture. Every upload is validated against the official Redrob schema first - mismatches surface as a git diff style report, not a stack trace.

setCandidates(null)} accent="bone" /> setJd(null)} accent="bone" hint={ jd ? `Will rank against your uploaded JD` : `If empty, the default Senior-AI-Engineer JD is used` } />
setTopK(Math.min(100, Math.max(1, Number(e.target.value) || 1)))} className="w-24 bg-ink-800/80 border hairline px-3 py-1.5 text-sm font-mono focus:outline-none focus:border-bone-50 transition-colors" />
{candidates && ( Ready: {candidates.name} ( {(candidates.size / 1024).toFixed(1)} KB) )}
); }; // ───────────────────────── helpers ───────────────────────────────────────── const Spinner: React.FC = () => ( ); const SparkleIcon: React.FC = () => ( ); interface DropFieldProps { label: string; file: File | null; onClear: () => void; dropzone: ReturnType; accent: "bone" | "red"; hint?: string; } const DropField: React.FC = ({ label, file, onClear, dropzone, hint }) => { const { getRootProps, getInputProps, isDragActive, isDragReject } = dropzone; return (
{label}
{file ? file.name : isDragActive ? "drop to attach" : "drag or click to attach"} {file ? ( ) : ( Browse )}
{hint && (
{hint}
)}
); }; export default RunPanel;