feat: implement curriculum management system including automated generation, enrichment, and versioning workflows

This commit is contained in:
RaymondVerhoef
2026-05-24 19:50:20 +02:00
parent 8e01b21a50
commit c5e23c77cd
15 changed files with 1354 additions and 623 deletions

View File

@@ -6,7 +6,7 @@ import Button from '../components/ui/Button';
import Tag from '../components/ui/Tag';
import * as db from '../lib/db';
import { getAssignedTopic } from '../lib/learningService';
import { getYearProgress, getQuarterName, getQuarterForWeek, hasCurriculum as checkHasCurriculum } from '../lib/curriculumService';
import { getYearProgress, getCurriculumCycle, getCurriculumWeek, getActiveVersion } from '../lib/curriculumService';
const Dashboard = () => {
const { state } = useApp();
@@ -22,6 +22,7 @@ const Dashboard = () => {
activity: [],
yearProgress: null,
hasCurriculum: false,
theme: '',
});
useEffect(() => {
@@ -57,23 +58,35 @@ const Dashboard = () => {
let yearProgress = null;
let curriculumExists = false;
try {
curriculumExists = await checkHasCurriculum();
const activeVersion = await getActiveVersion();
curriculumExists = !!activeVersion;
if (curriculumExists) {
yearProgress = await getYearProgress(currentUser.id);
yearProgress = await getYearProgress(currentUser.id, weekNumber);
}
} catch (e) {
console.warn('[Dashboard] Could not load curriculum data:', e.message);
}
setDashData({ topic, learnDone, testResult, top3, myRank, myPoints, activity, yearProgress, hasCurriculum: curriculumExists });
setDashData({
topic,
learnDone,
testResult,
top3,
myRank,
myPoints,
activity,
yearProgress,
hasCurriculum: curriculumExists,
theme: topic?.theme || '',
});
};
load();
}, [currentUser, weekNumber]);
const { topic, learnDone, testResult, top3, myRank, myPoints, activity, yearProgress, hasCurriculum: curriculumActive } = dashData;
const currentQuarter = getQuarterForWeek(weekNumber);
const quarterName = getQuarterName(weekNumber);
const { topic, learnDone, testResult, top3, myRank, myPoints, activity, yearProgress, hasCurriculum: curriculumActive, theme } = dashData;
const currentCycle = getCurriculumCycle(weekNumber);
const currWeek = getCurriculumWeek(weekNumber);
return (
<div className="p-6 md:p-10 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
@@ -81,12 +94,12 @@ const Dashboard = () => {
<h1 className="text-3xl md:text-5xl mb-2">Welcome, {currentUser?.name}</h1>
<p className="text-fg-muted text-lg">
{curriculumActive
? `Week ${weekNumber} · ${quarterName}`
? `Cycle ${currentCycle} · Week ${currWeek} of 26`
: `Here is your overview for week ${weekNumber}.`}
</p>
</header>
{/* Annual Progress Bar (only when curriculum exists) */}
{/* Cycle Progress Bar (only when curriculum exists) */}
{curriculumActive && yearProgress && (
<Card className="border border-bg-warm">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
@@ -107,21 +120,21 @@ const Dashboard = () => {
</span>
</div>
<div>
<h3 className="font-bold text-lg">Annual Progress</h3>
<h3 className="font-bold text-lg">Cycle Progress</h3>
<p className="text-sm text-fg-muted">{yearProgress.completed} of {yearProgress.total} weeks completed</p>
</div>
</div>
<div className="flex items-center gap-3">
<Tag variant="dark" className="text-xs">Q{currentQuarter}</Tag>
<span className="text-sm text-fg-muted">{52 - weekNumber} weeks remaining</span>
<Tag variant="dark" className="text-xs">Cycle {currentCycle}</Tag>
<span className="text-sm text-fg-muted">{26 - currWeek} weeks remaining</span>
</div>
</div>
{/* Visual week progress bar */}
<div className="mt-4 flex gap-[2px] h-2 rounded-full overflow-hidden">
{Array.from({ length: 52 }, (_, i) => {
{Array.from({ length: 26 }, (_, i) => {
const w = i + 1;
const isCurrent = w === weekNumber;
const isPast = w < weekNumber;
const isCurrent = w === currWeek;
const isPast = w < currWeek;
return (
<div
key={w}