第一期:家庭自学打卡系统

- 家长手机号登录 + 孩子设备一次性激活(长期会话)
- 周模板自动生成每日任务快照,支持拍照凭证直传对象存储
- 连击计算(空白日自动达成)、家长补卡带标记
- 家长端三标签:今日概览 / 任务模板 / 设备管理
- Docker 独立输出,经 Traefik 接入 jump.hn12301.net

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jacobxu666
2026-08-02 23:19:13 +08:00
co-authored by Claude Opus 4.8
commit af7a144d58
57 changed files with 8875 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
"use client";
import { logout } from "@/app/login/actions";
import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { DevicesTab } from "./devices-tab";
import { OverviewTab } from "./overview-tab";
import { TemplatesTab } from "./templates-tab";
export type OverviewData = {
kids: {
id: number;
name: string;
streak: number;
today: { done: number; total: number };
week: {
date: string;
isToday: boolean;
tasks: { id: number; title: string; status: "pending" | "done"; isMakeup: boolean }[];
}[];
}[];
};
export type TemplatesData = {
kids: {
id: number;
name: string;
items: { id: number; dow: number; title: string; requiresPhoto: boolean }[];
}[];
};
export type DevicesData = {
rows: {
id: number;
owner: string;
deviceLabel: string | null;
createdAt: string;
expiresAt: string;
isCurrent: boolean;
}[];
};
export function ParentDashboard({
overview,
templates,
devices,
}: {
overview: OverviewData;
templates: TemplatesData;
devices: DevicesData;
}) {
const kids = overview.kids.map((k) => ({ id: k.id, name: k.name }));
return (
<main className="mx-auto max-w-2xl space-y-5 p-4 pb-10">
<header className="flex items-center justify-between">
<h1 className="text-2xl font-bold">家长端</h1>
<form action={logout}>
<Button type="submit" variant="outline" size="sm">
退出登录
</Button>
</form>
</header>
<Tabs defaultValue="overview">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="overview">今日概览</TabsTrigger>
<TabsTrigger value="templates">任务模板</TabsTrigger>
<TabsTrigger value="devices">设备管理</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="mt-4">
<OverviewTab data={overview} />
</TabsContent>
<TabsContent value="templates" className="mt-4">
<TemplatesTab data={templates} />
</TabsContent>
<TabsContent value="devices" className="mt-4">
<DevicesTab data={devices} kids={kids} />
</TabsContent>
</Tabs>
</main>
);
}