File size: 9,931 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
185
"use client";

import { useMemo, useState } from "react";
import Link from "next/link";
import { CheckCircle2, Clock, Flag, Send } from "lucide-react";
import { buildWeekendPyqTest, evaluatePyqAnswers, updatePhase3Activity, type WeekendPyqResult } from "@/lib/phase3-learning";
import { cn } from "@/lib/utils";
import { getDefaultTuitionSupportInput } from "./tuitionSupportData";

export function WeekendPyqTestPage() {
  const input = useMemo(() => getDefaultTuitionSupportInput(), []);
  const test = useMemo(() => buildWeekendPyqTest(input), [input]);
  const [activeQuestionId, setActiveQuestionId] = useState(test.questions[0]?.id ?? "");
  const [answers, setAnswers] = useState<Record<string, string>>({});
  const [marked, setMarked] = useState<Set<string>>(() => new Set());
  const [result, setResult] = useState<WeekendPyqResult | null>(null);
  const activeQuestion = test.questions.find((question) => question.id === activeQuestionId) ?? test.questions[0];

  function updateAnswer(questionId: string, answer: string) {
    setAnswers((current) => ({ ...current, [questionId]: answer }));
  }

  function toggleMarked(questionId: string) {
    setMarked((current) => {
      const next = new Set(current);
      if (next.has(questionId)) next.delete(questionId);
      else next.add(questionId);
      return next;
    });
  }

  function submitTest() {
    const nextResult = evaluatePyqAnswers(test, answers);
    setResult(nextResult);
    updatePhase3Activity({
      pyqSubmittedAt: new Date().toISOString(),
      pyqScore: nextResult.score,
      pyqMaxScore: nextResult.maxScore,
      weakTopicsAdded: nextResult.weakAreas,
    });
  }

  return (
    <section className="mx-auto grid w-full max-w-7xl gap-5 px-4 py-6 text-white sm:px-6 lg:grid-cols-[220px_minmax(0,1fr)_280px] lg:px-8">
      <aside className="rounded-xl border border-white/[0.08] bg-[#0B0B0B] p-4 lg:sticky lg:top-5 lg:self-start">
        <p className="text-[10px] font-bold uppercase tracking-[0.16em] text-white/34">Navigator</p>
        <div className="mt-4 grid grid-cols-4 gap-2 lg:grid-cols-2">
          {test.questions.map((question, index) => {
            const active = question.id === activeQuestion.id;
            const answered = Boolean(answers[question.id]?.trim());
            return (
              <button
                key={question.id}
                type="button"
                onClick={() => setActiveQuestionId(question.id)}
                className={cn(
                  "relative flex min-h-12 items-center justify-center rounded-lg border text-sm font-bold transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/30",
                  active ? "border-white bg-white text-black" : "border-white/[0.08] bg-white/[0.03] text-white/62 hover:text-white",
                )}
              >
                {index + 1}
                {answered ? <CheckCircle2 className="absolute right-1.5 top-1.5 size-3" /> : null}
                {marked.has(question.id) ? <Flag className="absolute bottom-1.5 right-1.5 size-3" /> : null}
              </button>
            );
          })}
        </div>
        <Link href="/tuition" className="mt-4 inline-flex min-h-11 w-full items-center justify-center rounded-lg border border-white/[0.1] px-3 text-sm font-semibold text-white/66 hover:border-white/25 hover:text-white">
          Back to Today&apos;s Tuition
        </Link>
      </aside>

      <main className="min-w-0 space-y-5">
        <header className="border-b border-white/[0.1] pb-5">
          <div className="flex flex-wrap items-start justify-between gap-4">
            <div>
              <p className="text-[10px] font-bold uppercase tracking-[0.16em] text-white/34">{test.weekLabel}</p>
              <h1 className="mt-2 text-2xl font-semibold text-white sm:text-3xl">Previous Questions Practice</h1>
              <p className="mt-2 text-sm leading-6 text-white/56">
                Sample exam practice for {test.subject} - {test.chapter}. More previous-question practice is coming as the beta improves.
              </p>
            </div>
            <span className="inline-flex items-center gap-2 rounded-full border border-white/[0.1] bg-white/[0.04] px-3 py-1.5 text-xs font-semibold text-white/60">
              <Clock className="size-4" />
              {test.estimatedMinutes} min
            </span>
          </div>
          <p className="mt-4 border-l-2 border-white/15 pl-3 text-xs leading-5 text-white/48">{test.safePatternNote}</p>
        </header>

        <article className="rounded-xl border border-white/[0.08] bg-[#0B0B0B] p-5 sm:p-6">
          <div className="flex flex-wrap items-center justify-between gap-3">
            <div>
              <p className="text-[11px] font-bold uppercase tracking-[0.16em] text-white/34">{activeQuestion.label}</p>
              <h2 className="mt-2 text-lg font-semibold leading-7 text-white">{activeQuestion.question}</h2>
            </div>
            <span className="rounded-full border border-white/[0.1] px-2.5 py-1 text-xs font-semibold text-white/56">
              {activeQuestion.marks} marks
            </span>
          </div>
          <textarea
            aria-label="Write your board-style answer"
            value={answers[activeQuestion.id] ?? ""}
            onChange={(event) => updateAnswer(activeQuestion.id, event.target.value)}
            rows={10}
            placeholder="Write your board-style answer here. Include exam keywords."
            className="mt-5 min-h-56 w-full resize-none rounded-lg border border-white/[0.12] bg-[#050505] p-4 text-base leading-7 text-white placeholder:text-white/34 focus:border-white/30 focus:outline-none focus-visible:ring-2 focus-visible:ring-white/30"
          />
          <div className="mt-4 flex flex-wrap items-center justify-between gap-3">
            <button
              type="button"
              onClick={() => toggleMarked(activeQuestion.id)}
              className="inline-flex min-h-11 items-center gap-2 rounded-lg border border-white/[0.1] px-3 text-sm font-semibold text-white/66 transition hover:border-white/25 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/30"
            >
              <Flag className="size-4" />
              {marked.has(activeQuestion.id) ? "Marked for review" : "Mark for review"}
            </button>
            <button
              type="button"
              onClick={submitTest}
              className="inline-flex min-h-11 items-center gap-2 rounded-lg bg-white px-4 text-sm font-bold text-black transition hover:bg-white/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/40 focus-visible:ring-offset-2 focus-visible:ring-offset-black"
            >
              <Send className="size-4" />
              Submit practice
            </button>
          </div>
        </article>
      </main>

      <aside className="space-y-5 lg:sticky lg:top-5 lg:self-start">
        <section className="rounded-xl border border-white/[0.08] bg-[#0B0B0B] p-4">
          <p className="text-[10px] font-bold uppercase tracking-[0.16em] text-white/34">Sections</p>
          <div className="mt-3 divide-y divide-white/[0.08] border-y border-white/[0.08]">
            {["A", "B"].map((section) => (
              <div key={section} className="py-3">
                <p className="text-sm font-semibold text-white">Section {section}</p>
                <p className="mt-1 text-xs text-white/44">
                  {section === "A" ? "Previous-question style" : "Exam practice"}
                </p>
              </div>
            ))}
          </div>
        </section>

        {result ? (
          <section className="rounded-xl border border-white/[0.08] bg-white p-4 text-black">
            <p className="text-[10px] font-bold uppercase tracking-[0.16em] text-black/45">Answer review</p>
            <p className="mt-2 text-3xl font-black">{result.score}/{result.maxScore}</p>
            <div className="mt-4 space-y-3 text-sm">
              <ReviewBlock title="Board-style answer" items={[result.correctAnswer]} />
              <ReviewBlock title="Missing keywords" items={result.missingKeywords.length ? result.missingKeywords : ["No major keyword gap detected."]} />
              <ReviewBlock title="Marks lost" items={[`${result.maxScore - result.score} mark${result.maxScore - result.score === 1 ? "" : "s"} lost from missing keywords or unclear points.`]} />
              <ReviewBlock title="Topics to revise" items={result.weakAreas.length ? result.weakAreas : ["No weak area added."]} />
              <p className="rounded-lg bg-black/[0.06] p-3 text-xs leading-5 text-black/70">{result.suggestedRevision}</p>
            </div>
            <Link href="/tuition/revision" className="mt-4 inline-flex min-h-11 w-full items-center justify-center rounded-lg bg-black px-3 text-sm font-bold text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-black/40">
              Add weak topics to revision
            </Link>
          </section>
        ) : (
          <section className="border-y border-white/[0.08] py-4">
            <p className="text-[10px] font-bold uppercase tracking-[0.16em] text-white/34">After submit</p>
            <p className="mt-2 text-sm leading-6 text-white/52">
              You will see score, board-style answer, missing keywords, marks lost, and revision suggestions.
            </p>
          </section>
        )}
      </aside>
    </section>
  );
}

function ReviewBlock({ title, items }: { title: string; items: string[] }) {
  return (
    <div>
      <p className="text-[10px] font-bold uppercase tracking-[0.14em] text-black/45">{title}</p>
      <ul className="mt-1 space-y-1">
        {items.map((item) => (
          <li key={item} className="text-xs leading-5 text-black/70">{item}</li>
        ))}
      </ul>
    </div>
  );
}