File size: 8,898 Bytes
2b62b0c
 
dbeb485
c1e702e
dbeb485
 
 
c1e702e
dbeb485
 
 
 
 
85cb661
 
dbeb485
 
 
 
bb6e880
dbeb485
 
 
 
c1e702e
dbeb485
 
 
 
 
85cb661
 
dbeb485
 
 
 
c1e702e
bb6e880
c1e702e
 
 
 
 
dbeb485
 
 
 
 
 
 
 
 
c1e702e
dbeb485
c1e702e
 
 
 
 
 
 
 
 
 
dbeb485
 
 
 
 
 
 
 
 
 
c1e702e
 
dbeb485
 
c1e702e
dbeb485
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bb6e880
 
c1e702e
bb6e880
 
 
 
 
dbeb485
 
c1e702e
dbeb485
 
 
 
c1e702e
dbeb485
 
 
 
 
c1e702e
dbeb485
 
 
bb6e880
 
 
 
 
 
dbeb485
 
 
 
 
 
 
 
 
 
 
 
 
c1e702e
dbeb485
85cb661
dbeb485
 
85cb661
 
 
 
 
 
 
dbeb485
 
 
c1e702e
 
 
 
 
 
 
 
 
 
 
 
 
 
dbeb485
 
 
 
 
 
 
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
186
187
188
189
190
import { Button } from "@pram/ui/components/button";
import { Card, CardContent, CardHeader, CardTitle } from "@pram/ui/components/card";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { EXAM_TYPES, SECTIONS } from "@/lib/generate-constants";

interface TestBlueprintCardProps {
  examType: string;
  selectedSections: string[];
  selectedFormats: string[];
  questionCount: number;
  weaknessAlign: number;
  mode: "quick" | "agentic";
  setMode: (mode: "quick" | "agentic") => void;
  activeCount: number;
  maxParallel: number;
  generatePending: boolean;
  hasKey: boolean;
  error: string | null;
  onGenerate: () => void;
  disableQuick?: boolean;
}

