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:
@@ -8,6 +8,7 @@
|
||||
import * as Textarea from '$lib/components/ui/textarea';
|
||||
import * as Label from '$lib/components/ui/label';
|
||||
import DatePicker from '$lib/components/booking/DatePicker.svelte';
|
||||
import PaymentModal from '$lib/components/payments/PaymentModal.svelte';
|
||||
import type { Booking, WorkingHoursDay, AvailableHoursDay } from '$lib/types/booking';
|
||||
import { extractBookedSlots, getLunchProtectionForSlots } from '$lib/lunchProtection';
|
||||
|
||||
@@ -94,6 +95,25 @@
|
||||
.reduce((sum, p) => sum + p.amount, 0) || 0
|
||||
);
|
||||
|
||||
let depositOutstanding = $derived(
|
||||
selectedBooking?.deposit_required && !selectedBooking?.deposit_paid
|
||||
);
|
||||
|
||||
let canPayEarly = $derived(
|
||||
selectedBooking &&
|
||||
!depositOutstanding &&
|
||||
totalPaid < selectedBooking.total_amount &&
|
||||
['confirmed', 'pending'].includes(selectedBooking.status)
|
||||
);
|
||||
|
||||
let showPaymentModal = $state(false);
|
||||
|
||||
function handlePaymentComplete() {
|
||||
toast.success('Payment completed');
|
||||
showPaymentModal = false;
|
||||
fetchBookingDetails();
|
||||
}
|
||||
|
||||
let isRescheduleValid = $derived(
|
||||
rescheduleDate && rescheduleTime && rescheduleTime.length >= 4
|
||||
);
|
||||
@@ -847,12 +867,37 @@
|
||||
Add to Calendar
|
||||
</Button>
|
||||
{/if}
|
||||
<Button size="sm" class="flex-1" onclick={() => (open = false)}>Close</Button>
|
||||
{#if depositOutstanding}
|
||||
<Button
|
||||
size="sm"
|
||||
class="flex-1 bg-amber-600 hover:bg-amber-700 text-white"
|
||||
onclick={() => (showPaymentModal = true)}
|
||||
>
|
||||
Pay Deposit
|
||||
</Button>
|
||||
{:else if canPayEarly}
|
||||
<Button
|
||||
size="sm"
|
||||
class="flex-1 bg-emerald-600 hover:bg-emerald-700 text-white"
|
||||
onclick={() => (showPaymentModal = true)}
|
||||
>
|
||||
Pay Early
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
<Button size="sm" class="w-full" variant="ghost" onclick={() => (open = false)}>Close</Button>
|
||||
</div>
|
||||
</Modal.Content>
|
||||
</Modal.Root>
|
||||
|
||||
{#if showPaymentModal && selectedBooking}
|
||||
<PaymentModal
|
||||
booking={selectedBooking}
|
||||
onClose={() => (showPaymentModal = false)}
|
||||
onComplete={handlePaymentComplete}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<Modal.Root open={showCancelConfirm} onOpenChange={(v) => (showCancelConfirm = v)}>
|
||||
<Modal.Content class="max-w-sm">
|
||||
<Modal.Header>
|
||||
|
||||
Reference in New Issue
Block a user