diff --git a/src/app/learn/[id]/page.tsx b/src/app/learn/[id]/page.tsx index 62651b0..fedc5e4 100644 --- a/src/app/learn/[id]/page.tsx +++ b/src/app/learn/[id]/page.tsx @@ -21,7 +21,7 @@ export default async function LessonPage({ params }: { params: Promise<{ id: str return (
-
+
← 课程地图 @@ -32,8 +32,10 @@ export default async function LessonPage({ params }: { params: Promise<{ id: str 已点亮 )} -

{point.title}

- {point.summary &&

🎯 {point.summary}

} +

+ {point.title} +

+ {point.summary &&

🎯 {point.summary}

}
diff --git a/src/app/parent/actions.ts b/src/app/parent/actions.ts index ec622b1..3f1179b 100644 --- a/src/app/parent/actions.ts +++ b/src/app/parent/actions.ts @@ -162,3 +162,38 @@ export async function revokeSession(sessionId: number) { await db.delete(sessions).where(eq(sessions.id, sessionId)); revalidatePath("/parent"); } + +// ===== 孩子管理:增/删 ===== +export async function addChild(formData: FormData): Promise { + const parent = await getSessionUser(); + if (!parent || parent.role !== "parent") return "请先以家长身份登录"; + const name = String(formData.get("name") ?? "").trim(); + if (!name) return "请填写孩子称呼"; + const [mx] = await db + .select({ m: sql`COALESCE(MAX(${users.sortOrder}), -1)` }) + .from(users) + .where(eq(users.familyId, parent.familyId)); + await db.insert(users).values({ + familyId: parent.familyId, + role: "child", + displayName: name, + sortOrder: mx.m + 1, + }); + revalidatePath("/parent"); + revalidatePath("/learn"); + return null; +} + +export async function removeChild(childId: number): Promise { + const parent = await getSessionUser(); + if (!parent || parent.role !== "parent") return; + const [row] = await db + .select({ id: users.id }) + .from(users) + .where(and(eq(users.id, childId), eq(users.familyId, parent.familyId), eq(users.role, "child"))) + .limit(1); + if (!row) return; + await db.delete(users).where(eq(users.id, childId)); + revalidatePath("/parent"); + revalidatePath("/learn"); +} diff --git a/src/app/parent/child-manage.tsx b/src/app/parent/child-manage.tsx new file mode 100644 index 0000000..88f73e4 --- /dev/null +++ b/src/app/parent/child-manage.tsx @@ -0,0 +1,61 @@ +"use client"; + +import { useState, useTransition } from "react"; +import { Trash2, UserPlus } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { addChild, removeChild } from "./actions"; + +// 孩子管理:增加/删除孩子 +export function ChildManage({ kids }: { kids: { id: number; name: string }[] }) { + const [name, setName] = useState(""); + const [msg, setMsg] = useState(""); + const [pending, startTransition] = useTransition(); + + const add = (e: React.FormEvent) => { + e.preventDefault(); + setMsg(""); + const fd = new FormData(); + fd.set("name", name); + startTransition(async () => { + const r = await addChild(fd); + if (r?.error) setMsg(r.error); + else { + setName(""); + setMsg("已添加,用孩子设备激活即可"); + } + }); + }; + + return ( +
+
+ {kids.map((k) => ( +
+ {k.name} + +
+ ))} +
+
+
+ setName(e.target.value)} placeholder="新孩子称呼,如:徐子惠" maxLength={20} /> +
+ +
+ {msg &&

{msg}

} +
+ ); +} diff --git a/src/app/parent/dashboard.tsx b/src/app/parent/dashboard.tsx index bcf6dc1..dbbfb1a 100644 --- a/src/app/parent/dashboard.tsx +++ b/src/app/parent/dashboard.tsx @@ -5,6 +5,7 @@ import { logout } from "@/app/login/actions"; import { BackgroundBlobs } from "@/components/background-blobs"; import { Button } from "@/components/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { ChildManage } from "./child-manage"; import { DevicesTab } from "./devices-tab"; import { OverviewTab } from "./overview-tab"; import { TemplatesTab } from "./templates-tab"; @@ -26,7 +27,6 @@ export type OverviewData = { days: Record; }[]; }; - export type TemplatesData = { kids: { id: number; @@ -43,7 +43,6 @@ export type TemplatesData = { }[]; }[]; }; - export type DevicesData = { rows: { id: number; @@ -54,11 +53,20 @@ export type DevicesData = { isCurrent: boolean; }[]; }; - export type WeeklyRow = { childName: string; weekKey: string; score: number; passed: boolean }; const tabCls = "data-[state=active]:bg-white data-[state=active]:text-indigo-600 data-[state=active]:shadow-soft"; +function Section({ title, desc, children }: { title: string; desc?: string; children: React.ReactNode }) { + return ( +
+

{title}

+ {desc &&

{desc}

} +
{children}
+
+ ); +} + export function ParentDashboard({ overview, templates, @@ -85,26 +93,25 @@ export function ParentDashboard({
- + 学习概览 - - 任务模板 - - - 课程管理 + + 功能管理 - - - - -
+ + +
+ +
+ +
@@ -124,8 +131,15 @@ export function ParentDashboard({
+ + +
+ ({ id: k.id, name: k.name }))} /> +
+ +
({ id: k.id, name: k.name }))} /> - +