export function TestBlueprintCard({
  examType,
  selectedSections,
  selectedFormats,
  questionCount,
  weaknessAlign,
  mode,
  setMode,
  activeCount,
  maxParallel,
  generatePending,
  hasKey,
  error,
  onGenerate,
  onDismissError,
  disableQuick,
}: TestBlueprintCardProps & { onDismissError?: () => void }) {
  const sectionNames = selectedSections
    .map((id) => SECTIONS.find((s) => s.id === id)?.name)
    .filter(Boolean);

  return (
    <div className="lg:col-span-4 sticky top-8">
      <Card className="bg-[var(--pure-white)] border-2 border-[var(--oat-border)] clay-shadow rounded-[var(--radius-xl)]">
        <CardHeader>
          <div className="flex items-center gap-3">
            <MaterialIcon name="analytics" className="text-[var(--matcha-600)]" />
            <CardTitle className="font-headline text-xl text-[var(--clay-black)]">Test Blueprint</CardTitle>
          </div>
        </CardHeader>
        <CardContent className="space-y-5">
          <div className="flex justify-between items-center pb-4 border-b border-[var(--oat-border)]">
            <span className="text-[var(--warm-charcoal)]">Ujian</span>
            <span className="font-bold text-[var(--clay-black)]">
              {EXAM_TYPES.find((t) => t.id === examType)?.name}
            </span>
          </div>
          <div className="flex justify-between items-center pb-4 border-b border-[var(--oat-border)]">
            <span className="text-[var(--warm-charcoal)]">Section</span>
            <span className="font-bold text-[var(--clay-black)] text-right">
              {sectionNames.join(" + ")}
            </span>
          </div>
          <div className="flex justify-between items-center pb-4 border-b border-[var(--oat-border)]">
            <span className="text-[var(--warm-charcoal)]">Jumlah Soal</span>
            <span className="font-bold text-[var(--clay-black)]">{questionCount} Soal</span>
          </div>
          <div className="flex justify-between items-center pb-4 border-b border-[var(--oat-border)]">
            <span className="text-[var(--warm-charcoal)]">Format</span>
            <span className="font-bold text-[var(--clay-black)]">{selectedFormats.length} Jenis</span>
          </div>
          <div className="flex justify-between items-center pb-4 border-b border-[var(--oat-border)]">
            <span className="text-[var(--warm-charcoal)]">Estimasi Durasi</span>
            <span className="font-bold text-[var(--clay-black)]">{questionCount * 2} Menit</span>
          </div>

          {/* AI Confidence Score */}
          <div className="bg-[var(--oat-light)] rounded-[var(--radius-lg)] p-6 flex flex-col items-center gap-4 relative overflow-hidden">
            <div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-[var(--matcha-600)] to-transparent" />
            <span className="text-xs font-label uppercase tracking-widest text-[var(--warm-charcoal)] font-bold">
              AI Confidence Score
            </span>
            <div className="relative flex items-center justify-center">
              <svg className="w-32 h-32 transform -rotate-90">
                <circle className="text-[var(--warm-silver)]" cx="64" cy="64" fill="transparent" r="56" stroke="currentColor" strokeWidth="8" />
                <circle
                  className="text-[var(--matcha-600)]"
                  cx="64"
                  cy="64"
                  fill="transparent"
                  r="56"
                  stroke="currentColor"
                  strokeDasharray="351.85"
                  strokeDashoffset={351.85 - (351.85 * (weaknessAlign / 100))}
                  strokeWidth="8"
                />
              </svg>
              <span className="absolute text-3xl font-headline font-extrabold text-[var(--clay-black)]">{Math.round(weaknessAlign)}%</span>
            </div>
            <p className="text-[11px] text-center text-[var(--warm-charcoal)] px-2 leading-tight">
              Probabilitas tinggi untuk mengatasi blindspot linguistik utama.
            </p>
          </div>

          {/* Mode Toggle */}
          <div className="flex gap-1 p-1 rounded-[var(--radius-lg)] bg-[var(--oat-light)]">
            <button
              onClick={() => !disableQuick && setMode("quick")}
              disabled={disableQuick}
              className={`flex-1 py-2.5 px-3 rounded-[var(--radius-md)] text-sm font-semibold transition-all flex items-center justify-center gap-2 min-h-[44px] ${
                disableQuick
                  ? "bg-[var(--oat-light)] text-[var(--warm-silver)] cursor-not-allowed opacity-50"
                  : mode === "quick"
                    ? "bg-[var(--pure-white)] text-[var(--clay-black)] clay-shadow"
                    : "text-[var(--warm-charcoal)] hover:text-[var(--clay-black)]"
              }`}
            >
              <MaterialIcon name="flash_on" className="text-sm" />
              Quick
            </button>
            <button
              onClick={() => setMode("agentic")}
              className={`flex-1 py-2.5 px-3 rounded-[var(--radius-md)] text-sm font-semibold transition-all flex items-center justify-center gap-2 min-h-[44px] ${
                mode === "agentic"
                  ? "bg-[var(--pure-white)] text-[var(--clay-black)] clay-shadow"
                  : "text-[var(--warm-charcoal)] hover:text-[var(--clay-black)]"
              }`}
            >
              <MaterialIcon name="psychology" className="text-sm" />
              Agentic
            </button>
          </div>
          {disableQuick && (
            <p className="text-xs text-[var(--warm-charcoal)] flex items-center gap-1">
              <MaterialIcon name="info" className="text-xs shrink-0" />
              Reading/Writing membutuhkan Agentic mode (min 20 soal)
            </p>
          )}

          {mode === "agentic" && (
            <div className="p-3 rounded-[var(--radius-md)] bg-[var(--badge-blue-bg)] border-2 border-[var(--badge-blue-bg)] text-xs text-[var(--badge-blue-text)]">
              <div className="flex items-center gap-2 mb-1">
                <MaterialIcon name="info" className="text-sm" />
                <span className="font-semibold">Mode Agentic</span>
              </div>
              <p>Multi-step validation: passage → validate → questions → self-check → quality score. Lebih lambat tapi kualitas lebih terjamin.</p>
            </div>
          )}

          {/* Primary CTA */}
          <Button
            className="w-full py-5 rounded-[var(--radius-lg)] bg-[var(--clay-black)] text-[var(--pure-white)] font-bold text-lg flex items-center justify-center gap-3 clay-shadow clay-hover hover:bg-[var(--warm-charcoal)] transition-all active:scale-95 h-auto min-h-[56px]"
            onClick={onGenerate}
            disabled={activeCount >= maxParallel || generatePending || selectedFormats.length === 0 || !hasKey}
          >
            <MaterialIcon name="auto_awesome" className="group-hover:rotate-12 transition-transform" />
            {activeCount >= maxParallel
              ? `Maks ${maxParallel} Paralel`
              : activeCount > 0
                ? mode === "agentic"
                  ? "Agentic..."
                  : "Generating..."
                : "Generate & Launch"}
          </Button>

          {error && (
            <div className="p-4 rounded-[var(--radius-md)] bg-[var(--pomegranate-400)]/10 text-[var(--pomegranate-400)] text-sm border-2 border-[var(--pomegranate-400)]/20">
              <div className="flex items-start gap-2">
                <MaterialIcon name="error" className="text-sm shrink-0 mt-0.5" />
                <span className="flex-1">{error}</span>
                {onDismissError && (
                  <button
                    onClick={onDismissError}
                    className="shrink-0 p-1 rounded hover:bg-[var(--pomegranate-400)]/10 transition-colors"
                    aria-label="Tutup error"
                  >
                    <MaterialIcon name="close" className="text-sm" />
                  </button>
                )}
              </div>
            </div>
          )}
        </CardContent>
      </Card>
    </div>
  );
}