feat: Square payment integration, booking flow redesign, and timezone/weekday fixes

- Add Square payment integration (mock + handlers + UI): terminal/online payments,
  refunds, tips, saved cards, webhooks. Build-tagged dev/prod clients.
- Redesign booking flow: Step 4 conditional (deposit only), Step 5 confirmation
  screen with booking ID, auto-submit on transition.
- Redesign schedule modal: 2x3 button grid with Pay Deposit/Pay Early logic.
- Add deposit warning banner at Step 1 for users with outstanding deposits.
- Fix weekday conversion bug: Go 0=Sunday vs DB 0=Monday mismatch in 6 locations.
- Fix timezone bug: UTC vs London time in closing hours validation.
- Fix frontend error parsing: plain text backend errors now displayed correctly.
- Fix crypto.randomUUID fallback for environments without Web Crypto.
- Add 7 new regression tests: closing hours, advance check, active booking limit,
  weekday conversion, UTC/London, deposit snapshot, exceptional hours.
- Fix 3 flaky tests: dynamic dates instead of fixed, no-show timing.
This commit is contained in:
2026-05-23 11:29:34 +01:00
parent bcd5ed2bd9
commit 2e1ab9d745
31 changed files with 6155 additions and 229 deletions
@@ -6,6 +6,7 @@
import { Button } from '$lib/components/ui/button';
import { Badge } from '$lib/components/ui/badge';
import { Skeleton } from '$lib/components/ui/skeleton';
import PaymentModal from '$lib/components/payments/PaymentModal.svelte';
interface Props {
openBookingModal: (bookingId: string) => void;
@@ -44,6 +45,7 @@
let loading = $state(true);
let timeRemaining = $state(0); // minutes remaining in current appointment
let isInProgress = $state(false);
let showPaymentModal = $state(false);
// Calculate time remaining and free time
let interval: ReturnType<typeof setInterval> | null = null;
@@ -163,7 +165,12 @@
}
function handleTakePayment() {
toast.info('Take payment - Coming soon');
showPaymentModal = true;
}
function handlePaymentComplete(payment: unknown) {
toast.success('Payment completed');
showPaymentModal = false;
}
function handleCancel() {
@@ -425,3 +432,7 @@
</Card.Content>
{/if}
</Card.Root>
{#if showPaymentModal}
<PaymentModal booking={activeAppointment} onClose={() => showPaymentModal = false} onComplete={handlePaymentComplete} />
{/if}