"use client"; import { useMemo } from "react"; import { FileText, Upload, Edit2 } from "lucide-react"; import type { ApiDocument } from "@/lib/api/types"; import { cn } from "@/lib/utils"; import { documentKindLabel, documentStatusLabel } from "./utils"; import { WorkspaceSearch } from "./WorkspacePanelPrimitives"; function formatMaterialType(materialType: string | null | undefined): string { if (!materialType || materialType === "unknown") return "Unclassified"; return materialType.replace(/_/g, " ").replace(/\b\w/g, (char) => char.toUpperCase()); } function getMaterialTypeBadgeColor(materialType: string | null | undefined): string { if (!materialType || materialType === "unknown") { return "border-white/12 bg-white/[0.04] text-white/46"; } const colors: Record = { notes: "border-white/14 bg-white/[0.06] text-white/72", chapter: "border-blue-300/16 bg-blue-400/[0.08] text-blue-200/80", textbook: "border-emerald-300/16 bg-emerald-400/[0.08] text-emerald-200/80", handwritten_notes: "border-amber-300/16 bg-amber-400/[0.08] text-amber-200/80", question_paper: "border-rose-300/16 bg-rose-400/[0.08] text-rose-200/80", pyq_collection: "border-orange-300/16 bg-orange-400/[0.08] text-orange-200/80", syllabus: "border-cyan-300/16 bg-cyan-400/[0.08] text-cyan-200/80", formula_sheet: "border-pink-300/16 bg-pink-400/[0.08] text-pink-200/80", mixed: "border-white/14 bg-white/[0.06] text-white/72", }; return colors[materialType] || colors.notes; } function formatEducationSummary(document: ApiDocument): string { const status = document.education_extraction_status; const warnings = document.education_warnings_json ?? []; if (status === "extraction_failed") return "Extraction failed"; if (status === "needs_review") return warnings[0] ? `Needs review - ${warnings[0]}` : "Needs review"; if (document.material_type === "syllabus") { const count = document.syllabus_items_count ?? 0; return count > 0 ? `Syllabus - ${count} chapter${count === 1 ? "" : "s"} extracted` : "Syllabus - extracting topics"; } if (document.material_type === "question_paper") { const count = document.pyq_questions_count ?? 0; const years = (document.pyq_years_json ?? []).join(", "); if (count > 0) { return `PYQ paper${years ? ` - ${years}` : ""} - ${count} question${count === 1 ? "" : "s"} extracted`; } return "Question paper - no questions extracted yet"; } if (document.status === "ready") return "Generic notes - no PYQ data"; return documentStatusLabel(document.status); } type SourceRailProps = { documents: ApiDocument[]; selectedSourceIds: string[]; isLoading: boolean; search: string; onSearchChange: (value: string) => void; onToggleSource: (id: string) => void; onUpload: () => void; onEditClassification?: (documentId: string, documentTitle: string, currentMaterialType: string | null) => void; }; export function SourceRail({ documents, selectedSourceIds, isLoading, search, onSearchChange, onToggleSource, onUpload, onEditClassification, }: SourceRailProps) { const filtered = useMemo(() => { const query = search.trim().toLowerCase(); if (!query) return documents; return documents.filter((document) => [document.title, document.subject, document.chapter, document.syllabus, document.file_type, document.material_type] .filter(Boolean) .some((value) => String(value).toLowerCase().includes(query)), ); }, [documents, search]); return (

Sources

{documents.length ? `${documents.filter((document) => document.status === "ready").length} ready of ${documents.length}` : "Upload study material"}

{isLoading ? (
{Array.from({ length: 5 }).map((_, index) => (
))}
) : filtered.length === 0 ? (

No sources yet

Upload a selectable-text PDF or TXT file, or paste syllabus text.

) : (
{filtered.map((document) => { const active = selectedSourceIds.includes(document.id); return (
{document.status === "ready" && onEditClassification && ( )}
); })}
)}
); }