payday tests

This commit is contained in:
JCEEE
2026-09-20 17:49:14 +01:00
parent 9e6d835061
commit 18511b6546
2 changed files with 58 additions and 12 deletions
+1
View File
@@ -6,6 +6,7 @@ export { default as ViewHeader } from './ViewHeader.svelte';
export { default as Card } from './Card.svelte'; export { default as Card } from './Card.svelte';
export { default as CardGrid } from './CardGrid.svelte'; export { default as CardGrid } from './CardGrid.svelte';
export { default as Button } from './Button.svelte'; export { default as Button } from './Button.svelte';
export { default as SaveButton } from './SaveButton.svelte';
export { default as Accordion } from './Accordion.svelte'; export { default as Accordion } from './Accordion.svelte';
export { default as AccordionItem } from './AccordionItem.svelte'; export { default as AccordionItem } from './AccordionItem.svelte';
export { default as Chat } from './Chat.svelte'; export { default as Chat } from './Chat.svelte';
@@ -9,6 +9,7 @@
CardGrid, CardGrid,
Card, Card,
Button, Button,
SaveButton,
Accordion, Accordion,
AccordionItem, AccordionItem,
NoticeDialog NoticeDialog
@@ -89,6 +90,17 @@
}); });
let qrDataUrl = $state(''); let qrDataUrl = $state('');
let copied = $state(false); let copied = $state(false);
// Save-button success feedback: each save form holds a SaveButton ref and
// flashes it green for 4s from its use:enhance callback on success.
type Flasher = { flash: () => void } | null;
let renameBtn: Flasher = $state(null);
let paydayBtn: Flasher = $state(null);
let seasonBtn: Flasher = $state(null);
let lockBtn: Flasher = $state(null);
let addMemberBtn: Flasher = $state(null);
let inviteBtn: Flasher = $state(null);
let applyCodeBtn: Flasher = $state(null);
let parentInviteName = $state(''); let parentInviteName = $state('');
let parentInviteEmail = $state(''); let parentInviteEmail = $state('');
@@ -153,6 +165,7 @@
if (result.type === 'success') { if (result.type === 'success') {
const d = result.data as { ok?: boolean; error?: string } | undefined; const d = result.data as { ok?: boolean; error?: string } | undefined;
if (d?.ok) { if (d?.ok) {
inviteBtn?.flash();
notices.success( notices.success(
'Invite sent', 'Invite sent',
`Email sent to ${parentInviteEmail} with a join link and code.` `Email sent to ${parentInviteEmail} with a join link and code.`
@@ -209,7 +222,16 @@
<code class="slug-inline">/{famSlug}</code> even if you rename it — links you've shared keep <code class="slug-inline">/{famSlug}</code> even if you rename it — links you've shared keep
working. working.
</p> </p>
<form method="POST" action="?/renameFam" use:enhance> <form
method="POST"
action="?/renameFam"
use:enhance={() => {
return async ({ result, update }) => {
await update();
if (result.type === 'success') renameBtn?.flash();
};
}}
>
<label class="field-label" for="fam-name">Display name</label> <label class="field-label" for="fam-name">Display name</label>
<input <input
id="fam-name" id="fam-name"
@@ -218,7 +240,7 @@
placeholder={fam?.name || 'Family name'} placeholder={fam?.name || 'Family name'}
required required
/> />
<Button type="submit" size="sm">Rename</Button> <SaveButton bind:this={renameBtn} label="Rename" />
</form> </form>
{#if fam?.slug} {#if fam?.slug}
<p class="hint slug-line"> <p class="hint slug-line">
@@ -234,6 +256,7 @@
use:enhance={() => { use:enhance={() => {
return async ({ result }) => { return async ({ result }) => {
if (result.type === 'error') alert(result.error || 'Failed to update payday'); if (result.type === 'error') alert(result.error || 'Failed to update payday');
else if (result.type === 'success') paydayBtn?.flash();
}; };
}} }}
class="payday-form" class="payday-form"
@@ -265,7 +288,7 @@
{/each} {/each}
</select> </select>
</div> </div>
<Button type="submit" size="sm">Set payday</Button> <SaveButton bind:this={paydayBtn} label="Set payday" />
</form> </form>
<p class="hint"> <p class="hint">
Payday: the week starts on this day and weekly earnings are settled at this time. Pick Payday: the week starts on this day and weekly earnings are settled at this time. Pick
@@ -278,7 +301,17 @@
Group chores into seasons. Tick a season on to make it available for assignment; untick to Group chores into seasons. Tick a season on to make it available for assignment; untick to
disable it. disable it.
</p> </p>
<form method="POST" action="?/createSeason" use:enhance class="season-form"> <form
method="POST"
action="?/createSeason"
use:enhance={() => {
return async ({ result, update }) => {
await update();
if (result.type === 'success') seasonBtn?.flash();
};
}}
class="season-form"
>
<label class="field-label" for="season-name">New season</label> <label class="field-label" for="season-name">New season</label>
<input id="season-name" name="name" placeholder="Season name" required /> <input id="season-name" name="name" placeholder="Season name" required />
<div class="season-color-row"> <div class="season-color-row">
@@ -297,7 +330,7 @@
</div> </div>
<input type="hidden" name="color" value={seasonColor} /> <input type="hidden" name="color" value={seasonColor} />
</div> </div>
<Button type="submit" size="sm">Add</Button> <SaveButton bind:this={seasonBtn} label="Add" />
</form> </form>
<ul class="season-list"> <ul class="season-list">
@@ -385,8 +418,10 @@
return async ({ result }) => { return async ({ result }) => {
if (result.type !== 'success') return; if (result.type !== 'success') return;
const d = result.data as any; const d = result.data as any;
if (d?.ok) lockMins = String(d.lockMins); if (d?.ok) {
else if (d?.error) alert(d.error); lockMins = String(d.lockMins);
lockBtn?.flash();
} else if (d?.error) alert(d.error);
}; };
}} }}
> >
@@ -395,7 +430,7 @@
<option value="2">After 2 minutes of no use</option> <option value="2">After 2 minutes of no use</option>
<option value="10">After 10 minutes of no use</option> <option value="10">After 10 minutes of no use</option>
</select> </select>
<Button type="submit" size="sm">Save</Button> <SaveButton bind:this={lockBtn} label="Save" />
</form> </form>
</Card> </Card>
</CardGrid> </CardGrid>
@@ -405,7 +440,16 @@
<CardGrid> <CardGrid>
<Card title="New member" cols={1}> <Card title="New member" cols={1}>
<p class="hint">Add a child. They'll pick their own colour after joining.</p> <p class="hint">Add a child. They'll pick their own colour after joining.</p>
<form method="POST" action="?/addMember" use:enhance> <form
method="POST"
action="?/addMember"
use:enhance={() => {
return async ({ result, update }) => {
await update();
if (result.type === 'success') addMemberBtn?.flash();
};
}}
>
<label class="field-label" for="new-child">New child</label> <label class="field-label" for="new-child">New child</label>
<input <input
id="new-child" id="new-child"
@@ -414,7 +458,7 @@
placeholder="Child name" placeholder="Child name"
required required
/> />
<Button type="submit" size="sm">Add child</Button> <SaveButton bind:this={addMemberBtn} label="Add child" />
</form> </form>
</Card> </Card>
@@ -631,7 +675,7 @@
placeholder="parent@example.com" placeholder="parent@example.com"
required required
/> />
<Button type="submit" size="sm">Send invite</Button> <SaveButton bind:this={inviteBtn} label="Send invite" />
</form> </form>
<p class="hint">They'll receive a link with a code to set up their own password.</p> <p class="hint">They'll receive a link with a code to set up their own password.</p>
</Card> </Card>
@@ -704,6 +748,7 @@
const d = result.data as { error?: string; ok?: boolean }; const d = result.data as { error?: string; ok?: boolean };
accessMsg = d.error || 'Code applied — access enabled.'; accessMsg = d.error || 'Code applied — access enabled.';
accessCodeInput = ''; accessCodeInput = '';
if (d.ok && !d.error) applyCodeBtn?.flash();
} }
await update(); await update();
}; };
@@ -715,7 +760,7 @@
placeholder="Enter access code" placeholder="Enter access code"
autocomplete="off" autocomplete="off"
/> />
<Button type="submit" size="sm" variant="primary">Apply code</Button> <SaveButton bind:this={applyCodeBtn} label="Apply code" />
</form> </form>
{/if} {/if}
{#if accessMsg} {#if accessMsg}