File size: 6,786 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 | "use client";
// βββ Chapter Intelligence Header ββββββββββββββββββββββββββββββββββββββββββββββ
// Surfaces exam intelligence from curriculum metadata above lesson content.
// Renders nothing if chapter cannot be matched (graceful degradation).
import { Clock, Flame, Star, BookMarked, AlertTriangle } from "lucide-react";
import { searchChapters } from "@/lib/curriculum/index";
import {
getExamProfile,
pyqLabel,
difficultyLabel,
} from "@/lib/curriculum/exam-intelligence";
import { cn } from "@/lib/utils";
// βββ Helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function normalizeLevel(raw: string | null | undefined): string | undefined {
if (!raw) return undefined;
const s = raw.toLowerCase();
if (s.includes("sslc") || s.includes("10")) return "SSLC";
if (s.includes("+2") || s.includes("12") || s.includes("plus two") || s.includes("class 12")) return "+2";
if (s.includes("+1") || s.includes("11") || s.includes("plus one") || s.includes("class 11")) return "+1";
return undefined;
}
// βββ Sub-components βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function IntelChip({
children,
variant = "neutral",
}: {
children: React.ReactNode;
variant?: "neutral" | "green" | "amber" | "violet" | "red";
}) {
const variants = {
neutral: "border-white/[0.08] bg-white/[0.04] text-white/50",
green: "border-emerald-400/20 bg-emerald-400/8 text-emerald-400",
amber: "border-amber-400/20 bg-amber-400/8 text-amber-400",
violet: "border-violet-400/20 bg-violet-400/8 text-violet-400",
red: "border-red-400/20 bg-red-400/8 text-red-400",
};
return (
<span
className={cn(
"inline-flex items-center gap-1 rounded-full border px-2.5 py-0.5 text-[11px] font-medium",
variants[variant],
)}
>
{children}
</span>
);
}
// βββ Main component βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
type Props = {
/** Chapter name or topic extracted from study request analysis */
chapterQuery: string;
/** Level hint: "+1", "+2", "SSLC", grade string, or exam string */
levelHint?: string | null;
};
export function ChapterIntelHeader({ chapterQuery, levelHint }: Props) {
if (!chapterQuery) return null;
const level = normalizeLevel(levelHint);
const results = searchChapters(chapterQuery, { level, limit: 1 });
if (results.length === 0) return null;
const { chapter, level: foundLevel, subjectSlug } = results[0];
const profile = getExamProfile(chapter);
const priorityVariant: "green" | "amber" | "violet" | "red" =
profile.priority === "critical" ? "red"
: profile.priority === "important" ? "amber"
: profile.priority === "moderate" ? "violet"
: "neutral" as "violet";
const priorityLabel =
profile.priority === "critical" ? "Exam critical"
: profile.priority === "important" ? "High priority"
: profile.priority === "moderate" ? "Worth studying"
: "Low priority";
return (
<div className="rounded-xl border border-white/[0.07] bg-white/[0.025] px-4 py-3.5">
{/* Top row: chapter identity */}
<div className="flex flex-wrap items-center gap-2">
<span className="text-[12px] font-semibold text-white/70">{chapter.name}</span>
<span className="text-white/25">Β·</span>
<span className="text-[11px] text-white/40">{foundLevel} {subjectSlug.charAt(0).toUpperCase() + subjectSlug.slice(1)}</span>
</div>
{/* Badge row */}
<div className="mt-2.5 flex flex-wrap gap-1.5">
{/* Priority */}
<IntelChip variant={priorityVariant}>
<Flame className="size-2.5" />
{priorityLabel}
</IntelChip>
{/* PYQ frequency */}
<IntelChip variant={chapter.pyqFrequency === "very_high" || chapter.pyqFrequency === "high" ? "amber" : "neutral"}>
<Star className="size-2.5" />
{pyqLabel(chapter)}
</IntelChip>
{/* Difficulty */}
<IntelChip variant={chapter.difficultyScore <= 3 ? "green" : "neutral"}>
{difficultyLabel(chapter)}
</IntelChip>
{/* Study time */}
<IntelChip variant="neutral">
<Clock className="size-2.5" />
{chapter.estimatedMinutes} min
</IntelChip>
{/* Formulas count */}
{chapter.formulas && chapter.formulas.length > 0 ? (
<IntelChip variant="violet">
<BookMarked className="size-2.5" />
{chapter.formulas.length} formula{chapter.formulas.length !== 1 ? "s" : ""}
</IntelChip>
) : null}
{/* Last night priority */}
{chapter.lastNightPriority >= 4 ? (
<IntelChip variant="green">
<AlertTriangle className="size-2.5" />
Study tonight
</IntelChip>
) : null}
</div>
{/* Insight line */}
<p className="mt-2 text-[11.5px] leading-relaxed text-white/40">
{profile.action}
{chapter.examWeightageMarks
? ` Β· ~${chapter.examWeightageMarks} marks in theory paper`
: null}
</p>
{/* Prerequisites */}
{chapter.prerequisites.length > 0 ? (
<div className="mt-2.5 flex flex-wrap items-center gap-1.5">
<span className="text-[10.5px] text-white/28">Before this, revise:</span>
{chapter.prerequisites.map((prereq) => (
<span
key={prereq}
className="rounded-full border border-white/[0.06] bg-white/[0.03] px-2 py-0.5 text-[10.5px] text-white/40"
>
{prereq}
</span>
))}
</div>
) : null}
{/* Top formulas preview */}
{chapter.formulas && chapter.formulas.length > 0 ? (
<div className="mt-2.5 flex flex-wrap gap-1.5">
{chapter.formulas.slice(0, 3).map((f) => (
<code
key={f}
className="rounded border border-amber-400/15 bg-amber-400/5 px-2 py-0.5 font-mono text-[10.5px] text-amber-400/80"
>
{f}
</code>
))}
{chapter.formulas.length > 3 ? (
<span className="text-[10.5px] text-white/28">+{chapter.formulas.length - 3} more</span>
) : null}
</div>
) : null}
</div>
);
}
|