From 5a4d42137ba246eeec93f04f70bc1eb7fdbaa1ad Mon Sep 17 00:00:00 2001 From: iamdoubz <> Date: Wed, 17 Jun 2026 21:22:17 -0500 Subject: [PATCH] Show Color it optional step as complete if done, make levels accordion style collapsible --- src/app/learn/page.tsx | 40 ++++++++++++++++++++++--------- src/components/ColoringRunner.tsx | 4 ++++ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/app/learn/page.tsx b/src/app/learn/page.tsx index cd8b86b..43709d8 100644 --- a/src/app/learn/page.tsx +++ b/src/app/learn/page.tsx @@ -17,7 +17,7 @@ export default async function LearnPage() { const isDone = (l: Lesson) => { if (!user) return false; if (l.badgeKey && hasBadge(user.id, l.badgeKey)) return true; - return l.phase === "extra" ? false : doneCount(l) >= stepCount(l); + return doneCount(l) >= stepCount(l); }; return ( @@ -27,12 +27,16 @@ export default async function LearnPage() {

Learn to draw 🎨

Each picture has an Outline lesson and a Details lesson (earn the badge), plus an optional Color it! lesson. - {!user && " Log in to track progress and earn badges."} + {!user && " Log in to track your progress and earn badges."}

{LEVELS.map((lvl, idx) => { const subjects = getSubjects(lvl.key); + const complete = new Map(subjects.map((s) => [s.key, s.lessons.every((l) => isDone(l))])); + // The first not-yet-finished phased subject is expanded; completed ones collapse. + const firstIncomplete = subjects.find((s) => (s.lessons.length > 1 || !!s.lessons[0].phase) && !complete.get(s.key))?.key; + return (
@@ -44,8 +48,8 @@ export default async function LearnPage() {
{subjects.map((subj) => { const phased = subj.lessons.length > 1 || !!subj.lessons[0].phase; + if (!phased) { - // legacy single lesson const l = subj.lessons[0]; const done = isDone(l); const inProg = !done && doneCount(l) > 0; @@ -53,26 +57,28 @@ export default async function LearnPage() {
{subj.emoji} {l.title} - {done ? ✓ Done : inProg ? Step {Math.min(doneCount(l) + 1, stepCount(l))}/{stepCount(l)} : {stepCount(l)} steps} + {done ? ✓ Done : inProg ? Step {Math.min(doneCount(l) + 1, stepCount(l))}/{stepCount(l)} : {stepCount(l)} steps}
); } - // phased subject (Outline / Details / Color it) + const byPhase = (p: string) => subj.lessons.find((l) => l.phase === p); const outline = byPhase("outline"); const detail = byPhase("detail"); const extra = byPhase("extra"); const outlineDone = outline ? isDone(outline) : false; const detailDone = detail ? isDone(detail) : false; + const subjComplete = !!complete.get(subj.key); + const phaseCard = (l: Lesson | undefined, locked: boolean, optional = false) => { if (!l) return null; const done = isDone(l); const inProg = !done && !locked && doneCount(l) > 0; const inner = ( -
+
{PHASE_LABEL[l.phase ?? ""]}{optional ? " (optional)" : ""} - + {locked ? "🔒 Locked" : done ? "✓ Done" : inProg ? `Step ${Math.min(doneCount(l) + 1, stepCount(l))}/${stepCount(l)}` : l.phase === "extra" ? "Add color" : `${stepCount(l)} steps`}
@@ -83,15 +89,27 @@ export default async function LearnPage() { {inner} ); }; + return ( -
- {subj.emoji} {subj.name} -
+
+ + {subj.emoji} {subj.name} + {subjComplete ? ( + ✓ Done + ) : outlineDone || detailDone ? ( + In progress + ) : null} + +
{phaseCard(outline, false)} {phaseCard(detail, !outlineDone)} {phaseCard(extra, !detailDone, true)}
-
+ ); })}
diff --git a/src/components/ColoringRunner.tsx b/src/components/ColoringRunner.tsx index fa7db0e..8d9bebf 100644 --- a/src/components/ColoringRunner.tsx +++ b/src/components/ColoringRunner.tsx @@ -217,6 +217,10 @@ export default function ColoringRunner({ meta, loggedIn, username, alreadyEarned function toggleVisible(id: number) { const l = layersRef.current.find((x) => x.id === id); if (l) { l.visible = !l.visible; composite(); force((n) => n + 1); scheduleSave(); } } async function finish() { + // Record completion of the Color it! lesson so the Learn page can mark it Done. + if (loggedIn) { + try { await fetch("/api/progress", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ level: meta.level, sublevel: meta.sublevel, step: 1 }) }); } catch { /* */ } + } if (hasContent()) { const data = flatten(true); setFinalImage(data); if (loggedIn) await saveFinalDrawing(meta.level, meta.sublevel, "canvas", data); } setDone(true); }