{#if role === 'parent'} {#if toast}
{toast}
{/if}
{adminTiles.members} members
{adminTiles.pts} points this week
£{adminTiles.money.toFixed(2)} cash this week
{adminTiles.chores} chores done
{#each parentMembers as m} {@const total = totalChoresFor(m.id)} {@const todays = todayCompletionsFor(m.id)} {@const done = todays.length} {@const pct = total > 0 ? Math.round((done / total) * 100) : 0} {@const s = memberInSummary(m.id)}
{m.name} Kanban
Points: {s?.pointsEarned ?? 0} Money: £{(s?.moneyEarned ?? 0).toFixed(2)}
Today:
{done}/{total}
{#if done > 0}
    {#each todays as c}
  • ✅ {choreNameFor(c.assignedChoreId)}
    { return async (args) => handleResult(args); }} class="revoke-form" >
  • {/each}
{/if}
{/each}
{#if manualConfigs().length === 0}

No manual bonus configs.

{:else} {#each manualConfigs() as bc} {@const val = bc.rewardType === 'cash' ? `£${Number(bc.rewardValue).toFixed(2)}` : bc.rewardType === 'points' ? `${bc.rewardValue} pts` : bc.rewardValue} {@const targeted = bc.memberId ? parentMembers.find((m: any) => m.id === bc.memberId) : null}
{bc.name} {val}
{#if targeted}
{ return async (args: any) => handleResult(args); }} >
■ {targeted.name}
{:else}
{ return async (args: any) => handleResult(args); }} >
{/if}
{/each} {/if}
{#if claimableRewards().length === 0}

No outstanding claims

{:else} {#each parentMembers as m} {@const requested = memberRequested(m.id)} {@const outstanding = memberOutstanding(m.id)} {@const claimable = memberClaimable(m.id)} {@const cashClaimable = claimable.filter((r: any) => r.rewardType === 'cash')} {@const cashRequested = requested.filter((r: any) => r.rewardType === 'cash')} {@const requestedTotal = cashRequested.reduce( (sum: number, r: any) => sum + Number(r.value), 0 )} {@const total = cashClaimable.reduce((sum: number, r: any) => sum + Number(r.value), 0)} {@const hasClaims = requested.length > 0 || outstanding.length > 0} {#if hasClaims}

{m.name}

{#if total > 0}

£{total.toFixed(2)} owed

{/if} {#if cashRequested.length > 0}
{ return async (args: any) => handleResult(args); }} >
{/if} {#if requested.length > 0} {#each requested as r}
{ return async (args: any) => handleResult(args); }} >
{r.label}
{rewardLabel(r)}
{/each} {/if} {#if outstanding.length > 0} {#each outstanding as r}
{r.label} {rewardLabel(r)} {#if paydayLocked(r)} 🔒 {formatShortDate(r.settleDate)} {/if}
{/each} {/if}

Response:

{#each responseTags as tag} {/each}
{ if (customMessage[m.id]) selectedMessage[m.id] = customMessage[m.id]; }} />
{/if} {/each} {/if}

Read-only preview of what the payday rollover will settle for this week. Nothing here is written.

{ return async ({ result }) => { const d = (result as any).data || {}; if (d.error) toast = d.error; else { eowPreview = d.preview; simulateEow = !!d.simulateEow; } }; }} >
{#if simulateEow}

👀 Preview mode is ON — child dashboards show a "payday preview" notice.

{ return async ({ result }) => { const d = (result as any).data || {}; if (d.error) toast = d.error; else simulateEow = !!d.simulateEow; }; }} >
{/if} {#if eowPreview}

Week {formatDDMMYY(eowPreview.weekStart)} → {formatDDMMYY(eowPreview.weekEnd)}

On payday these {eowPreview.completionsThisWeek} completions reset; week starts anew at {formatDDMMYY(eowPreview.nextWeekStart)}.

{#each eowPreview.summaries as s}
{s.memberName} {s.pointsEarned} pts £{Number(s.moneyEarned || 0).toFixed(2)} {s.choresCompleted} chores
{/each}
{#if eowPreview.predictedRewards.length}

Rewards that would be auto-created

{#each eowPreview.predictedRewards as r} {r.memberName} · {r.config} · {r.type === 'cash' ? '£' + Number(r.value).toFixed(2) : r.value + ' pts'} {/each}
{:else}

No new rewards would be auto-created this week.

{/if}
{/if}
{:else} {#if loading}

Loading...

{:else if error}

{error}

{:else} {#if simulateEow}
👀 Payday preview — your parent is checking this week's payday. Nothing is paid out yet.
{/if} {#if owedCash > 0}
🎉 You've earned £{owedCash.toFixed(2)} — go get it from your parent!
{/if} {#if showCountdown}
💰

Payday is today!

{#if countdownHours > 0} {countdownHours}h {countdownMinutes}m left {:else} {countdownMinutes}m {countdownSecs}s left {/if}

Your earnings get settled at {paydayTime}

{/if}
{ e.stopPropagation(); showColorPicker = !showColorPicker; }} onkeydown={(e) => e.key === 'Enter' && (showColorPicker = !showColorPicker)} > {#if editingName} { if (e.key === 'Enter') { editingName = false; if (nameInput && nameInput !== memberName) { const old = memberName; memberName = nameInput; try { const res = await fetch('/api/members/me', { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'x-device-token': deviceToken, 'x-device-famid': famId }, body: JSON.stringify({ name: nameInput }) }); if (!res.ok) memberName = old; } catch { memberName = old; } } } else if (e.key === 'Escape') { editingName = false; nameInput = memberName; } }} onblur={() => { editingName = false; nameInput = memberName; }} autofocus /> {:else} { nameInput = memberName; editingName = true; }} onkeydown={(e) => e.key === 'Enter' && ((nameInput = memberName), (editingName = true))} > {memberName || username} {/if}
{headDay()} {headMonth()}
{#if showColorPicker}
(showColorPicker = false)}>
e.stopPropagation()}>

Pick a color:

{#each ['#ef4444', '#f97316', '#eab308', '#22c55e', '#14b8a6', '#3b82f6', '#6366f1', '#a855f7', '#ec4899', '#78716c'] as color} {/each}
{/if}
{weeklyTotal > 0 ? weeklyPct : 0}%
{weeklyTotal > 0 ? `${weeklyDone}/${weeklyTotal} done` : 'no chores yet'}
{#if weekBonusTallies.length > 0}
{#each weekBonusTallies as t} 🎉 {t.name} ×{t.count} {/each}
{:else} no bonuses earned yet {/if}
{daysLeft} days until payday
£{weekChoreCash.toFixed(2)} cash this week
{weekChorePoints} points this week
{#if thresholdGoals.length > 0}
{#each thresholdGoals as goal} {@const pct = goal.criteriaValue > 0 ? Math.min(100, Math.round((goal.current / goal.criteriaValue) * 100)) : 0}
{goal.config.name} {#if goal.config.rewardType === 'cash'} £{Number(goal.config.rewardValue).toFixed(2)} {:else if goal.config.rewardType === 'prize'} 🎁 {:else} {goal.config.rewardValue} pts {/if}
{Math.min(goal.current, goal.criteriaValue)} / {goal.criteriaValue} {#if goal.achieved} ✅ earned {:else} {pct}% {/if}
{/each}
{/if}

🎯 Daily ({dailyPending.length})

{#if dailyPending.length === 0}

All done!

{:else} {#each dailyPending as chore} {/each} {/if}

📅 Weekly ({weeklyPending.length + memberTodos.filter((t) => !isCompleted(t.id, todayChild)).length})

{#if weeklyPending.length === 0 && memberTodos.filter((t) => !isCompleted(t.id, todayChild)).length === 0}

All done!

{:else} {#each weeklyPending as chore} {/each} {#each memberTodos.filter((t) => !isCompleted(t.id, todayChild)) as todo} {@const urgency = todoUrgency(todo)} {#if todo.type === 'emoji'}
📋 {todo.customName || 'Todo'} todo 🎯
{:else} {/if} {/each} {/if}

✅ Done ({completedToday.length})

{#if completedToday.length === 0}

Nothing yet

{:else} {#each completedToday as c} {@const chore = assigned.find((a) => a.id === c.assignedChoreId)} {#if chore} {/if} {/each} {/if}
💰 Wallet {#if allTimeCash > 0 || allTimePoints > 0} £{allTimeCash.toFixed(2)} · {allTimePoints} pts {/if}
{#if pendingRewards.length === 0}

No rewards waiting

{:else}
{#each pendingRewards as r} {@const isReq = r.status === 'requested'}
{r.label} {#if r.rewardType === 'cash'} £{Number(r.value).toFixed(2)} {:else if r.rewardType === 'prize'} 🎁 {:else} {r.value} pts {/if} {#if r.rewardType === 'points'} ✓ credited {:else if isReq} ⏳ waiting {:else if paydayLocked(r)} 🔒 pays out {formatShortDate(r.settleDate)} {:else} {/if}
{/each}
{/if}
{/if} {/if}