Static Cloudflare Pages site + KV-backed Pages Function for jointly planning Tina's Barcelona visit (7 Jul - 18 Aug 2026). List and calendar views share one client state; dated evening events are radio groups to prevent double-booking, multi-night shows live once in TRIP_DATA.spanning instead of being duplicated per day, and undated collections get optionally date-tagged when picked from the calendar view. Includes a client-side-only password gate and an instructions page. Live at https://summeroffun.hortal.me. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
40 lines
1.2 KiB
JavaScript
40 lines
1.2 KiB
JavaScript
// GET /api/selections -> { picks: { [groupId]: string | string[] } }
|
|
// POST /api/selections { groupId, value } -> merges value into stored picks, returns updated state.
|
|
//
|
|
// Single shared document in KV under key "picks" — no auth, no per-user accounts.
|
|
// Good enough for a private link shared between two people; anyone with the URL can edit.
|
|
|
|
const KEY = "picks";
|
|
|
|
export async function onRequestGet({ env }) {
|
|
const raw = await env.PICKS.get(KEY);
|
|
const picks = raw ? JSON.parse(raw) : {};
|
|
return Response.json({ picks });
|
|
}
|
|
|
|
export async function onRequestPost({ request, env }) {
|
|
let body;
|
|
try {
|
|
body = await request.json();
|
|
} catch {
|
|
return new Response("Invalid JSON body", { status: 400 });
|
|
}
|
|
|
|
const { groupId, value } = body;
|
|
if (!groupId || typeof groupId !== "string") {
|
|
return new Response("groupId is required", { status: 400 });
|
|
}
|
|
|
|
const raw = await env.PICKS.get(KEY);
|
|
const picks = raw ? JSON.parse(raw) : {};
|
|
|
|
if (value === "" || value == null) {
|
|
delete picks[groupId];
|
|
} else {
|
|
picks[groupId] = value; // string for day radio picks, string[] for collection checkboxes
|
|
}
|
|
|
|
await env.PICKS.put(KEY, JSON.stringify(picks));
|
|
return Response.json({ picks });
|
|
}
|