{#if role === 'parent'} {#if toast}
{toast}
{/if} {#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, (d) => { if (d.revoked) famStore.applyRecord('completions', { id: d.id }, 'delete'); }); }} 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) / 100).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, (d: any) => { if (d.records) { d.records.forEach((r: any) => famStore.applyRecord('rewards', r, 'create')); } }); }}>
■ {targeted.name}
{:else}
{ return async (args: any) => handleResult(args, (d: any) => { if (d.records) { d.records.forEach((r: any) => famStore.applyRecord('rewards', r, 'create')); } }); }}>
{/if}
{/each} {/if}
{#if claimableRewards().length === 0}

No outstanding claims

{:else} {#each parentMembers as m} {@const mc = memberClaimable(m.id)} {#if mc.length > 0}

{m.name}

{#each mc as r}
{ return async (args: any) => handleResult(args, (d: any) => { if (d.record) { famStore.applyRecord('rewards', d.record, 'update'); } }); }}>
{r.label}
{rewardLabel(r)}
{/each}

Response:

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

{ 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}

{#if showColorPicker}
showColorPicker = false}>
e.stopPropagation()}>

Pick a color:

{#each ['#ef4444','#f97316','#eab308','#22c55e','#14b8a6','#3b82f6','#6366f1','#a855f7','#ec4899','#78716c'] as color} {/each}
{/if} {#if loading}

Loading...

{:else if error}

{error}

{:else}
Bonus Notices {bonusNotices.length} {#if bonusNotices.length > 0} +{bonusNotices.reduce((s, r) => s + Number(r.value), 0)} pts {/if}
Total Cash £{(totalCash / 100).toFixed(2)}
Pending Cash £{(pendingCash / 100).toFixed(2)}
Total Points {totalPoints}
{#if weeklyTotal > 0} {@const wpct = Math.round((weeklyDone / weeklyTotal) * 100)}
This Week {weeklyDone}/{weeklyTotal} done
{/if} {#if bonusProgresses.length > 0}
{#each bonusProgresses as bp} {@const pct = bp.criteria > 0 ? Math.min(100, (bp.current / bp.criteria) * 100) : 0} {@const hasReward = bp.periodStart ? rewards.some(r => r.memberId === memberId && r.bonusConfigId === bp.config.id && r.date >= bp.periodStart && r.date <= bp.periodEnd) : rewards.some(r => r.memberId === memberId && r.bonusConfigId === bp.config.id)} {@const typeClass = bp.config.type === 'threshold' ? 'type-threshold' : bp.config.type === 'count' ? 'type-count' : 'type-manual'}
{bp.config.name} {bp.config.type}
{#if bp.criteria > 0}
{bp.current}/{bp.criteria} {bp.config.type === 'threshold' ? 'pts' : 'chores'} {/if}
{#if hasReward} Reward earned 🎉 {/if}
{/each}
{/if}

Daily Pending ({dailyPending.length})

{#if dailyPending.length === 0}

All done!

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

Weekly Pending ({weeklyPending.length})

{#if weeklyPending.length === 0}

All done!

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

Completed Today ({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}

Claimable ({claimableRewardsChild.length})

{#if claimableRewardsChild.length === 0}

Nothing to claim

{:else} {#each claimableRewardsChild as r}
{r.label} {rewardLabelChild(r)}
{r.rewardType}
{/each} {/if}
{/if}
{/if}