feat: 已接受契约持久化 — 保存/加载/自动展示

- PATCH /api/blindbox/plan: 接受契约时更新状态为 accepted
- GET /api/blindbox/plan: 查询房间内最近一次已接受的计划
- 进入房间时自动加载已接受计划并展示行程视图
- 修复整个周末想法不足时重复活动的问题(不足则只生成一天)
This commit is contained in:
2026-02-27 01:57:42 +08:00
parent 9c680ec11e
commit d8e42b860f
2 changed files with 85 additions and 8 deletions
+34 -3
View File
@@ -71,6 +71,7 @@ export default function BlindboxRoomPage() {
const [confirmLeave, setConfirmLeave] = useState(false);
const [leaving, setLeaving] = useState(false);
const [locating, setLocating] = useState(false);
const [planId, setPlanId] = useState<string | null>(null);
const [planDays, setPlanDays] = useState<WeekendPlanData[]>([]);
const [planAccepted, setPlanAccepted] = useState(false);
const [generating, setGenerating] = useState(false);
@@ -136,9 +137,28 @@ export default function BlindboxRoomPage() {
} catch { /* ignore */ }
}, [room]);
const fetchAcceptedPlan = useCallback(async () => {
const p = getCachedProfile();
if (!room || !p) return;
try {
const res = await fetch(`/api/blindbox/plan?roomId=${room.id}&userId=${p.id}`);
if (!res.ok) return;
const data = await res.json();
if (data.plan) {
setPlanId(data.plan.id);
setPlanDays(data.plan.days);
setPlanAccepted(true);
setPhase("plan_reveal");
}
} catch { /* ignore */ }
}, [room]);
useEffect(() => {
if (isMember && room) fetchIdeas();
}, [isMember, room, fetchIdeas]);
if (isMember && room) {
fetchIdeas();
fetchAcceptedPlan();
}
}, [isMember, room, fetchIdeas, fetchAcceptedPlan]);
useEffect(() => {
if (isMember && inputRef.current) {
@@ -212,6 +232,7 @@ export default function BlindboxRoomPage() {
throw new Error(data.error || "生成失败");
}
const data = await res.json();
setPlanId(data.id);
setPlanDays(data.days);
setPlanAccepted(false);
setPhase("plan_reveal");
@@ -782,9 +803,18 @@ export default function BlindboxRoomPage() {
days={planDays}
accepted={planAccepted}
regenerating={generating}
onAccept={() => {
onAccept={async () => {
setPlanAccepted(true);
fireConfetti();
if (planId && profile) {
try {
await fetch("/api/blindbox/plan", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ planId, userId: profile.id }),
});
} catch { /* best-effort */ }
}
}}
onRegenerate={() => {
setPhase("time_select");
@@ -792,6 +822,7 @@ export default function BlindboxRoomPage() {
onShare={() => setShowPlanShareCard(true)}
onBack={() => {
setPhase("pool");
setPlanId(null);
setPlanDays([]);
setPlanAccepted(false);
}}