remove payday debug and add multiple ui fixes

This commit is contained in:
JCEEE
2026-08-28 19:32:32 +01:00
parent bcdd3805d5
commit d303eda038
19 changed files with 705 additions and 697 deletions
@@ -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 ── */