add new GDPR page and handle account creation email

This commit is contained in:
JCEEE
2026-09-08 15:20:34 +01:00
parent 9117c0ec70
commit afc463a0f0
8 changed files with 406 additions and 14 deletions
+29 -4
View File
@@ -11,11 +11,18 @@
<div class="footer-inner px-8 py-16" class:sidebar>
<div class="footer-row flex items-center justify-between gap-2">
<span class="footer-star">✦</span>
<span class="footer-copy"
>&copy; {new Date().getFullYear()}
<span class="footer-text font-bold text-xl"><FamDone /></span> All rights reserved.</span
<div class="footer-center">
<span class="footer-copy"
>&copy; {new Date().getFullYear()}
<span class="footer-text font-bold text-xl"><FamDone /></span> All rights reserved.</span
>
<span class="footer-links">
<a href="/privacy">Privacy Policy</a>
</span>
</div>
<aside
class="footer-aside text-text-200 text-wash-300 flex flex-col justify-end text-right text-sm"
>
<aside class="footer-aside text-text-200 text-wash-300 flex flex-col justify-end text-right text-sm">
<span>Built by humans</span>
<a href="https://threejjjs.xyz">
<h5 class="m-0 text-right font-tertiary text-xl md:text-3xl">
@@ -53,6 +60,24 @@
text-align: center;
padding: 0 1rem;
}
.footer-center {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
text-align: center;
}
.footer-links {
font-size: 0.8rem;
}
.footer-links a {
color: #4c1d95;
text-decoration: none;
}
.footer-links a:hover {
text-decoration: underline;
}
.footer-row {
position: relative;
}
@@ -0,0 +1,46 @@
// Code-specific email templates for successful access-code redemption.
// Key = the raw access-code value a family enters (e.g. "copppermill03").
// Add an entry here and its custom email is sent when that code is used.
// If no entry matches, `defaultAccessEmailTemplate` is used.
export interface AccessEmailVars {
famName: string;
codeName: string;
dashboardUrl: string;
}
export interface AccessEmailTemplate {
subject: (vars: AccessEmailVars) => string;
html: (vars: AccessEmailVars) => string;
}
const DEFAULT_TEMPLATE: AccessEmailTemplate = {
subject: ({ famName }) => `Your ${famName} access is unlocked`,
html: ({ famName, codeName, dashboardUrl }) => `
<p>Hi${famName ? ` ${famName}` : ' there'}! 🎉</p>
<p>Your access code (<strong>${codeName}</strong>) has been applied successfully.</p>
<p>Your <strong>${famName}</strong> family on <strong>FamDone</strong> is now unlocked.</p>
<p><a href="${dashboardUrl}">Open your family dashboard</a> to add kids, set up chores, and get going.</p>
<p>If you have any questions, just reply to this email.</p>
<p>Talk soon,<br />The FamDone team</p>
`
};
export const accessEmailTemplates: Record<string, AccessEmailTemplate> = {
copppermill03: {
subject: ({ famName }) => `Save a spot for ${famName} — access unlocked`,
html: ({ famName, codeName, dashboardUrl }) => `
<p>Hi${famName ? ` ${famName}` : ' there'}! 🎉</p>
<p>Your access code (<strong>${codeName}</strong>) has been applied successfully.</p>
<p>Your <strong>${famName}</strong> family on <strong>FamDone</strong> is now unlocked.</p>
<p><a href="${dashboardUrl}">Open your family dashboard</a> to add kids, set up chores, and get going.</p>
<p>If you have any questions, just reply to this email.</p>
<p>Talk soon,<br />The FamDone team</p>
`
}
};
// Resolve the template for a redeemed code, falling back to the default.
export function getAccessEmailTemplate(codeValue: string): AccessEmailTemplate {
return accessEmailTemplates[codeValue] || DEFAULT_TEMPLATE;
}
+51
View File
@@ -1,5 +1,6 @@
import { Resend } from 'resend';
import { RESEND_API } from '$app/env/private';
import { getAccessEmailTemplate } from './email-templates';
const resend = new Resend(String(RESEND_API));
@@ -43,3 +44,53 @@ export async function sendParentInviteEmail(opts: {
`
});
}
export async function sendAccessUnlockedEmail(opts: {
to: string;
famName: string;
codeName: string;
codeValue: string;
dashboardUrl: string;
}) {
const { to, famName, codeName, codeValue, dashboardUrl } = opts;
const template = getAccessEmailTemplate(codeValue);
const vars = { famName, codeName, dashboardUrl };
await resend.emails.send({
from: 'no-reply@famchamp.ai',
to,
subject: template.subject(vars),
html: template.html(vars)
});
}
export async function sendWelcomeEmail(opts: {
to: string;
famName: string;
dashboardUrl: string;
}) {
const { to, famName, dashboardUrl } = opts;
await resend.emails.send({
from: 'no-reply@famchamp.ai',
to,
subject: `Welcome to FamDone${famName ? `, ${famName}` : ''}!`,
html: `
<p>Hi${famName ? ` ${famName}` : ' there'}! 👋</p>
<p>Welcome to <strong>FamDone</strong> — your family chore and pocket-money app.</p>
<p>Your family is all set up. Here's how to get going:</p>
<ul>
<li>Open your <a href="${dashboardUrl}">family dashboard</a>.</li>
<li>Add your kids and create join codes from <strong>Family Settings</strong>.</li>
<li>Set up chores, rewards, and allowances.</li>
</ul>
<p>
If you haven't already, you can pick a plan and pay from your dashboard to
unlock your family.
</p>
<p>If you have any questions, just reply to this email — we're happy to help.</p>
<p>Talk soon,<br />The FamDone team</p>
`
});
}