fix(frontend): Svelte 5 reactivity, each block keys, and path resolution

Add proper keys to #each blocks across 15+ components to fix reordering bugs. Replace new Date() with SvelteDate in reactive contexts. Use $derived for computed values (totalPages). Use resolve() from $app/paths for all internal navigation hrefs. Add ARIA labels and keyboard accessibility to NavBar mobile menu. Remove unused handleRetry from UserPaymentModal.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-06-04 01:08:36 +01:00
co-authored by Sisyphus
parent c76c3f52f7
commit 6c16d65476
33 changed files with 417 additions and 300 deletions
+7 -5
View File
@@ -1,16 +1,18 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { authStore } from '$lib/stores/auth.svelte';
import { browser } from '$app/environment';
import { toast } from 'svelte-sonner';
import { SvelteDate } from 'svelte/reactivity';
import UserBookingModal from '$lib/components/account/UserBookingModal.svelte';
import type { Booking, BookingService } from '$lib/types/booking';
import { Button } from '$lib/components/ui/button';
import * as Card from '$lib/components/ui/card';
let pageState = $state<'loading' | 'authorized' | 'unauthorized'>('loading');
let bookings = $state<any[]>([]);
let bookings = $state<Booking[]>([]);
let loading = $state(false);
let selectedBookingId = $state<string | null>(null);
let showBookingModal = $state(false);
@@ -53,13 +55,13 @@
const now = new SvelteDate();
bookings = (data.bookings || [])
.filter((b: any) => {
.filter((b: Booking) => {
const startTime = new SvelteDate(b.start_time);
const endTime = new SvelteDate(startTime.getTime() + (b.duration_minutes || 0) * 60000);
return endTime > now;
})
.sort(
(a: any, b: any) =>
(a: Booking, b: Booking) =>
new SvelteDate(a.start_time).getTime() - new SvelteDate(b.start_time).getTime()
);
} catch (err) {
@@ -107,7 +109,7 @@
<Card.Description>You don't have any upcoming appointments.</Card.Description>
</Card.Header>
<Card.Content>
<Button href="/book">Book an Appointment</Button>
<Button href={resolve('/book')}>Book an Appointment</Button>
</Card.Content>
</Card.Root>
{:else}
@@ -146,7 +148,7 @@
<div>
{#if booking.services && booking.services.length > 0}
<p class="font-medium">
{booking.services.map((s: any) => s.service_name).join(', ')}
{booking.services.map((s: BookingService) => s.service_name).join(', ')}
</p>
{/if}
{#if booking.total_amount}