File size: 5,663 Bytes
65d68f5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
"use client";

import { FileText, X } from "lucide-react";
import type { ApiDocument } from "@/lib/api/types";
import { cn } from "@/lib/utils";

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<string, string> = {
    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 compactEducationBadge(document: ApiDocument): string | null {
  if (document.education_extraction_status === "extraction_failed") return "Extraction failed";
  if (document.education_extraction_status === "needs_review") return "Needs review";
  if (document.material_type === "syllabus" && (document.syllabus_items_count ?? 0) > 0) {
    return `${document.syllabus_items_count} topics`;
  }
  if (document.material_type === "question_paper" && (document.pyq_questions_count ?? 0) > 0) {
    return `${document.pyq_questions_count} PYQ`;
  }
  return null;
}

export function SourcePill({
  document,
  active,
  onToggle,
  onRemove,
}: {
  document: ApiDocument;
  active: boolean;
  onToggle: () => void;
  onRemove?: () => void;
}) {
  const educationBadge = compactEducationBadge(document);
  return (
    <button
      type="button"
      onClick={onToggle}
      className={cn(
        "group inline-flex min-w-0 items-center gap-2 rounded-full border px-3 py-1.5 text-xs font-semibold transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/22",
        active
          ? "border-white/[0.2] bg-white text-black"
          : "border-white/[0.1] bg-white/[0.035] text-white/58 hover:border-white/[0.18] hover:text-white",
      )}
    >
      <FileText className="size-3.5 shrink-0" />
      <span className="max-w-[9rem] truncate sm:max-w-[12rem]">{document.title}</span>
      <span className={cn("size-1.5 rounded-full", document.status === "ready" ? "bg-current" : "bg-current opacity-45")} />
      {document.material_type && document.material_type !== "unknown" && (
        <span className={cn(
          "rounded-full border px-1.5 py-0.5 text-[9px] font-medium",
          active ? getMaterialTypeBadgeColor(document.material_type).replace("text-", "text-black/") : getMaterialTypeBadgeColor(document.material_type)
        )}>
          {formatMaterialType(document.material_type)}
        </span>
      )}
      {educationBadge ? (
        <span className={cn(
          "rounded-full border px-1.5 py-0.5 text-[9px] font-medium",
          active ? "border-black/10 bg-black/[0.04] text-black/65" : "border-white/[0.08] bg-white/[0.025] text-white/40",
        )}>
          {educationBadge}
        </span>
      ) : null}
      {onRemove ? (
        <span
          role="button"
          tabIndex={-1}
          aria-label={`Remove ${document.title}`}
          onClick={(event) => {
            event.stopPropagation();
            onRemove();
          }}
          className="ml-0.5 rounded-full opacity-55 transition group-hover:opacity-100"
        >
          <X className="size-3.5" />
        </span>
      ) : null}
    </button>
  );
}

export function SelectedSourcesStrip({
  selectedDocuments,
  documents,
  onToggleSource,
  onClear,
}: {
  selectedDocuments: ApiDocument[];
  documents: ApiDocument[];
  onToggleSource: (id: string) => void;
  onClear: () => void;
}) {
  const previewDocuments = selectedDocuments.length > 0 ? selectedDocuments : documents.slice(0, 4);
  if (previewDocuments.length === 0) return null;

  return (
    <div className="flex min-w-0 max-w-full flex-col items-start gap-2 sm:flex-row sm:items-center">
      <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-white/34">
        {selectedDocuments.length > 0 ? "Selected sources" : "Recent sources"}
      </span>
      <div className="flex w-full min-w-0 max-w-full gap-2 overflow-x-auto pb-1 [scrollbar-width:none] sm:flex-1 [&::-webkit-scrollbar]:hidden">
        {previewDocuments.map((document) => (
          <SourcePill
            key={document.id}
            document={document}
            active={selectedDocuments.some((item) => item.id === document.id)}
            onToggle={() => onToggleSource(document.id)}
            onRemove={selectedDocuments.some((item) => item.id === document.id) ? () => onToggleSource(document.id) : undefined}
          />
        ))}
      </div>
      {selectedDocuments.length > 0 ? (
        <button
          type="button"
          onClick={onClear}
          className="rounded-full px-2.5 py-1 text-[11px] font-semibold text-white/38 transition hover:bg-white/[0.04] hover:text-white/70"
        >
          Clear
        </button>
      ) : null}
    </div>
  );
}