Use native navigation for site routes

This commit is contained in:
biss committed 2026-09-16 21:38:59 +08:00
1 parent f613e25407
commit 2674c6b95f
2 files changed
+3 -5

No files matched your search

+1 -2
View File
@@ -1,6 +1,5 @@
"use client";
import Link from "next/link";
import { useEffect, useMemo, useState } from "react";
import { CalendarDays, CheckCircle2, ChevronLeft, ChevronRight, Cloud, LayoutList, Timer } from "lucide-react";
import { Checkbox } from "@/components/ui/checkbox";
@@ -35,7 +34,7 @@ export default function CheckinClient() {
const toggle = (key: string, on: boolean) => void save({ ...record, doneTasks: JSON.stringify(on ? [...done, key] : done.filter(x => x !== key)) });
return <main><div className="shell">
<header><div><p className="eyebrow">CET-6 · 2026</p><h1>大学英语六级 · 90天打卡</h1><p className="sub">从今天的一件事开始,完成比完美重要。</p></div><div className="header-actions"><Link className="overview-link" href="/tasks"><LayoutList size={17} />任务总览</Link><p className="sync"><Cloud size={16} />{saving ? "正在云端保存…" : status}</p></div></header>
<header><div><p className="eyebrow">CET-6 · 2026</p><h1>大学英语六级 · 90天打卡</h1><p className="sub">从今天的一件事开始,完成比完美重要。</p></div><div className="header-actions"><a className="overview-link" href="/tasks"><LayoutList size={17} />任务总览</a><p className="sync"><Cloud size={16} />{saving ? "正在云端保存…" : status}</p></div></header>
<section className="stats"><Stat icon={<CheckCircle2 />} value={Math.round(finished / total * 100) + "%"} label="总体完成度" detail={finished + " / " + total + " 项任务"} /><Stat icon={<CalendarDays />} value={complete + " 天"} label="完整完成" detail="目标共 90 天" /><Stat icon={<CheckCircle2 />} value={day.phase} label="当前阶段" detail="12 月 12 日笔试" /><div className="countdown"><span><Timer size={19} /></span><p>距离六级笔试</p><strong>{remaining.days}<em>天</em> {remaining.hours}<em>时</em> {remaining.minutes}<em>分</em></strong><small>2026 年 12 月 12 日 09:00</small></div></section>
<section className="calendar"><div className="calendar-head"><div><p className="eyebrow">CHECK-IN CALENDAR</p><h2>打卡日历</h2></div><div className="month-tabs">{[8, 9, 10, 11].map(m => <button className={month === m ? "active" : ""} onClick={() => setMonth(m)} key={m}>{m + 1}月</button>)}</div></div><div className="weekdays">{["日", "一", "二", "三", "四", "五", "六"].map(x => <span key={x}>{x}</span>)}</div><div className="calendar-grid">{blanks.map((_, i) => <span key={"e" + i} />)}{dates.map(date => { const idx = plan.findIndex(p => p.date === date), p = plan[idx], n = parseTasks(records[date]?.doneTasks).length, full = !!p && n === p.tasks.length, partial = !!p && n > 0 && !full; return <button disabled={!p} key={date} onClick={() => { if (p) { setSelected(idx); setMonth(new Date(date + "T00:00:00Z").getUTCMonth()); } }} className={"calendar-day " + (idx === selected ? "chosen " : "") + (full ? "full " : partial ? "partial " : "") + (p ? "" : "outside")}><b>{Number(date.slice(-2))}</b>{p && <small>{full ? "完成" : partial ? n + "/" + p.tasks.length : "待打卡"}</small>}</button>; })}</div><p className="calendar-note"><i className="full-dot" />全部完成 <i className="partial-dot" />进行中 <span>点击日期即可查看并打卡</span></p></section>
<section className="layout"><article className="card"><div className="hero"><button className="round" onClick={() => setSelected(n => Math.max(0, n - 1))} disabled={!selected}><ChevronLeft /></button><div><p>{day.phase} · {day.week}</p><h2>{fmt(day.date)}</h2><small>已完成 {done.length} / {day.tasks.length} 项</small></div><button className="round" onClick={() => setSelected(n => Math.min(89, n + 1))} disabled={selected === 89}><ChevronRight /></button><Progress value={done.length / day.tasks.length * 100} /></div><div className="body"><div className="tasks">{day.tasks.map(t => <label className={done.includes(t.key) ? "task done" : "task"} key={t.key}><Checkbox checked={done.includes(t.key)} onCheckedChange={v => toggle(t.key, v === true)} /><b>{t.label}</b><span>{t.text}</span></label>)}</div><div className="review"><p>今日状态</p><div>{["专注", "平稳", "有点累", "需要调整"].map(m => <button className={record.mood === m ? "pill active" : "pill"} key={m} onClick={() => void save({ ...record, mood: record.mood === m ? "" : m })}>{m}</button>)}</div><label>一句复盘(可选)<textarea value={record.note} onChange={e => setRecords(x => ({ ...x, [day.date]: { ...record, note: e.target.value } }))} onBlur={() => void save(record)} placeholder="今天的收获、卡住的地方,或明天要调整的一点。" /></label></div></div></article><aside><div className="dark"><p>当前阶段</p><h2>{day.phase}</h2><p>每天稳步推进,答案会慢慢变得清楚。</p></div><div className="side"><b>90 天阶段</b>{["Ⅰ 恢复期 · 9/13–10/5", "Ⅱ 专项期 · 10/6–11/2", "Ⅲ 真题期 · 11/3–11/29", "Ⅳ 冲刺期 · 11/30–12/11"].map(x => <p key={x}>{x}</p>)}</div></aside></section>
+2 -3
View File
@@ -1,6 +1,5 @@
"use client";
import Link from "next/link";
import { useEffect, useMemo, useState } from "react";
import { ArrowLeft, CheckCircle2, Circle, ListChecks } from "lucide-react";
import { Progress } from "@/components/ui/progress";
@@ -24,9 +23,9 @@ export default function TasksClient() {
const overview = useMemo(() => groups.map(group => { const tasks = plan.flatMap(day => day.tasks.filter(task => task.key === group.key).map(task => ({ ...task, date: day.date, week: day.week, phase: day.phase }))); const complete = tasks.filter(task => parsed(records[task.date]?.doneTasks).includes(group.key)).length; return { ...group, tasks, complete }; }), [records]);
const selected = overview.find(x => x.key === active)!;
return <main><div className="shell task-overview">
<header><div><p className="eyebrow">PLAN OVERVIEW</p><h1>任务总览</h1><p className="sub">按任务类型查看全部安排和完成情况。</p></div><Link className="overview-link" href="/"><ArrowLeft size={17} />返回打卡</Link></header>
<header><div><p className="eyebrow">PLAN OVERVIEW</p><h1>任务总览</h1><p className="sub">按任务类型查看全部安排和完成情况。</p></div><a className="overview-link" href="/"><ArrowLeft size={17} />返回打卡</a></header>
<p className="overview-status">{status}</p>
<section className="type-cards">{overview.map(group => <button key={group.key} onClick={() => setActive(group.key)} className={"type-card " + group.color + (active === group.key ? " selected" : "")}><div><span>{group.title}</span><b>{group.complete} / {group.tasks.length}</b></div><Progress value={group.tasks.length ? group.complete / group.tasks.length * 100 : 0} /><small>{group.hint}</small></button>)}</section>
<section className="task-list-panel"><div className="task-list-head"><div><p className="eyebrow">{selected.title.toUpperCase()}</p><h2>{selected.title}任务</h2><p>{selected.hint}</p></div><div className="type-progress"><b>{selected.complete} / {selected.tasks.length}</b><span>已完成</span></div></div><div className="all-tasks">{selected.tasks.map((task, i) => { const complete = parsed(records[task.date]?.doneTasks).includes(active); return <Link href="/" className={complete ? "task-row completed" : "task-row"} key={task.date + i}><span className="task-state">{complete ? <CheckCircle2 size={19} /> : <Circle size={19} />}</span><div><b>{task.date.slice(5).replace("-", "月")}日 · {task.week}</b><p>{task.text}</p></div><small>{task.phase}</small></Link>; })}</div></section>
<section className="task-list-panel"><div className="task-list-head"><div><p className="eyebrow">{selected.title.toUpperCase()}</p><h2>{selected.title}任务</h2><p>{selected.hint}</p></div><div className="type-progress"><b>{selected.complete} / {selected.tasks.length}</b><span>已完成</span></div></div><div className="all-tasks">{selected.tasks.map((task, i) => { const complete = parsed(records[task.date]?.doneTasks).includes(active); return <a href="/" className={complete ? "task-row completed" : "task-row"} key={task.date + i}><span className="task-state">{complete ? <CheckCircle2 size={19} /> : <Circle size={19} />}</span><div><b>{task.date.slice(5).replace("-", "月")}日 · {task.week}</b><p>{task.text}</p></div><small>{task.phase}</small></a>; })}</div></section>
</div></main>;
}