{#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); }} 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 total = memberRequestedTotal(m.id)} {#if requested.length > 0 || outstanding.length > 0}

{m.name}

{#if total > 0}

£{total.toFixed(2)} requested

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

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.toFixed(2)}
Pending Cash £{pendingCash.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} {#if claimableRewardsChild.length > 1} {/if} {#each claimableRewardsChild as r}
{r.label} {rewardLabelChild(r)}
{r.rewardType}
{/each} {/if}

Requested ({requestedRewardsChild.length})

{#if requestedRewardsChild.length === 0}

No pending requests

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