remove payday debug and add multiple ui fixes
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import Footer from '$lib/components/Footer.svelte';
|
||||
import { homeIcon } from '$lib/components/icons';
|
||||
import { homeIcon, checkCircleIcon, chevronRightIcon } from '$lib/components/icons';
|
||||
import { readShortcut, shortcutTarget, type Shortcut } from '$lib/shortcut';
|
||||
|
||||
let shortcut = $state<(Shortcut & { target: string }) | null>(null);
|
||||
@@ -29,7 +29,7 @@
|
||||
</span>
|
||||
<span class="return-text">
|
||||
<span class="return-fam">{shortcut.famName}</span>
|
||||
<span class="return-sub">Jump back in →</span>
|
||||
<span class="return-sub">Jump back in {@html chevronRightIcon}</span>
|
||||
</span>
|
||||
</a>
|
||||
{/if}
|
||||
@@ -45,9 +45,9 @@
|
||||
automatically — no spreadsheets, no nagging.
|
||||
</p>
|
||||
<ul class="hero-ticks">
|
||||
<li>Set up in under a minute</li>
|
||||
<li>Kids join with a simple invite code</li>
|
||||
<li>Allowance lands on payday, automatically</li>
|
||||
<li><span class="tick">{@html checkCircleIcon}</span>Set up in under a minute</li>
|
||||
<li><span class="tick">{@html checkCircleIcon}</span>Kids join with a simple invite code</li>
|
||||
<li><span class="tick">{@html checkCircleIcon}</span>Allowance lands on payday, automatically</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
<p class="card-sub">Free to get going. Takes about a minute.</p>
|
||||
<a class="submit" href="/signup">Create my family</a>
|
||||
<p class="card-alt">
|
||||
<a href="/pricing">See pricing →</a>
|
||||
<a href="/pricing">See pricing {@html chevronRightIcon}</a>
|
||||
</p>
|
||||
<p class="card-alt">
|
||||
Already have a family? <a href="/login">Log in</a>
|
||||
@@ -119,7 +119,7 @@
|
||||
<div class="cta">
|
||||
<p>Ready to make chores painless?</p>
|
||||
<a class="cta-btn" href="/signup">Create your family</a>
|
||||
<p class="card-alt"><a href="/pricing">or see pricing →</a></p>
|
||||
<p class="card-alt"><a href="/pricing">or see pricing {@html chevronRightIcon}</a></p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
@@ -230,10 +230,20 @@
|
||||
gap: 0.6rem;
|
||||
}
|
||||
.hero-ticks li::before {
|
||||
content: "✓";
|
||||
color: #4338ca;
|
||||
font-weight: 700;
|
||||
content: "";
|
||||
}
|
||||
.hero-ticks .tick {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.15rem;
|
||||
height: 1.15rem;
|
||||
margin-right: 0.5rem;
|
||||
color: #4338ca;
|
||||
}
|
||||
.hero-ticks .tick :global(svg) {
|
||||
width: 1.1em;
|
||||
height: 1.1em;
|
||||
}
|
||||
|
||||
/* SIGNUP CARD */
|
||||
|
||||
@@ -35,7 +35,7 @@ export async function load(event) {
|
||||
s.crud.list('rewards', famId),
|
||||
s.crud.list('bonus-configs', famId),
|
||||
s.crud.list('completions', famId),
|
||||
s.settings.get(famId).catch(() => ({ simulateEow: false, webhookUrl: '' }))
|
||||
s.settings.get(famId)
|
||||
]);
|
||||
return {
|
||||
role: 'parent',
|
||||
@@ -105,8 +105,7 @@ export async function load(event) {
|
||||
tallies: {} as Record<string, unknown>,
|
||||
payday: chores.payday,
|
||||
paydayTime: chores.paydayTime || '18:00',
|
||||
timezone: chores.timezone || 'auto',
|
||||
simulateEow: !!chores.simulateEow
|
||||
timezone: chores.timezone || 'auto'
|
||||
};
|
||||
} catch {
|
||||
return empty;
|
||||
@@ -114,33 +113,6 @@ export async function load(event) {
|
||||
}
|
||||
|
||||
export const actions = {
|
||||
setEow: async (event) => {
|
||||
if (!event.locals.user) throw redirect(303, '/login');
|
||||
const famId = event.locals.user.famId;
|
||||
const fd = await event.request.formData();
|
||||
const on = fd.get('on') === 'true';
|
||||
try {
|
||||
const s = createServices(pbUser(event), event.locals.user);
|
||||
const result = await s.settings.update(famId, { simulateEow: on });
|
||||
return { simulateEow: result.simulateEow };
|
||||
} catch (e) {
|
||||
return { error: e instanceof Error ? e.message : 'Failed to update settings' };
|
||||
}
|
||||
},
|
||||
|
||||
previewEow: async (event) => {
|
||||
if (!event.locals.user) throw redirect(303, '/login');
|
||||
const famId = event.locals.user.famId;
|
||||
try {
|
||||
const s = createServices(pbUser(event), event.locals.user);
|
||||
const preview = await s.fam.eowPreview(famId);
|
||||
await s.settings.update(famId, { simulateEow: true });
|
||||
return { preview, simulateEow: true };
|
||||
} catch (e) {
|
||||
return { error: e instanceof Error ? e.message : 'Failed to preview payday' };
|
||||
}
|
||||
},
|
||||
|
||||
claim: async (event) => {
|
||||
if (!event.locals.user) throw redirect(303, '/login');
|
||||
const famId = event.locals.user.famId;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
import { famStore } from '$lib/stores/fam.svelte';
|
||||
import { ViewHeader, CardGrid, Card } from '$lib/components';
|
||||
import TemplateIcon from '$lib/components/TemplateIcon.svelte';
|
||||
import { choresIcon, todoIcon } from '$lib/components/icons';
|
||||
import { choresIcon, todoIcon, checkCircleIcon } from '$lib/components/icons';
|
||||
import { formatHumanDate } from '$lib/format';
|
||||
import { TEMPLATE_COLORS, ICON_NAMES, accentBg, outlineColor } from '$lib/templateIcons';
|
||||
import type { ChoreTemplate, AssignedChore, Member, Season, Completion } from '$lib/types';
|
||||
@@ -48,8 +48,26 @@
|
||||
|
||||
// Curated celebration emojis for todos
|
||||
const EMOJI_CHOICES = [
|
||||
'🎉', '⭐', '🏆', '💪', '🧹', '📚', '🦸', '✨', '🌟', '🥇',
|
||||
'🎯', '💚', '🚀', '🦄', '🌈', '🍀', '🎨', '🎸', '⚽', '🍰'
|
||||
'🎉',
|
||||
'⭐',
|
||||
'🏆',
|
||||
'💪',
|
||||
'🧹',
|
||||
'📚',
|
||||
'🦸',
|
||||
'✨',
|
||||
'🌟',
|
||||
'🥇',
|
||||
'🎯',
|
||||
'💚',
|
||||
'🚀',
|
||||
'🦄',
|
||||
'🌈',
|
||||
'🍀',
|
||||
'🎨',
|
||||
'🎸',
|
||||
'⚽',
|
||||
'🍰'
|
||||
];
|
||||
|
||||
let editFreq = $state('daily');
|
||||
@@ -68,9 +86,11 @@
|
||||
|
||||
// Platform-owned templates only — families assign these, they don't create.
|
||||
let templates = $derived(
|
||||
((famStore.initialized ? (famStore.templates as ChoreTemplate[]) : (data.templates as ChoreTemplate[])) || []).filter(
|
||||
(t: ChoreTemplate) => t.global
|
||||
)
|
||||
(
|
||||
(famStore.initialized
|
||||
? (famStore.templates as ChoreTemplate[])
|
||||
: (data.templates as ChoreTemplate[])) || []
|
||||
).filter((t: ChoreTemplate) => t.global)
|
||||
);
|
||||
let members = $state(
|
||||
famStore.initialized ? (famStore.members as Member[]) : (data.members as Member[]) || []
|
||||
@@ -85,7 +105,7 @@
|
||||
// Only active seasons are selectable for assignment / filtering.
|
||||
let activeSeasons = $derived((seasons || []).filter((s) => s.active !== false));
|
||||
let completions = $state((data.completions as Completion[]) || []);
|
||||
|
||||
// $effect(() => console.log(seasons, activeSeasons));
|
||||
let filteredAssigned = $derived(
|
||||
seasonFilter === 'all' ? assigned : assigned.filter((a) => a.seasonIds?.includes(seasonFilter))
|
||||
);
|
||||
@@ -366,7 +386,10 @@
|
||||
</h3>
|
||||
|
||||
<!-- TODOS accordion wrapper -->
|
||||
<div class="accordion-wrapper accordion-todos" class:open={accordionState[m.id]?.todos ?? true}>
|
||||
<div
|
||||
class="accordion-wrapper accordion-todos"
|
||||
class:open={accordionState[m.id]?.todos ?? true}
|
||||
>
|
||||
<button class="accordion-head" onclick={() => toggleAccordion(m.id, 'todos')}>
|
||||
<span class="accordion-title">
|
||||
<span class="accordion-icon">{@html todoIcon}</span>
|
||||
@@ -376,7 +399,9 @@
|
||||
>{@html (accordionState[m.id]?.todos ?? true) ? CHEVRON_UP : CHEVRON_DOWN}</span
|
||||
>
|
||||
</button>
|
||||
<span class="accordion-count" style="background:{m.color}">{todosForMember(m.id).length}</span>
|
||||
<span class="accordion-count" style="background:{m.color}"
|
||||
>{todosForMember(m.id).length}</span
|
||||
>
|
||||
{#if accordionState[m.id]?.todos ?? true}
|
||||
<div class="accordion-body">
|
||||
{#each sortedTodosForMember(m.id) as a}
|
||||
@@ -414,7 +439,8 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if completed}
|
||||
<span class="todo-completed-badge">✅ TBC completed</span>
|
||||
<span class="todo-completed-badge">{@html checkCircleIcon} TBC completed</span
|
||||
>
|
||||
{:else}
|
||||
<button
|
||||
class="del-btn"
|
||||
@@ -440,7 +466,10 @@
|
||||
</div>
|
||||
|
||||
<!-- CHORES accordion wrapper -->
|
||||
<div class="accordion-wrapper accordion-chores" class:open={accordionState[m.id]?.chores ?? true}>
|
||||
<div
|
||||
class="accordion-wrapper accordion-chores"
|
||||
class:open={accordionState[m.id]?.chores ?? true}
|
||||
>
|
||||
<button class="accordion-head" onclick={() => toggleAccordion(m.id, 'chores')}>
|
||||
<span class="accordion-title">
|
||||
<span class="accordion-icon">{@html choresIcon}</span>
|
||||
@@ -450,7 +479,9 @@
|
||||
>{@html (accordionState[m.id]?.chores ?? true) ? CHEVRON_UP : CHEVRON_DOWN}</span
|
||||
>
|
||||
</button>
|
||||
<span class="accordion-count" style="background:{m.color}">{assignedForMember(m.id).length}</span>
|
||||
<span class="accordion-count" style="background:{m.color}"
|
||||
>{assignedForMember(m.id).length}</span
|
||||
>
|
||||
{#if accordionState[m.id]?.chores ?? true}
|
||||
<div class="accordion-body">
|
||||
{#each assignedForMember(m.id) as a}
|
||||
@@ -460,14 +491,20 @@
|
||||
onclick={() => openEdit(a)}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
style="border-left-color:{outlineColor(a.color)}; background:{accentBg(a.color)}"
|
||||
style="border-left-color:{outlineColor(a.color)}; background:{accentBg(
|
||||
a.color
|
||||
)}"
|
||||
>
|
||||
<div class="card-body">
|
||||
<TemplateIcon name={a.icon} size={16} color={outlineColor(a.color)} />
|
||||
<strong>{a.customName || tName}</strong>
|
||||
<span class="badge">{a.frequency}</span>
|
||||
{#each a.seasonIds || [] as sid}
|
||||
<span class="season-dot" title={seasonName(sid)} style="background:{seasonColor(sid)}"></span>
|
||||
<span
|
||||
class="season-dot"
|
||||
title={seasonName(sid)}
|
||||
style="background:{seasonColor(sid)}"
|
||||
></span>
|
||||
{/each}
|
||||
{#if (a.seasonIds || []).length === 0}
|
||||
<span class="badge season muted">All year</span>
|
||||
@@ -509,14 +546,16 @@
|
||||
</CardGrid>
|
||||
|
||||
<!-- Season pill selector (reused by assign + edit modals) -->
|
||||
{#snippet seasonPills(list: Season[], selected: string[], set: (v: string[]) => void)}
|
||||
<div class="season-pills">
|
||||
{#snippet seasonPills(list: Season[] = [], selected: string[] = [], set: (v: string[]) => void)}
|
||||
<div class="season-pills" role="radiogroup" aria-label="Seasons">
|
||||
<button
|
||||
type="button"
|
||||
class="season-pill"
|
||||
class:selected={selected.length === 0}
|
||||
style="--pill:#9ca3af"
|
||||
onclick={() => set([])}>All year</button
|
||||
role="radio"
|
||||
aria-checked={selected.length === 0}
|
||||
onclick={() => set([])}>Any</button
|
||||
>
|
||||
{#each list as s}
|
||||
<button
|
||||
@@ -524,6 +563,8 @@
|
||||
class="season-pill"
|
||||
class:selected={selected[0] === s.id}
|
||||
style="--pill:{s.color}"
|
||||
role="radio"
|
||||
aria-checked={selected[0] === s.id}
|
||||
onclick={() => set([s.id])}>{s.name}</button
|
||||
>
|
||||
{/each}
|
||||
@@ -532,16 +573,25 @@
|
||||
|
||||
<!-- Assign from Template Modal -->
|
||||
{#if assignModal}
|
||||
<div class="overlay" onclick={(e) => { if (e.target === e.currentTarget) assignModal = null; }} role="presentation">
|
||||
<div
|
||||
class="overlay"
|
||||
onclick={(e) => {
|
||||
if (e.target === e.currentTarget) assignModal = null;
|
||||
}}
|
||||
role="presentation"
|
||||
>
|
||||
<div class="modal" onclick={(e) => e.stopPropagation()} role="dialog">
|
||||
<h3>Assign: {assignModal.t.name}</h3>
|
||||
<p class="hint">
|
||||
Copies this platform template into {members.find((m) => m.id === assignModal?.memberId)?.name}'s
|
||||
chores. Amend, then Assign.
|
||||
Copies this platform template into {members.find((m) => m.id === assignModal?.memberId)
|
||||
?.name}'s chores. Amend, then Assign.
|
||||
</p>
|
||||
<label>Name <input bind:value={assignName} placeholder="Task name" /></label>
|
||||
<label
|
||||
>Description <input bind:value={assignDescription} placeholder="Optional description" /></label
|
||||
>Description <input
|
||||
bind:value={assignDescription}
|
||||
placeholder="Optional description"
|
||||
/></label
|
||||
>
|
||||
<div class="color-field">
|
||||
<span class="field-label">Colour</span>
|
||||
@@ -556,7 +606,8 @@
|
||||
? '#e5e7eb'
|
||||
: c.value}"
|
||||
title={c.label}
|
||||
onclick={() => (assignColor = c.value)}></button>
|
||||
onclick={() => (assignColor = c.value)}
|
||||
></button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -589,7 +640,7 @@
|
||||
</label>
|
||||
<div class="season-field">
|
||||
<span class="field-label">Seasons</span>
|
||||
<seasonPills activeSeasons selected={assignSeasonIds} set={(v: string[]) => (assignSeasonIds = v)} />
|
||||
{@render seasonPills(seasons, assignSeasonIds, (v: string[]) => (assignSeasonIds = v))}
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button type="button" onclick={() => (assignModal = null)}>Cancel</button>
|
||||
@@ -601,7 +652,13 @@
|
||||
|
||||
<!-- Edit Assignment Modal -->
|
||||
{#if showEditModal && editingAssignment}
|
||||
<div class="overlay" onclick={(e) => { if (e.target === e.currentTarget) closeEdit(); }} role="presentation">
|
||||
<div
|
||||
class="overlay"
|
||||
onclick={(e) => {
|
||||
if (e.target === e.currentTarget) closeEdit();
|
||||
}}
|
||||
role="presentation"
|
||||
>
|
||||
<div class="modal" onclick={(e) => e.stopPropagation()} role="dialog">
|
||||
<h3>
|
||||
{#if editingAssignment.isTodo}
|
||||
@@ -619,7 +676,11 @@
|
||||
</label>
|
||||
<label
|
||||
>Description
|
||||
<input name="description" bind:value={editDescription} placeholder="Optional description" /></label
|
||||
<input
|
||||
name="description"
|
||||
bind:value={editDescription}
|
||||
placeholder="Optional description"
|
||||
/></label
|
||||
>
|
||||
|
||||
{#if editingAssignment.isTodo}
|
||||
@@ -639,7 +700,14 @@
|
||||
{:else if editType === 'money'}
|
||||
<label>
|
||||
Cash amount (£)
|
||||
<input name="value" type="number" step="0.01" min="0.01" bind:value={editValue} required />
|
||||
<input
|
||||
name="value"
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0.01"
|
||||
bind:value={editValue}
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
{:else}
|
||||
<input type="hidden" name="emoji" value={editEmoji} />
|
||||
@@ -648,7 +716,8 @@
|
||||
<button
|
||||
type="button"
|
||||
class="link-btn"
|
||||
onclick={() => (showTodoEmojiPicker = !showTodoEmojiPicker)}>
|
||||
onclick={() => (showTodoEmojiPicker = !showTodoEmojiPicker)}
|
||||
>
|
||||
{showTodoEmojiPicker ? 'Hide' : 'Choose'}
|
||||
</button>
|
||||
</div>
|
||||
@@ -691,20 +760,21 @@
|
||||
? '#e5e7eb'
|
||||
: c.value}"
|
||||
title={c.label}
|
||||
onclick={() => (editColor = c.value)}></button>
|
||||
onclick={() => (editColor = c.value)}
|
||||
></button>
|
||||
{/each}
|
||||
</div>
|
||||
<input type="hidden" name="color" value={editColor} />
|
||||
</div>
|
||||
<label
|
||||
><span class="icon-preview"
|
||||
><TemplateIcon name={editIcon} size={22} color={outlineColor(editColor)} /></span
|
||||
>
|
||||
<select name="icon" bind:value={editIcon}>
|
||||
<option value="">None</option>
|
||||
{#each ICON_NAMES as n}<option value={n}>{n}</option>{/each}
|
||||
</select>
|
||||
</label>
|
||||
<label class="icon-row"
|
||||
><span class="icon-preview"
|
||||
><TemplateIcon name={editIcon} size={22} color={outlineColor(editColor)} /></span
|
||||
>
|
||||
<select name="icon" bind:value={editIcon}>
|
||||
<option value="">None</option>
|
||||
{#each ICON_NAMES as n}<option value={n}>{n}</option>{/each}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Frequency
|
||||
<select name="frequency" bind:value={editFreq}>
|
||||
@@ -725,7 +795,7 @@
|
||||
</label>
|
||||
<div class="season-field">
|
||||
<span class="field-label">Seasons</span>
|
||||
<seasonPills activeSeasons selected={editSeasonIds} set={(v: string[]) => (editSeasonIds = v)} />
|
||||
{@render seasonPills(seasons, editSeasonIds, (v: string[]) => (editSeasonIds = v))}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="modal-actions">
|
||||
@@ -739,7 +809,13 @@
|
||||
|
||||
<!-- Add Todo Modal -->
|
||||
{#if showTodoModal && todoMemberId}
|
||||
<div class="overlay" onclick={(e) => { if (e.target === e.currentTarget) showTodoModal = false; }} role="presentation">
|
||||
<div
|
||||
class="overlay"
|
||||
onclick={(e) => {
|
||||
if (e.target === e.currentTarget) showTodoModal = false;
|
||||
}}
|
||||
role="presentation"
|
||||
>
|
||||
<div class="modal" onclick={(e) => e.stopPropagation()} role="dialog">
|
||||
<h3>Add Todo for {members.find((m) => m.id === todoMemberId)?.name || 'Member'}</h3>
|
||||
<form
|
||||
@@ -785,13 +861,24 @@
|
||||
{:else if todoType === 'money'}
|
||||
<label>
|
||||
Cash amount (£)
|
||||
<input name="value" type="number" step="0.01" min="0.01" bind:value={todoValue} required />
|
||||
<input
|
||||
name="value"
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0.01"
|
||||
bind:value={todoValue}
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
{:else}
|
||||
<input type="hidden" name="emoji" value={todoEmoji} />
|
||||
<div class="emoji-field">
|
||||
<span class="emoji-preview">{todoEmoji}</span>
|
||||
<button type="button" class="link-btn" onclick={() => (showTodoEmojiPicker = !showTodoEmojiPicker)}>
|
||||
<button
|
||||
type="button"
|
||||
class="link-btn"
|
||||
onclick={() => (showTodoEmojiPicker = !showTodoEmojiPicker)}
|
||||
>
|
||||
{showTodoEmojiPicker ? 'Hide' : 'Choose'}
|
||||
</button>
|
||||
</div>
|
||||
@@ -1464,6 +1551,13 @@
|
||||
border-radius: 4px;
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
.todo-completed-badge :global(svg) {
|
||||
width: 0.95em;
|
||||
height: 0.95em;
|
||||
}
|
||||
|
||||
/* ── Points badge ── */
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { page } from '$app/state';
|
||||
import { enhance } from '$app/forms';
|
||||
import { Button } from '$lib/components';
|
||||
import { homeIcon } from '$lib/components/icons';
|
||||
|
||||
const famSlug = page.params.fam;
|
||||
let name = $state('');
|
||||
@@ -14,7 +15,7 @@
|
||||
<main class="mx-auto flex min-h-screen max-w-md flex-col items-center justify-center px-6">
|
||||
<section class="w-full rounded-2xl border border-slate-200 bg-white p-8 text-center shadow-sm">
|
||||
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-indigo-100 text-2xl">
|
||||
🏠
|
||||
<span class="home-badge">{@html homeIcon}</span>
|
||||
</div>
|
||||
<h1 class="text-xl font-bold text-slate-900">Join {famSlug}</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">
|
||||
@@ -63,3 +64,10 @@
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.home-badge :global(svg) {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { page } from '$app/state';
|
||||
import { enhance } from '$app/forms';
|
||||
import { Button } from '$lib/components';
|
||||
import { homeIcon } from '$lib/components/icons';
|
||||
|
||||
const famSlug = page.params.fam;
|
||||
const username = page.params.username;
|
||||
@@ -14,7 +15,7 @@
|
||||
<main class="mx-auto flex min-h-screen max-w-md flex-col items-center justify-center px-6">
|
||||
<section class="w-full rounded-2xl border border-slate-200 bg-white p-8 text-center shadow-sm">
|
||||
<div class="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-indigo-100 text-2xl">
|
||||
🏠
|
||||
<span class="home-badge">{@html homeIcon}</span>
|
||||
</div>
|
||||
<h1 class="text-xl font-bold text-slate-900">Welcome to {famSlug}!</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">
|
||||
@@ -54,4 +55,10 @@
|
||||
Code is valid for 20 minutes. Ask your parent for a new one if it expires.
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
</main>
|
||||
<style>
|
||||
.home-badge :global(svg) {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import TemplateIcon from '$lib/components/TemplateIcon.svelte';
|
||||
import { ICON_NAMES, TEMPLATE_COLORS } from '$lib/templateIcons';
|
||||
import Footer from '$lib/components/Footer.svelte';
|
||||
import { homeIcon, chevronRightIcon } from '$lib/components/icons';
|
||||
|
||||
let { data, form } = $props();
|
||||
|
||||
@@ -85,7 +86,7 @@
|
||||
|
||||
<div class="shell">
|
||||
<header class="topbar">
|
||||
<a class="brand" href="/"><span class="brand-icon">🏠</span> <strong>FamChore</strong></a>
|
||||
<a class="brand" href="/"><span class="brand-icon">{@html homeIcon}</span> <strong>FamChore</strong></a>
|
||||
<nav>
|
||||
{#if data.authenticated}
|
||||
<form method="POST" action="?/logout" use:enhance>
|
||||
@@ -311,7 +312,7 @@
|
||||
<span class="badge none">None</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="row-actions" data-label="Actions"><a href="/{fam.slug}" class="link">View →</a></td>
|
||||
<td class="row-actions" data-label="Actions"><a href="/{fam.slug}" class="link">View {@html chevronRightIcon}</a></td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { loadStripe, type StripeEmbeddedCheckout } from '@stripe/stripe-js';
|
||||
import { Button, PricingPlans } from '$lib/components';
|
||||
import Footer from '$lib/components/Footer.svelte';
|
||||
import { homeIcon, chevronLeftIcon, chevronRightIcon } from '$lib/components/icons';
|
||||
|
||||
let { data, form } = $props();
|
||||
|
||||
@@ -58,7 +59,7 @@
|
||||
|
||||
<div class="shell">
|
||||
<header class="topbar">
|
||||
<a class="brand" href="/"><span class="brand-icon">🏠</span> <strong>FamChore</strong></a>
|
||||
<a class="brand" href="/"><span class="brand-icon">{@html homeIcon}</span> <strong>FamChore</strong></a>
|
||||
<nav>
|
||||
<a href="/login" class="nav-link">Log in</a>
|
||||
<a href="/signup" class="nav-cta">Get started</a>
|
||||
@@ -77,13 +78,13 @@
|
||||
<div class="checkout-card">
|
||||
<div class="checkout-head">
|
||||
<h2>Checkout — {checkoutTitle}</h2>
|
||||
<Button variant="ghost" size="sm" onclick={resetCheckout}>← Back to plans</Button>
|
||||
<Button variant="ghost" size="sm" onclick={resetCheckout}>{@html chevronLeftIcon} Back to plans</Button>
|
||||
</div>
|
||||
<div bind:this={checkoutEl} class="checkout-host"></div>
|
||||
<p class="hint">
|
||||
You can close and go to your dashboard any time — access unlocks once payment completes.
|
||||
</p>
|
||||
<a href={data.famSlug ? `/${data.famSlug}` : '/'} class="dash-link">Go to dashboard →</a>
|
||||
<a href={data.famSlug ? `/${data.famSlug}` : '/'} class="dash-link">Go to dashboard {@html chevronRightIcon}</a>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user