Create your family
+Set up chores and how much each one is worth. Your kids join in seconds with an invite code.
+diff --git a/frontend/src/lib/components/index.ts b/frontend/src/lib/components/index.ts index 39a8b2b..37cd171 100644 --- a/frontend/src/lib/components/index.ts +++ b/frontend/src/lib/components/index.ts @@ -7,3 +7,4 @@ export { default as CardGrid } from './CardGrid.svelte'; export { default as Button } from './Button.svelte'; export { default as Accordion } from './Accordion.svelte'; export { default as Chat } from './Chat.svelte'; +export { default as AuthShell } from './AuthShell.svelte'; diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index cc88df0..9156dfc 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -1,2 +1,337 @@ -
Visit svelte.dev/docs/kit to read the documentation
+ + ++ FamChore turns everyday household chores into points and pocket money. Assign the + chores, let your kids see their progress live, and FamChore calculates the allowance + automatically — no spreadsheets, no nagging. +
+Free to get going. Takes about a minute.
+ ++ Already have a family? Log in +
+Set up chores and how much each one is worth. Your kids join in seconds with an invite code.
+They see today's chores as a simple card board and tick them off as they go — points are added instantly.
+Points add up, then turn into pocket money on payday. Rewards and monthly bonuses keep it fun.
+Reusable chore templates and a simple assignment grid. Build once, reuse every week.
+Realtime sync across the whole family — see points and progress update the moment a chore is done.
+Points convert to money using your family's own rules. No mental maths, no arguments.
+Kids can spend points on rewards, and you can run a monthly bonus for the top earner.
+A simple, colourful interface kids love — big buttons, clear feedback, their own space.
+Everything is scoped to your family. Kids join with an invite code and stay in your family.
+Ready to make chores painless?
+ Create your family +Enter your name to join. It must match a member slot created by your admin.
- - + + diff --git a/frontend/src/routes/join/[code]/[member]/+page.svelte b/frontend/src/routes/join/[code]/[member]/+page.svelte index 0cb1f4c..e0bb402 100644 --- a/frontend/src/routes/join/[code]/[member]/+page.svelte +++ b/frontend/src/routes/join/[code]/[member]/+page.svelte @@ -1,24 +1,45 @@ -{form.error}
+ {/if} -{#if form?.error} -{form.error}
-{:else} -Click below to join as {memberName}
-{/if} - - + +Don't have a family yet? Create one
++ Don't have a family yet? Create one +
+ diff --git a/frontend/src/routes/signup/+page.svelte b/frontend/src/routes/signup/+page.svelte index 1d1e8b4..8ee50fe 100644 --- a/frontend/src/routes/signup/+page.svelte +++ b/frontend/src/routes/signup/+page.svelte @@ -1,4 +1,6 @@ -{form.error}
+{form.error}
{/if} - +Already have an account? Log in
++ Already have a family? Log in +
+ diff --git a/proxy/src/index.ts b/proxy/src/index.ts index 0137e77..b022a0c 100644 --- a/proxy/src/index.ts +++ b/proxy/src/index.ts @@ -341,21 +341,26 @@ app.post("/api/members/verify-token", async (c) => { const { deviceToken, famSlug } = await c.req.json(); if (!deviceToken || !famSlug) return c.json({ error: "deviceToken, famSlug required" }, 400); - const fams = await pb.getList("fams", `slug = '${famSlug}'`); - const fam = fams.items?.[0]; - if (!fam) return c.json({ error: "Fam not found" }, 404); const hashHex = crypto .createHash("sha256") .update(deviceToken) .digest("hex"); + // Look the member up by its (unique) device token hash, then confirm the + // requested slug belongs to that member's own fam. Looking up by slug first + // is unsafe because slug isn't unique — duplicate fams (e.g. after dev↔prod + // store drift) would resolve to the wrong family and reject valid tokens. const members = await pb.getList( "members", - `famId = '${fam.id}' && deviceToken = '${hashHex}'`, + `deviceToken = '${hashHex}'`, ); const member = members.items?.[0]; if (!member) return c.json({ error: "Invalid device token" }, 401); + const fams = await pb.getList("fams", `id = '${member.famId}'`); + const fam = fams.items?.[0]; + if (!fam || fam.slug !== famSlug) + return c.json({ error: "Invalid device token" }, 401); return c.json({ - famId: fam.id, + famId: member.famId, memberId: member.id, name: member.name, color: member.color,