File size: 7,149 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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
"use client";

import { useEffect, useState } from "react";
import { FileText, BookOpen, PenTool, ClipboardList, Library, GraduationCap, Sparkles } from "lucide-react";
import { Modal, PrimaryButton, SecondaryButton } from "@/components/dashboard/dashboardPrimitives";
import { reclassifyDocument } from "@/lib/api/documents";
import { normalizeApiError } from "@/lib/api/client";
import { cn } from "@/lib/utils";

const MATERIAL_TYPE_OPTIONS = [
  { value: "notes", label: "Notes", icon: FileText, description: "Typed class notes or study notes" },
  { value: "chapter", label: "Chapter PDF", icon: BookOpen, description: "Textbook chapter, reference material" },
  { value: "textbook", label: "Textbook", icon: Library, description: "Full textbook or reference book" },
  { value: "handwritten_notes", label: "Digitized Notes", icon: PenTool, description: "Handwritten notes already converted to readable text" },
  { value: "question_paper", label: "Question Paper", icon: ClipboardList, description: "Single exam or test paper" },
  { value: "syllabus", label: "Syllabus", icon: GraduationCap, description: "Course syllabus or curriculum" },
] as const;

type MaterialType = typeof MATERIAL_TYPE_OPTIONS[number]["value"];

type SourceClassificationModalProps = {
  isOpen: boolean;
  onClose: () => void;
  documentId: string;
  documentTitle: string;
  currentMaterialType?: string | null;
  onClassificationComplete: (materialType: string) => void;
  autoDetectedType?: string | null;
};

export function SourceClassificationModal({
  isOpen,
  onClose,
  documentId,
  documentTitle,
  currentMaterialType,
  onClassificationComplete,
  autoDetectedType,
}: SourceClassificationModalProps) {
  const [selectedType, setSelectedType] = useState<MaterialType | null>(
    (currentMaterialType || autoDetectedType || null) as MaterialType | null
  );
  const [isSaving, setIsSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (!isOpen) return;
    const timeoutId = window.setTimeout(() => {
      setSelectedType((currentMaterialType || autoDetectedType || null) as MaterialType | null);
      setError(null);
    }, 0);

    return () => window.clearTimeout(timeoutId);
  }, [autoDetectedType, currentMaterialType, isOpen]);

  async function handleSave() {
    if (!selectedType) {
      setError("Please select a material type");
      return;
    }

    setIsSaving(true);
    setError(null);

    try {
      await reclassifyDocument(documentId, selectedType);
      onClassificationComplete(selectedType);
      onClose();
    } catch (err) {
      setError(normalizeApiError(err));
    } finally {
      setIsSaving(false);
    }
  }

  function handleSkip() {
    // If auto-detected type exists, use it; otherwise use current or default to unknown
    const typeToUse = selectedType || autoDetectedType || currentMaterialType || "unknown";
    onClassificationComplete(typeToUse);
    onClose();
  }

  return (
    <Modal isOpen={isOpen} onClose={onClose} title="What type of material is this?">
      <div className="space-y-5">
        <div className="text-center">
          <p className="text-[13px] text-white/40 leading-relaxed">
            Classifying <span className="font-semibold text-white/60">&ldquo;{documentTitle}&rdquo;</span> helps DocDoe provide better study tools.
          </p>
        </div>

        {autoDetectedType && !currentMaterialType && (
          <div className="rounded-xl border border-white/14 bg-white/[0.06] px-4 py-3 flex items-start gap-3">
            <Sparkles className="size-4 text-white/75 shrink-0 mt-0.5" strokeWidth={1.8} />
            <div className="flex-1 min-w-0">
              <p className="text-[12px] font-semibold text-white/90">Auto-detected as: {autoDetectedType.replace(/_/g, " ")}</p>
              <p className="text-[11px] text-white/60 mt-1">Confirm or select a different type below.</p>
            </div>
          </div>
        )}

        <div className="grid grid-cols-2 gap-2 max-h-[240px] overflow-y-auto">
          {MATERIAL_TYPE_OPTIONS.map((option) => {
            const Icon = option.icon;
            const isSelected = selectedType === option.value;
            const isAutoDetected = autoDetectedType === option.value && !currentMaterialType;

            return (
              <button
                key={option.value}
                type="button"
                onClick={() => setSelectedType(option.value)}
                className={cn(
                  "flex flex-col items-start gap-2 rounded-xl border p-3 text-left transition-all",
                  "hover:bg-white/[0.04] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/35",
                  isSelected
                    ? "border-white/35 bg-white/[0.11] shadow-[0_0_16px_rgba(255,255,255,0.08)]"
                    : "border-white/[0.08] bg-white/[0.02]",
                  isAutoDetected && !isSelected
                    ? "border-white/20 bg-white/[0.05]"
                    : "",
                )}
              >
                <div className="flex items-center gap-2">
                  <Icon
                    className={cn(
                      "size-4 shrink-0",
                      isSelected ? "text-white" : "text-white/40",
                      isAutoDetected && !isSelected ? "text-white/75" : ""
                    )}
                    strokeWidth={1.8}
                  />
                  <span className={cn(
                    "text-[11.5px] font-semibold",
                    isSelected ? "text-white" : "text-white/70",
                    isAutoDetected && !isSelected ? "text-white/80" : ""
                  )}>
                    {option.label}
                  </span>
                  {isAutoDetected && (
                    <span className="text-[9px] font-semibold text-white/80 px-1.5 py-0.5 rounded-full bg-white/10">
                      Auto
                    </span>
                  )}
                </div>
                <p className="text-[10px] text-white/35 line-clamp-2 leading-tight">
                  {option.description}
                </p>
              </button>
            );
          })}
        </div>

        {error && (
          <div className="rounded-xl border border-rose-300/16 bg-rose-500/[0.08] px-4 py-3">
            <p className="text-[11px] text-rose-100/80">{error}</p>
          </div>
        )}

        <div className="flex items-center gap-3 pt-2">
          <SecondaryButton
            onClick={handleSkip}
            disabled={isSaving}
            className="flex-1"
          >
            Skip for Now
          </SecondaryButton>
          <PrimaryButton
            onClick={handleSave}
            disabled={isSaving || !selectedType}
            className="flex-1"
          >
            {isSaving ? "Saving..." : selectedType ? "Confirm" : "Select Type"}
          </PrimaryButton>
        </div>

        <p className="text-[10.5px] text-white/30 text-center leading-relaxed">
          You can change this later from the source card
        </p>
      </div>
    </Modal>
  );
}