Surface Create entry and creations, hidden when unconfigured
This commit is contained in:
+47
-48
@@ -4,6 +4,9 @@ import { LEVELS, getSubjects, getGroupedSubjects, BEGINNER_PACKS, lessonStepCoun
|
||||
import { getCurrentUser } from "@/lib/session";
|
||||
import { getCompletedSteps, hasBadge } from "@/lib/progress";
|
||||
import { isLevelUnlocked, countCompletedLessons, isPackComplete, BEGINNER_UNLOCK_THRESHOLD } from "@/lib/gating";
|
||||
import { canCreate } from "@/lib/session";
|
||||
import { isCreateEnabled } from "@/lib/comfyui";
|
||||
import { listCreatedForUser, listPromoted, buildCreatedLessons, type CreatedLessonRow } from "@/lib/createdLessons";
|
||||
|
||||
export const metadata = { title: "Learn · DrawIt" };
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -70,6 +73,47 @@ export default async function LearnPage() {
|
||||
);
|
||||
};
|
||||
|
||||
const creator = canCreate(user);
|
||||
const createEnabled = isCreateEnabled();
|
||||
const toCreatedSubject = (row: CreatedLessonRow) => ({ key: row.slug, name: row.subject, emoji: row.emoji, order: 0, lessons: buildCreatedLessons(row) });
|
||||
|
||||
// "Create your own" entry + the user's creations + any promoted (global) ones, for EB/Beginner.
|
||||
const creationsSection = (lvlKey: string) => {
|
||||
if (lvlKey !== "early-beginner" && lvlKey !== "beginner") return null;
|
||||
// Create UI is hidden entirely unless ComfyUI is configured (being a Creator alone isn't enough).
|
||||
const canCreateNow = creator && createEnabled;
|
||||
const mine: CreatedLessonRow[] = user && creator ? listCreatedForUser(user.id, lvlKey) : [];
|
||||
const featured: CreatedLessonRow[] = listPromoted(lvlKey);
|
||||
const ready = mine.filter((r) => r.status === "ready");
|
||||
const pending = mine.filter((r) => ["pending", "generating", "needs_review"].includes(r.status));
|
||||
// Nothing to show: Create disabled and no existing or featured creations.
|
||||
if (!canCreateNow && ready.length === 0 && pending.length === 0 && featured.length === 0) return null;
|
||||
return (
|
||||
<div style={{ border: "2px dashed var(--primary)", borderRadius: 16, padding: "12px 14px" }}>
|
||||
<div className="row" style={{ justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 8 }}>
|
||||
<strong style={{ fontSize: "1.05rem" }}>{canCreateNow ? "✨ Create your own" : "✨ My Creations"}</strong>
|
||||
{canCreateNow && <Link className="btn" style={{ minHeight: 36, padding: "6px 12px" }} href={`/create?level=${lvlKey}`}>+ New lesson</Link>}
|
||||
</div>
|
||||
<div className="stack" style={{ marginTop: 10 }}>
|
||||
{ready.map((r) => subjectAccordion(toCreatedSubject(r), false))}
|
||||
{pending.map((r) => (
|
||||
<div key={r.slug} style={phaseBox}>
|
||||
<strong>{r.emoji} {r.subject}</strong>{" "}
|
||||
<span className="muted" style={{ fontSize: "0.82rem" }}>{r.status === "needs_review" ? "⏳ Waiting for approval" : "🎨 Drawing…"}</span>
|
||||
</div>
|
||||
))}
|
||||
{canCreateNow && ready.length === 0 && pending.length === 0 && <p className="muted" style={{ fontSize: "0.9rem", margin: 0 }}>No creations yet — tap “+ New lesson”.</p>}
|
||||
{featured.length > 0 && (
|
||||
<>
|
||||
<strong style={{ fontSize: "0.95rem", marginTop: 6 }}>🌟 Featured creations</strong>
|
||||
{featured.map((r) => subjectAccordion(toCreatedSubject(r), false))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SiteNav />
|
||||
@@ -116,6 +160,7 @@ export default async function LearnPage() {
|
||||
) : lvl.key === "beginner" ? (
|
||||
// Beginner: group subjects into packs, each with a per-pack badge.
|
||||
<div className="stack" style={{ marginTop: 6 }}>
|
||||
{creationsSection(lvl.key)}
|
||||
{BEGINNER_PACKS.map((pack) => {
|
||||
const packSubjects = subjects.filter((s) => pack.subjectKeys.includes(s.key));
|
||||
const packDone = !!user && isPackComplete(user.id, pack);
|
||||
@@ -136,51 +181,5 @@ export default async function LearnPage() {
|
||||
) : lvl.key === "early-beginner" ? (
|
||||
// Early Beginner: themed groups, each holding its subjects.
|
||||
<div className="stack" style={{ marginTop: 6 }}>
|
||||
{getGroupedSubjects(lvl.key).map((group) => {
|
||||
const groupDone = group.subjects.every((s) => subjectComplete(s));
|
||||
return (
|
||||
<div key={group.key} style={{ border: groupDone ? "2px solid var(--leaf)" : "2px dashed var(--line)", borderRadius: 16, padding: "12px 14px" }}>
|
||||
<div className="row" style={{ justifyContent: "space-between", alignItems: "center" }}>
|
||||
<strong style={{ fontSize: "1.05rem" }}>{group.emoji} {group.name}</strong>
|
||||
<span className="muted" style={{ fontSize: "0.82rem" }}>{group.subjects.filter((s) => subjectComplete(s)).length}/{group.subjects.length} done</span>
|
||||
</div>
|
||||
<div className="stack" style={{ marginTop: 10 }}>
|
||||
{group.subjects.map((subj) => subjectAccordion(subj, subj.key === firstIncomplete))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
// Any future flat level
|
||||
<div className="stack" style={{ marginTop: 6 }}>
|
||||
{subjects.map((subj) => {
|
||||
const phased = subj.lessons.length > 1 || !!subj.lessons[0].phase;
|
||||
if (!phased) {
|
||||
const l = subj.lessons[0];
|
||||
const done = isDone(l);
|
||||
const inProg = !done && doneCount(l) > 0;
|
||||
return (
|
||||
<Link key={subj.key} href={`/learn/${l.level}/${l.slug}`} className="card" style={subCard(done)}>
|
||||
<div className="row" style={{ justifyContent: "space-between", alignItems: "center" }}>
|
||||
<strong>{subj.emoji} {l.title}</strong>
|
||||
{done ? <span className="pill active">✓ Done</span> : inProg ? <span className="pill pending">Step {Math.min(doneCount(l) + 1, lessonStepCount(l))}/{lessonStepCount(l)}</span> : <span className="muted" style={{ fontSize: "0.85rem" }}>{lessonStepCount(l)} steps</span>}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
return subjectAccordion(subj, subj.key === firstIncomplete);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const subCard = (done: boolean): React.CSSProperties => ({ textDecoration: "none", color: "inherit", border: done ? "2px solid var(--leaf)" : "2px solid var(--line)", boxShadow: "none", padding: 14 });
|
||||
const phaseBox: React.CSSProperties = { display: "block", border: "2px solid var(--line)", borderRadius: 12, padding: "12px 14px", background: "var(--surface)" };
|
||||
{creationsSection(lvl.key)}
|
||||
{getGroupedSubjects(lvl.key).map((group) =
|
||||
Reference in New Issue
Block a user