{#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 todays = todayCompletionsFor(m.id)} {@const done = todays.length} {@const s = memberInSummary(m.id)} {@const pm = parentBonusConfigs.find( (c: any) => c.isPocketMoney && c.memberId === m.id )}
{m.name} {#if !(pm && pm.rewardValue)} {/if}
Points: {s?.pointsEarned ?? 0} Money: £{(s?.moneyEarned ?? 0).toFixed(2)}
{#if pm && pm.rewardValue} Pocket money: £{Number(pm.rewardValue).toFixed(2)} · {pm.criteriaValue || 0}% of chores {:else} Pocket money: amount not set {/if}
{#if done > 0}
    {#each todays as c, i}
  • {@html checkCircleIcon} {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}
{@html checkCircleIcon}No outstanding claims
{:else} {@const familyOwed = claimableRewards() .filter((r: any) => r.rewardType === 'cash') .reduce((s: number, r: any) => s + Number(r.value), 0)} {@const pendingCount = claimableRewards().filter( (r: any) => r.status === 'requested' ).length}
{@html handCoinsIcon}
£{familyOwed.toFixed(2)}
Total owed
{@html clockIcon}
{pendingCount}
Awaiting approval
{#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 requested.length > 0} {#each requested as r}
{ return async (args: any) => handleResult(args); }} >
{@html r.rewardType === 'cash' ? coinsIcon : giftIcon} {r.label} {rewardLabel(r)}
{/each} {/if} {#if outstanding.length > 0} {#each outstanding as r}
{@html r.rewardType === 'cash' ? coinsIcon : giftIcon} {r.label} {rewardLabel(r)} {#if paydayLocked(r)} {@html lockIcon} {formatHumanDate(r.settleDate)} {/if}
{/each} {/if} {#if cashRequested.length > 0}
{ return async (args: any) => handleResult(args); }} >
{/if}
{/if} {/each} {/if}
{:else} {#if loading}

Loading...

{:else if error}

{error}

{:else} {#if owedCash > 0}
🎉 You've earned £{owedCash.toFixed(2)} — go get it from your parent!
{/if} {#if showCountdown}
{@html moneyIcon}

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', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, 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()} {headWeekday()} {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}%
{#if pmTargetPct != null}
{/if}
{#if pmTargetPct != null} {@html targetIcon} {pmTargetPct}% {/if}
{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.pct}
{goal.config.name} {#if goal.config.rewardType === 'cash'} £{Number(goal.config.rewardValue).toFixed(2)} {:else if goal.config.rewardType === 'prize'} {@html giftIcon} {:else} {goal.config.rewardValue} pts {/if}
{#if goal.targetPct < 100}
{/if}
{#if goal.isPercent && goal.totalPotential === 0} no chores set {:else if goal.isPercent} {Math.min(goal.current, goal.totalPotential)} / {goal.totalPotential} {:else} {Math.min(goal.current, goal.target)} / {goal.target} {/if} {#if goal.achieved} {@html checkCircleIcon} earned {:else} {pct}% {@html targetIcon} {goal.targetLabel} {/if}
{/each}
{/if} {#if accessDisabled}

{@html sunIcon} Daily

{@html lockIcon} Access paused

{@html calendarIcon} Weekly

{@html lockIcon}

{@html checkCircleIcon} Done

{@html lockIcon}

{:else} {#if celebrations.length > 0}
{#each celebrations as cel}
{cel.emoji} {cel.name} complete — amazing work!
{/each}
{/if}

{@html sunIcon} Daily ({dailyPending.length})

{#if dailyPending.length === 0}

All done!

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

{@html calendarIcon} Weekly ({weeklyPending.length + memberTodos.filter((t) => !isTodoDone(t.id)).length})

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

All done!

{:else} {#each weeklyPending as chore} {/each} {#each memberTodos.filter((t) => !isTodoDone(t.id)) as todo} {@const urgency = todoUrgency(todo)} {#if todo.type === 'emoji'} {:else} {/if} {/each} {/if}

{@html checkCircleIcon} 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}
{/if}
{@html walletIcon} 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'} {@html giftIcon} {:else} {r.value} pts {/if} {#if r.rewardType === 'points'} {@html checkCircleIcon} credited {:else if isReq} {@html clockIcon} waiting {:else if paydayLocked(r)} {@html lockIcon} pays out {formatHumanDate(r.settleDate)} {:else} {/if}
{/each}
{/if}
{/if} {/if}