fix: remove AM/PM filter from routines, add keyed each blocks

Remove the All/AM/PM filter buttons and part_of_day param from
the routines list page — date-based sorting/filtering is preferred.

Add missing keyed {#each} blocks across all pages to follow
Svelte 5 best practice (dashboard, products, medications,
lab results, routines list and detail, skin).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Piotr Oleszczyk 2026-02-28 12:57:56 +01:00
parent 66ee473deb
commit 853019075d
7 changed files with 15 additions and 39 deletions

View file

@ -44,7 +44,7 @@
{/if} {/if}
{#if s.active_concerns.length} {#if s.active_concerns.length}
<div class="flex flex-wrap gap-1"> <div class="flex flex-wrap gap-1">
{#each s.active_concerns as concern} {#each s.active_concerns as concern (concern)}
<Badge variant="secondary">{concern.replace(/_/g, ' ')}</Badge> <Badge variant="secondary">{concern.replace(/_/g, ' ')}</Badge>
{/each} {/each}
</div> </div>
@ -67,7 +67,7 @@
<CardContent> <CardContent>
{#if data.recentRoutines.length} {#if data.recentRoutines.length}
<ul class="space-y-2"> <ul class="space-y-2">
{#each data.recentRoutines as routine} {#each data.recentRoutines as routine (routine.id)}
<li class="flex items-center justify-between"> <li class="flex items-center justify-between">
<a href="/routines/{routine.id}" class="text-sm hover:underline"> <a href="/routines/{routine.id}" class="text-sm hover:underline">
{routine.routine_date} {routine.routine_date}

View file

@ -68,7 +68,7 @@
<SelectTrigger class="w-32">{filterFlag || 'All'}</SelectTrigger> <SelectTrigger class="w-32">{filterFlag || 'All'}</SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="">All</SelectItem> <SelectItem value="">All</SelectItem>
{#each flags as f} {#each flags as f (f)}
<SelectItem value={f}>{f}</SelectItem> <SelectItem value={f}>{f}</SelectItem>
{/each} {/each}
</SelectContent> </SelectContent>
@ -111,7 +111,7 @@
<SelectTrigger>{selectedFlag || 'None'}</SelectTrigger> <SelectTrigger>{selectedFlag || 'None'}</SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="">None</SelectItem> <SelectItem value="">None</SelectItem>
{#each flags as f} {#each flags as f (f)}
<SelectItem value={f}>{f}</SelectItem> <SelectItem value={f}>{f}</SelectItem>
{/each} {/each}
</SelectContent> </SelectContent>
@ -138,7 +138,7 @@
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{#each data.results as r} {#each data.results as r (r.id)}
<TableRow> <TableRow>
<TableCell class="text-sm">{r.collected_at.slice(0, 10)}</TableCell> <TableCell class="text-sm">{r.collected_at.slice(0, 10)}</TableCell>
<TableCell class="font-medium">{r.test_name_original ?? r.test_code}</TableCell> <TableCell class="font-medium">{r.test_name_original ?? r.test_code}</TableCell>

View file

@ -54,7 +54,7 @@
<Select type="single" value={kind} onValueChange={(v) => (kind = v)}> <Select type="single" value={kind} onValueChange={(v) => (kind = v)}>
<SelectTrigger>{kind}</SelectTrigger> <SelectTrigger>{kind}</SelectTrigger>
<SelectContent> <SelectContent>
{#each kinds as k} {#each kinds as k (k)}
<SelectItem value={k}>{k}</SelectItem> <SelectItem value={k}>{k}</SelectItem>
{/each} {/each}
</SelectContent> </SelectContent>
@ -81,7 +81,7 @@
{/if} {/if}
<div class="space-y-3"> <div class="space-y-3">
{#each data.medications as med} {#each data.medications as med (med.id)}
<div class="rounded-md border border-border px-4 py-3"> <div class="rounded-md border border-border px-4 py-3">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">

View file

@ -51,7 +51,7 @@
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="">All categories</SelectItem> <SelectItem value="">All categories</SelectItem>
{#each categories as cat} {#each categories as cat (cat)}
<SelectItem value={cat}>{cat.replace(/_/g, ' ')}</SelectItem> <SelectItem value={cat}>{cat.replace(/_/g, ' ')}</SelectItem>
{/each} {/each}
</SelectContent> </SelectContent>
@ -70,7 +70,7 @@
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{#each data.products as product} {#each data.products as product (product.id)}
<TableRow class="cursor-pointer hover:bg-muted/50"> <TableRow class="cursor-pointer hover:bg-muted/50">
<TableCell> <TableCell>
<a href="/products/{product.id}" class="font-medium hover:underline"> <a href="/products/{product.id}" class="font-medium hover:underline">
@ -83,7 +83,7 @@
</TableCell> </TableCell>
<TableCell> <TableCell>
<div class="flex flex-wrap gap-1"> <div class="flex flex-wrap gap-1">
{#each product.targets.slice(0, 3) as t} {#each product.targets.slice(0, 3) as t (t)}
<Badge variant="secondary" class="text-xs">{t.replace(/_/g, ' ')}</Badge> <Badge variant="secondary" class="text-xs">{t.replace(/_/g, ' ')}</Badge>
{/each} {/each}
{#if product.targets.length > 3} {#if product.targets.length > 3}

View file

@ -2,10 +2,9 @@ import { getRoutines } from '$lib/api';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ url }) => { export const load: PageServerLoad = async ({ url }) => {
const part_of_day = url.searchParams.get('part_of_day') ?? undefined;
const from_date = url.searchParams.get('from_date') ?? recentDate(30); const from_date = url.searchParams.get('from_date') ?? recentDate(30);
const routines = await getRoutines({ from_date, part_of_day }); const routines = await getRoutines({ from_date });
return { routines, part_of_day }; return { routines };
}; };
function recentDate(daysAgo: number): string { function recentDate(daysAgo: number): string {

View file

@ -2,14 +2,9 @@
import type { PageData } from './$types'; import type { PageData } from './$types';
import { Badge } from '$lib/components/ui/badge'; import { Badge } from '$lib/components/ui/badge';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import { goto } from '$app/navigation';
let { data }: { data: PageData } = $props(); let { data }: { data: PageData } = $props();
function filterPod(pod: string) {
goto(pod ? `/routines?part_of_day=${pod}` : '/routines');
}
// Group by date // Group by date
const byDate = $derived( const byDate = $derived(
data.routines.reduce( data.routines.reduce(
@ -34,24 +29,6 @@
<Button href="/routines/new">+ New routine</Button> <Button href="/routines/new">+ New routine</Button>
</div> </div>
<div class="flex gap-2">
<Button
variant={!data.part_of_day ? 'default' : 'outline'}
size="sm"
onclick={() => filterPod('')}
>All</Button>
<Button
variant={data.part_of_day === 'am' ? 'default' : 'outline'}
size="sm"
onclick={() => filterPod('am')}
>AM</Button>
<Button
variant={data.part_of_day === 'pm' ? 'default' : 'outline'}
size="sm"
onclick={() => filterPod('pm')}
>PM</Button>
</div>
{#if sortedDates.length} {#if sortedDates.length}
<div class="space-y-4"> <div class="space-y-4">
{#each sortedDates as date} {#each sortedDates as date}

View file

@ -65,7 +65,7 @@
{/if} {/if}
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{#each products as p} {#each products as p (p.id)}
<SelectItem value={p.id}>{p.name} ({p.brand})</SelectItem> <SelectItem value={p.id}>{p.name} ({p.brand})</SelectItem>
{/each} {/each}
</SelectContent> </SelectContent>
@ -90,7 +90,7 @@
{#if routine.steps.length} {#if routine.steps.length}
<div class="space-y-2"> <div class="space-y-2">
{#each routine.steps.toSorted((a, b) => a.order_index - b.order_index) as step} {#each routine.steps.toSorted((a, b) => a.order_index - b.order_index) as step (step.id)}
<div class="flex items-center justify-between rounded-md border border-border px-4 py-3"> <div class="flex items-center justify-between rounded-md border border-border px-4 py-3">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<span class="text-xs text-muted-foreground w-4">{step.order_index + 1}</span> <span class="text-xs text-muted-foreground w-4">{step.order_index + 1}</span>