feat(frontend): integrate custom services across booking flow and routes

Update BookingFlow to support custom service selection, add custom_services to booking types, and link CustomServicesManagement into admin pages. Minor updates to schedules, prices, and today routes.

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-15 16:58:37 +01:00
co-authored by Sisyphus
parent 8f8bf83b64
commit 867f86978b
7 changed files with 36 additions and 7 deletions
@@ -1514,10 +1514,19 @@
{services}
selected={selectedServices}
loading={servicesLoading}
ontoggle={toggleService}
/>
ontoggle={toggleService}
/>
{#if selectedServices.length > 0}
<div class="mt-4 rounded-lg border border-dashed border-gray-300 bg-gray-50 p-4 text-center">
<p class="text-sm text-gray-600">
Need something different? Select the closest service and add a note, or contact us for a bespoke treatment.
</p>
<a href="/contact" class="mt-1 inline-block text-sm font-medium text-blue-600 hover:underline">
Arrange a custom booking →
</a>
</div>
{#if selectedServices.length > 0}
<div class="rounded-lg bg-gray-50 p-4">
<h4 class="mb-2 font-semibold">Selected Services</h4>
<div class="space-y-2">
@@ -744,7 +744,7 @@
{item.data.user_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>
<span class="truncate">{(item.data.services ?? []).join(', ')}</span>
<span
class="shrink-0 rounded-full bg-gray-100 px-1.5 py-0.5 text-xs font-medium"
>
+8 -1
View File
@@ -6,10 +6,17 @@ export interface Service {
duration_minutes: number;
patch_test_duration_hours: number;
minimum_age_required: number;
// Patch test status (present when user is authenticated, not admin)
patch_test_status?: 'ok' | 'required' | 'expired';
}
export interface CustomService extends Service {
usage_count: number;
last_used_at: string | null;
notes: string | null;
created_at: string;
created_by?: string;
}
export interface CustomerInfo {
firstName: string;
lastName: string;
+2
View File
@@ -12,6 +12,7 @@
import TimeBlockers from '$lib/components/admin/TimeBlockers.svelte';
import WeeklySchedule from '$lib/components/admin/WeeklySchedule.svelte';
import ServicesManagement from '$lib/components/admin/ServicesManagement.svelte';
import CustomServicesManagement from '$lib/components/admin/CustomServicesManagement.svelte';
import PatchTestsManagement from '$lib/components/admin/PatchTestsManagement.svelte';
import DiscountsManagement from '$lib/components/admin/DiscountsManagement.svelte';
import GiftCardsManagement from '$lib/components/admin/GiftCardsManagement.svelte';
@@ -300,6 +301,7 @@
<HolidayHours />
<WeeklySchedule />
<ServicesManagement />
<CustomServicesManagement />
<PatchTestsManagement />
<DiscountsManagement />
<GiftCardsManagement />
@@ -131,7 +131,7 @@
}
function getServiceNames(b: ScheduleBooking): string {
return b.services
return (b.services ?? [])
.map((s) => s.service_name)
.filter(Boolean)
.join(', ');
+11
View File
@@ -156,6 +156,17 @@
{/each}
</div>
<Card.Root class="border-dashed border-gray-300 bg-gray-50">
<Card.Content class="p-6 text-center">
<p class="text-sm text-gray-600">
Need something different? Select the closest service and add a note, or contact us for a bespoke treatment.
</p>
<a href={resolve('/contact')} class="mt-1 inline-block text-sm font-medium text-blue-600 hover:underline">
Arrange a custom booking →
</a>
</Card.Content>
</Card.Root>
<Card.Root class="border-amber-200 bg-amber-50">
<Card.Header>
<Card.Title class="flex items-center gap-2 text-amber-800">
+1 -1
View File
@@ -135,8 +135,8 @@
<!-- Quick Booking + Till Purchases Grid -->
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:gap-6">
<div class="space-y-4">
<WalkInBooking />
<CallInBooking />
<WalkInBooking />
</div>
<div>
<TillPurchases />