feat: 契约生命周期 + 到期通知 + 成就墙

- 扩展 WeekendPlan schema: 新增 endTime 字段与 userId 索引
- PATCH /api/blindbox/plan 支持 accept/complete/expire 操作,
  接受时自动计算契约结束时间
- GET /api/blindbox/plan 支持 mode 参数 (latest/pending/history)
- 房间页接受契约后自动返回想法池,顶部显示"契约进行中"指示器
- 契约到期时触发浏览器通知 + 页面加载时弹出完成确认弹窗
- 新增 /achievements 成就墙页面:统计数据 + 决策记录/契约记录双标签
- 首页和个人中心新增成就墙入口
This commit is contained in:
2026-02-27 02:12:18 +08:00
parent d8e42b860f
commit c17b13b6a8
10 changed files with 889 additions and 36 deletions
+16 -1
View File
@@ -15,6 +15,8 @@ import {
Eye,
EyeOff,
Zap,
Trophy,
ChevronRight,
} from "lucide-react";
import Card from "@/components/Card";
import Input from "@/components/Input";
@@ -485,13 +487,26 @@ export default function ProfilePage() {
)}
</Card>
{/* Achievements link */}
<Card animated className="mt-4" delay={0.15}>
<button
onClick={() => router.push("/achievements")}
className="flex w-full items-center gap-2"
>
<Trophy size={15} className="text-amber-400" />
<h3 className="text-sm font-semibold text-secondary"></h3>
<span className="text-[10px] text-dim"> · </span>
<ChevronRight size={14} className="ml-auto text-muted" />
</button>
</Card>
<ProfileHistoryCard
history={history}
loading={historyLoading}
open={showHistory}
onToggle={() => setShowHistory((v) => !v)}
onEmpty={() => router.push("/blindbox")}
delay={0.15}
delay={0.2}
/>
<ProfileFavoritesCard