diff --git a/frontend/src/lib/server/email.ts b/frontend/src/lib/server/email.ts
index 1177a35..95350b9 100644
--- a/frontend/src/lib/server/email.ts
+++ b/frontend/src/lib/server/email.ts
@@ -123,7 +123,36 @@ export async function sendMentionEmail(opts: {
${escHtml(authorName)} mentioned you in your family chat:
${escHtml(message)}
Open your family dashboard to read and reply.
- Talk soon,
The FamDone team
- `
+ Talk soon,
The FamDone team
+ `
+ });
+}
+
+// A child asked to collect a reward — ping the parent(s) with the ask and a
+// link straight to the Claims card on the admin dashboard.
+export async function sendClaimRequestEmail(opts: {
+ to: string;
+ parentName: string;
+ childName: string;
+ rewardName: string;
+ rewardValue: string;
+ dashboardUrl: string;
+}) {
+ const { to, parentName, childName, rewardName, rewardValue, dashboardUrl } = opts;
+
+ await resend.emails.send({
+ from: 'no-reply@walthamstow.xyz',
+ to,
+ subject: `${childName} asked to claim ${rewardName}`,
+ html: `
+ Hi ${escHtml(parentName)},
+ ${escHtml(childName)} asked to collect a reward:
+
+ ${escHtml(rewardName)}
+ ${rewardValue ? `— ${escHtml(rewardValue)}` : ''}
+
+ Open the Claims card on your dashboard to review and issue it.
+ Talk soon,
The FamDone team
+ `
});
}
diff --git a/frontend/src/routes/api/members/rewards/[id]/claim/+server.ts b/frontend/src/routes/api/members/rewards/[id]/claim/+server.ts
index d22d6d9..6ecaa05 100644
--- a/frontend/src/routes/api/members/rewards/[id]/claim/+server.ts
+++ b/frontend/src/routes/api/members/rewards/[id]/claim/+server.ts
@@ -1,7 +1,9 @@
import { json, error } from '@sveltejs/kit';
import type { RequestEvent } from '@sveltejs/kit';
-import { createPbClient } from '$lib/server/pocketbase';
+import { createPbClient, pbAdmin } from '$lib/server/pocketbase';
import { createServices } from '$lib/server/services';
+import { sendClaimRequestEmail } from '$lib/server/email';
+import { handleOf } from '@shared/slugify';
export async function POST(event: RequestEvent) {
const u = event.locals.user;
@@ -10,8 +12,36 @@ export async function POST(event: RequestEvent) {
const id = event.params.id!;
try {
const s = createServices(pb, { id: u.id, role: u.role });
- return json(await s.rewards.claim(u.famId, id));
+ const record = await s.rewards.claim(u.famId, id);
+
+ // Tell the parent(s) a claim was requested — best-effort, so a mail
+ // outage can never fail the claim itself.
+ try {
+ const [parents, fam] = await Promise.all([
+ pbAdmin.getList('users', `famId = '${u.famId}' && role = 'parent'`),
+ pbAdmin.getOne('fams', u.famId)
+ ]);
+ const label = record?.label || 'a reward';
+ const idx = label.indexOf(' – ');
+ const rewardName = idx === -1 ? label : label.slice(0, idx);
+ const rewardValue = idx === -1 ? '' : label.slice(idx + 3);
+ for (const parent of parents) {
+ if (!parent.email) continue;
+ await sendClaimRequestEmail({
+ to: parent.email,
+ parentName: parent.name || 'there',
+ childName: u.name || 'Someone',
+ rewardName,
+ rewardValue,
+ dashboardUrl: `${event.url.origin}/${fam?.slug || u.famId}/${handleOf(parent.username)}`
+ });
+ }
+ } catch {
+ // Best-effort only — the claim already succeeded.
+ }
+
+ return json(record);
} catch (e) {
return json({ error: e instanceof Error ? e.message : 'claim failed' }, { status: 400 });
}
-}
\ No newline at end of file
+}