File size: 6,436 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 | "use client";
import { useState } from "react";
import { ClipboardCheck, LoaderCircle, Send } from "lucide-react";
import { generateBrainAnswerCorrection } from "@/lib/api/brain";
import type { BrainAnswerCorrectionOutput, BrainGenerationResponse } from "@/lib/api/types";
import type { ApiDocument } from "@/lib/api/types";
type AnswerCorrectionFormProps = {
selectedDocuments: ApiDocument[];
onSubmit: (result: BrainGenerationResponse<BrainAnswerCorrectionOutput>, question: string, studentAnswer: string) => void;
onCancel: () => void;
};
export function AnswerCorrectionForm({
selectedDocuments,
onSubmit,
onCancel,
}: AnswerCorrectionFormProps) {
const [question, setQuestion] = useState("");
const [studentAnswer, setStudentAnswer] = useState("");
const [marks, setMarks] = useState<string>("not_sure");
const [subject, setSubject] = useState(selectedDocuments[0]?.subject ?? "");
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
const canSubmit = question.trim().length > 0 && studentAnswer.trim().length > 0 && !isSubmitting;
async function handleSubmit() {
if (!canSubmit) return;
setIsSubmitting(true);
setError(null);
try {
const sourceIds = selectedDocuments.filter((d) => d.status === "ready").map((d) => d.id);
const result = await generateBrainAnswerCorrection({
topic: question,
subject: subject || undefined,
source_id: sourceIds[0] || undefined,
source_ids: sourceIds.length > 0 ? sourceIds : undefined,
language_preference: "English",
options: {
mode: "answer_correction",
question,
student_answer: studentAnswer,
marks,
subject: subject || undefined,
},
});
onSubmit(result, question, studentAnswer);
} catch (err) {
setError(err instanceof Error ? err.message : "Correction failed. Please try again.");
setIsSubmitting(false);
}
}
return (
<div className="animate-msg-assistant w-full rounded-xl border border-white/[0.06] bg-[#080809] p-5">
<div className="mb-4 flex items-center gap-2.5">
<ClipboardCheck className="size-4 shrink-0 text-emerald-400/70" />
<div>
<span className="text-[10px] font-medium uppercase tracking-[0.08em] text-white/22">Answer Correction</span>
<p className="text-[14px] font-semibold text-white/90">Find marks lost and rewrite in board format</p>
</div>
</div>
{selectedDocuments.length > 0 && (
<p className="mb-3 text-[11px] text-white/30">
Using: {selectedDocuments.map((d) => d.title).join(", ")}
</p>
)}
<div className="grid gap-3">
<label className="grid gap-1.5">
<span className="text-[10px] font-bold uppercase tracking-[0.12em] text-white/30">Question</span>
<textarea
value={question}
onChange={(e) => setQuestion(e.target.value)}
rows={2}
placeholder="Paste the exam question"
className="min-h-16 resize-none rounded-xl border border-white/[0.07] bg-black/30 px-3 py-2.5 text-[13px] leading-5 text-white/80 outline-none placeholder:text-white/25 focus:border-emerald-400/30"
/>
</label>
<label className="grid gap-1.5">
<span className="text-[10px] font-bold uppercase tracking-[0.12em] text-white/30">Your answer</span>
<textarea
value={studentAnswer}
onChange={(e) => setStudentAnswer(e.target.value)}
rows={4}
placeholder="Paste what you wrote"
className="min-h-24 resize-none rounded-xl border border-white/[0.07] bg-black/30 px-3 py-2.5 text-[13px] leading-5 text-white/80 outline-none placeholder:text-white/25 focus:border-emerald-400/30"
/>
</label>
<div className="grid gap-2 sm:grid-cols-[minmax(0,1fr)_120px]">
<label className="grid gap-1.5">
<span className="text-[10px] font-bold uppercase tracking-[0.12em] text-white/30">Subject or chapter</span>
<input
value={subject}
onChange={(e) => setSubject(e.target.value)}
placeholder="Optional"
className="h-10 rounded-xl border border-white/[0.07] bg-black/30 px-3 text-[13px] text-white/80 outline-none placeholder:text-white/25 focus:border-emerald-400/30"
/>
</label>
<label className="grid gap-1.5">
<span className="text-[10px] font-bold uppercase tracking-[0.12em] text-white/30">Marks</span>
<select
value={marks}
onChange={(e) => setMarks(e.target.value)}
className="h-10 rounded-xl border border-white/[0.07] bg-black/30 px-3 text-[13px] text-white/80 outline-none focus:border-emerald-400/30"
>
<option value="not_sure">Not sure</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
</select>
</label>
</div>
</div>
{error && (
<p className="mt-2 text-[12px] text-red-400/80">{error}</p>
)}
<div className="mt-4 flex items-center gap-2">
<button
type="button"
onClick={handleSubmit}
disabled={!canSubmit}
className="inline-flex h-9 items-center justify-center gap-2 rounded-xl bg-emerald-600/90 px-4 text-[12px] font-bold text-white shadow-[0_0_16px_rgba(16,185,129,0.15)] transition hover:bg-emerald-500 active:scale-95 disabled:cursor-not-allowed disabled:opacity-40"
>
{isSubmitting ? (
<LoaderCircle className="size-3.5 animate-spin" />
) : (
<Send className="size-3.5" />
)}
{isSubmitting ? "Correcting..." : "Correct answer"}
</button>
<button
type="button"
onClick={onCancel}
className="inline-flex h-9 items-center justify-center rounded-xl border border-white/[0.08] px-4 text-[12px] font-medium text-white/50 transition hover:border-white/20 hover:text-white/80"
>
Cancel
</button>
</div>
</div>
);
}
|