enable kids todos

This commit is contained in:
JCEEE
2026-09-24 12:50:20 +01:00
parent 2a3f3836c0
commit 8388868bee
5 changed files with 218 additions and 9 deletions
+10 -8
View File
@@ -694,7 +694,7 @@
border: 1px solid #e5e7eb;
border-top: 4px solid #6366f1;
border-radius: 12px;
padding: 0.75rem;
padding: 0.9rem;
background: #fafbff;
}
.col-member {
@@ -709,6 +709,7 @@
gap: 0.4rem;
flex: 1;
min-width: 0;
margin-bottom: 0.35rem;
}
.dot {
display: inline-block;
@@ -741,8 +742,8 @@
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 0.6rem;
margin: 0.9rem 0 0.7rem;
gap: 0.7rem;
margin: 1.1rem 0 1.2rem;
}
.donut-wrap {
position: relative;
@@ -780,19 +781,20 @@
.member-stats {
display: flex;
flex-direction: column;
gap: 0.3rem;
gap: 0.45rem;
margin-bottom: 0.5rem;
}
.member-stats.compact .mstat {
display: flex;
align-items: baseline;
gap: 0.4rem;
font-size: 0.76rem;
gap: 0.5rem;
font-size: 0.8rem;
line-height: 1.5;
color: #374151;
background: rgba(255, 255, 255, 0.65);
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 0.28rem 0.5rem;
border-radius: 10px;
padding: 0.45rem 0.65rem;
white-space: nowrap;
overflow: hidden;
}
@@ -266,6 +266,29 @@
let todayChild = $derived(todayInTz(famTz));
let togglingIds = $state<Set<string>>(new Set());
// ─── Kid-created todos ───
let showTodoModal = $state(false);
let newTodoName = $state('');
let todoSaving = $state(false);
let todoError = $state('');
async function submitTodo() {
const name = newTodoName.trim();
if (!name || todoSaving) return;
todoSaving = true;
todoError = '';
try {
const res = await memberApi.createTodo(name);
// Instant UI — the SSE echo dedupes on id via applyRecord.
famStore.applyRecord('assigned_chores', res.record, 'create');
newTodoName = '';
showTodoModal = false;
} catch (e) {
todoError = e instanceof Error ? e.message : 'Could not add todo';
} finally {
todoSaving = false;
}
}
// ─── Payday countdown (child view) ───
let nowMs = $state(Date.now());
let eowFired = $state(false);
@@ -1765,6 +1788,9 @@
{/if}
{/each}
{/if}
<button class="add-todo-cta" onclick={() => (showTodoModal = true)}>
<span aria-hidden="true">+</span> Add todo
</button>
</div>
<div class="column col-done">
<h2>{@html checkCircleIcon} Done ({completedToday.length})</h2>
@@ -1799,6 +1825,45 @@
{/if}
<!-- WALLET / CLAIMS -->
{#if showTodoModal}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="modal-overlay"
onclick={() => !todoSaving && (showTodoModal = false)}
onkeydown={(e) => e.key === 'Escape' && !todoSaving && (showTodoModal = false)}
>
<div class="modal-card" onclick={(e) => e.stopPropagation()}>
<h3>🎯 New todo</h3>
<p class="modal-sub">Something you want to get done this week? Add it here.</p>
<input
class="modal-input"
type="text"
maxlength="80"
placeholder="e.g. Tidy my desk"
bind:value={newTodoName}
disabled={todoSaving}
autofocus
onkeydown={(e) => e.key === 'Enter' && submitTodo()}
/>
{#if todoError}
<p class="modal-error">{todoError}</p>
{/if}
<div class="modal-actions">
<button
class="modal-btn modal-cancel"
onclick={() => (showTodoModal = false)}
disabled={todoSaving}>Cancel</button
>
<button
class="modal-btn modal-add"
onclick={submitTodo}
disabled={!newTodoName.trim() || todoSaving}
>{todoSaving ? 'Adding…' : 'Add todo'}</button
>
</div>
</div>
</div>
{/if}
<div class="wallet">
<div class="wallet-top">
<span class="wallet-lbl">{@html walletIcon} Wallet</span>
@@ -3001,6 +3066,93 @@
padding: 1px 5px;
flex-shrink: 0;
}
.add-todo-cta {
width: 100%;
margin-top: 0.5rem;
padding: 0.55rem;
border: 1.5px dashed #c7d2fe;
border-radius: 10px;
background: #eef2ff;
color: #4338ca;
font-size: 0.82rem;
font-weight: 700;
cursor: pointer;
}
.add-todo-cta:hover {
background: #e0e7ff;
border-color: #818cf8;
}
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
z-index: 1000;
}
.modal-card {
background: #fff;
border-radius: 14px;
padding: 1.25rem;
width: 100%;
max-width: 380px;
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.25);
}
.modal-card h3 {
margin: 0 0 0.25rem;
font-size: 1.1rem;
}
.modal-sub {
margin: 0 0 0.85rem;
font-size: 0.85rem;
color: #6b7280;
}
.modal-input {
width: 100%;
box-sizing: border-box;
border: 1.5px solid #d1d5db;
border-radius: 10px;
padding: 0.6rem 0.75rem;
font-size: 0.95rem;
}
.modal-input:focus {
outline: none;
border-color: #6366f1;
box-shadow: 0 0 0 3px #e0e7ff;
}
.modal-error {
margin: 0.5rem 0 0;
font-size: 0.8rem;
color: #dc2626;
}
.modal-actions {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
margin-top: 1rem;
}
.modal-btn {
border: none;
border-radius: 10px;
padding: 0.55rem 1rem;
font-size: 0.85rem;
font-weight: 700;
cursor: pointer;
}
.modal-btn:disabled {
opacity: 0.5;
cursor: default;
}
.modal-cancel {
background: #f3f4f6;
color: #374151;
}
.modal-add {
background: #4338ca;
color: #fff;
}
.todo-main {
display: flex;
flex-direction: column;