feat(frontend): add former name display, referral discount type, and refunds to bookings

Update frontend types, stores, and components to support name history display and new API fields.

- Add previousFirstName/previousLastName to User type and BookingUser type
- Add referral discount_source type and refunds array to Booking type
- Create nameDisplay.ts utility for rendering '(formerly ...)' labels
- Create booking.ts utility for booking-related helpers
- Update 25+ admin, payments, today, and account components to display
  former names on booking cards, modals, appointment views, and user lists

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-20 16:59:31 +01:00
co-authored by Sisyphus
parent 1cf7083bcc
commit 1a3829b4d9
27 changed files with 597 additions and 300 deletions
@@ -3,6 +3,7 @@
import { SvelteDate } from 'svelte/reactivity';
import { toast } from 'svelte-sonner';
import { sanitizeText } from '$lib/utils/toast-safe';
import { formatUserName } from '$lib/utils/nameDisplay';
import * as Card from '$lib/components/ui/card';
import { Button } from '$lib/components/ui/button';
import { Badge } from '$lib/components/ui/badge';
@@ -35,6 +36,8 @@
user_id: string;
services: string[];
duration_minutes: number;
previous_first_name?: string | null;
previous_last_name?: string | null;
};
type TimeBlocker = {
@@ -52,7 +55,7 @@
start_time: string;
duration_minutes: number;
status: string;
user: { id: string; full_name: string; email: string | null; phone: string | null } | null;
user: { id: string; full_name: string; email: string | null; phone: string | null; previous_first_name?: string | null; previous_last_name?: string | null } | null;
services: string[];
};
@@ -742,7 +745,7 @@
class="block max-w-full truncate font-medium hover:text-blue-600 hover:underline"
onclick={() => openUserModal(item.data.user_id)}
>
{item.data.user_name}
{formatUserName(item.data.user_name, item.data.previous_first_name, item.data.previous_last_name)}
</button>
<div class="flex flex-wrap items-center gap-x-1 text-sm text-gray-600">
<span class="truncate">{(item.data.services ?? []).join(', ')}</span>
@@ -997,7 +1000,7 @@
{#each overlappingBookings as booking (booking.id)}
<div class="rounded-md border border-amber-200 bg-white p-2">
<div class="min-w-0">
<div class="text-sm font-medium">{booking.user?.full_name || 'Unknown'}</div>
<div class="text-sm font-medium">{formatUserName(booking.user?.full_name || 'Unknown', booking.user?.previous_first_name, booking.user?.previous_last_name)}</div>
<div class="text-xs text-gray-500">
{new SvelteDate(booking.start_time).toLocaleTimeString('en-GB', {
hour: 'numeric',