Booking Details {#if selectedBooking}
ID: {selectedBooking.id}
{/if}
{#if selectedBooking} {@const isPastBooking = new SvelteDate(selectedBooking.start_time) < new SvelteDate()} {@const isUnpaid = selectedBooking.amount_due > 0} {@const showChip = !isPastBooking || isUnpaid} {@const isConfirmedOrLater = ['confirmed', 'in_progress', 'completed'].includes( selectedBooking.status )} {#if showChip}
{isPastBooking ? 'Unpaid' : selectedBooking.status.replace('_', ' ')} {#if selectedBooking.deposit_required} {#if selectedBooking.status === 'pending'} Will Require Deposit {:else if isConfirmedOrLater} {selectedBooking.deposit_paid ? 'Deposit Paid' : 'Deposit Due'} {/if} {/if}
{/if} {/if}
{#if loading}
Loading...
{:else if selectedBooking}

Appointment Details

{#if selectedBooking.user?.previous_first_name && selectedBooking.user?.previous_last_name} {@const fullName = `${selectedBooking.user.first_name} ${selectedBooking.user.last_name}`} {@const formerName = `${selectedBooking.user.previous_first_name} ${selectedBooking.user.previous_last_name}`}
{fullName}, (formerly {formerName})
{/if}
Scheduled Date & Time
{(() => { const date = parseWallClockDate(selectedBooking.start_time); const dateStr = date.toLocaleDateString('en-US', { weekday: 'long', day: 'numeric', month: 'short' }); const timeStr = date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }); return `${dateStr} at ${timeStr}`; })()}
Duration
{totalDuration} minutes
{#if selectedBooking.notes}
Notes
{selectedBooking.notes}
{/if}
{#if selectedBooking.services && selectedBooking.services.length > 0}

Services

{#each selectedBooking.services as service, index (index)}
{service.service_name || '—'}
{#if service.service_description}
{service.service_description}
{/if}
{service.override_duration_minutes ?? service.duration_minutes} min £{(service.override_price ?? service.price ?? 0).toFixed(2)}
{/each}
{/if}

Financial Summary

{#if ['confirmed', 'in_progress', 'completed'].includes(selectedBooking.status) && selectedBooking.deposit_required}
Deposit Required
£{selectedBooking.deposit_amount?.toFixed(2) || '0.00'}
{selectedBooking.deposit_paid ? 'Paid' : 'Outstanding'} {#if !selectedBooking.deposit_paid && selectedBooking.deposit_deadline} • Due: {parseWallClockDate( selectedBooking.deposit_deadline ).toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' })} at {parseWallClockDate( selectedBooking.deposit_deadline ).toLocaleTimeString('en-GB', { hour: 'numeric', minute: '2-digit', hour12: true })} {/if}
{/if}
Subtotal (Services) £{selectedBooking.total_amount.toFixed(2)}
{#if selectedBooking.discounts && selectedBooking.discounts.length > 0}
{#each selectedBooking.discounts as d (d.id)}
{#if d.discount_source === 'loyalty'} Loyalty Stamp Card (10% Off) {:else if d.discount_source === 'referral'} Referral Discount ({d.discount_percent}% Off) {:else if d.campaign_name} {d.campaign_name} ({d.discount_percent}% Off) {:else} Promo Campaign ({d.discount_percent}% Off) {/if} -£{d.discount_amount.toFixed(2)}
{/each}
Net Total £{( selectedBooking.total_amount - selectedBooking.discounts.reduce((sum, d) => sum + d.discount_amount, 0) ).toFixed(2)}
{/if} {#if hasVAT}
Net amount (excl. VAT) £{totalNet.toFixed(2)}
VAT ({businessSettings?.default_vat_rate ?? 20}%) £{totalVAT.toFixed(2)}
{/if}
Amount Paid (Card/Cash) £{(selectedBooking.payments ?? []) .filter((p) => p.payment_method !== 'discount' && p.status === 'completed') .reduce((sum, p) => sum + p.amount, 0) .toFixed(2)}
{#if totalRefunds > 0}
Refunds -£{totalRefunds.toFixed(2)}
{/if} {#if balanceDue > 0.01}
{isFutureBooking ? 'Estimated Subtotal' : 'Amount Due'} £{balanceDue.toFixed(2)}
{/if}
{#if (selectedBooking.payments && selectedBooking.payments.length > 0) || (selectedBooking.refunds && selectedBooking.refunds.length > 0)}

Payment History

{#each selectedBooking.payments as payment, index (index)}
{getPaymentName( payment, index, selectedBooking.payments, selectedBooking.discounts )} {payment.status}
{#if payment.payment_method === 'discount'} Applied automatically on completion {:else} {payment.payment_type.charAt(0).toUpperCase() + payment.payment_type.slice(1)} payment {#if payment.card_last4} , Card ending in {payment.card_last4} {/if} {/if}
{#if payment.is_vat_applicable}
Net: £{payment.net_amount?.toFixed(2) || '0.00'}
{#if payment.vat_amount}
VAT ({payment.vat_rate || 0}%): £{payment.vat_amount.toFixed(2)}
{/if}
{/if}
{new SvelteDate(payment.created_at).toLocaleString()}
{payment.payment_method === 'discount' ? '-' : ''}£{payment.amount.toFixed(2)}
{/each} {#each selectedBooking.refunds ?? [] as refund (refund.id)}
Refund {refund.status}
{refund.reason || 'Refund processed'}
{new SvelteDate(refund.created_at).toLocaleString()}
-£{refund.amount.toFixed(2)}
{/each}
{/if}
{/if}
{#if isCancellable} {#if canEditBooking} {/if} {/if}
{#if pendingEditRequest} {@const timeChanged = pendingEditRequest.original.start_time && pendingEditRequest.proposed?.start_time && pendingEditRequest.original.start_time !== pendingEditRequest.proposed.start_time} {@const servicesChanged = pendingEditRequest.proposed?.services?.length && JSON.stringify(pendingEditRequest.original.services?.map((s) => s.name)) !== JSON.stringify(pendingEditRequest.proposed.services?.map((s) => s.name))} {@const originalNames = (pendingEditRequest.original.services ?? []).map((s) => s.name)} {@const proposedNames = (pendingEditRequest.proposed.services ?? []).map((s) => s.name)} {@const addedServices = proposedNames.filter((n) => !originalNames.includes(n))} {@const removedServices = originalNames.filter((n) => !proposedNames.includes(n))} {#if timeChanged || servicesChanged}

Awaiting admin approval

{#if timeChanged} Reschedule requested from {parseWallClockDate( pendingEditRequest.original.start_time! ).toLocaleString('en-GB', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' })} to {parseWallClockDate( pendingEditRequest.proposed.start_time! ).toLocaleString('en-GB', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' })} {/if}

{#if addedServices.length > 0}

Services added: {addedServices.join(', ')}

{/if} {#if removedServices.length > 0}

Services removed: {removedServices.join(', ')}

{/if}

We'll let you know once it's been reviewed

{/if} {/if}
{#if selectedBooking} {/if} {#if isCompleted} {/if} {#if depositOutstanding && selectedBooking?.status !== 'pending'} {:else if canPayEarly && !hasPendingEditRequest} {/if} {#if hasPendingEditRequest && !depositOutstanding}
Payments paused while awaiting approval
{/if}
{#if showEditModal && selectedBooking} { showEditModal = false; fetchBookingDetails(); }} /> {/if} {#if showPaymentModal && selectedBooking} (showPaymentModal = false)} onComplete={handlePaymentComplete} {canSaveCards} defaultPaymentType={depositOutstanding ? 'deposit' : undefined} /> {/if} (showCancelConfirm = v)}> Cancel Booking {@const hoursUntilAppt = Math.round(hoursUntilAppointment)} {#if hasPayments}

Are you sure you want to cancel this booking? You have already made payments totalling £{totalPaid.toFixed(2)}.

{#if hoursUntilAppt > POLICY.FULL_REFUND_THRESHOLD_HOURS}

Full Refund

You have given over {POLICY.FULL_REFUND_THRESHOLD_HOURS} hours' notice. You will receive a full refund of £{totalPaid.toFixed(2)}. Nothing will be deducted.

{:else if hoursUntilAppt >= POLICY.PARTIAL_REFUND_THRESHOLD_HOURS}

Partial Refund

Based on your notice period ({hoursUntilAppt}h), up to {POLICY.PROTECTED_DEPOSIT_MAX_PCT * 100}% of the total (£{protectedDeposit.toFixed(2)}) is treated as a protected deposit and will be retained. The remaining £{estimatedRefund.toFixed(2)} will be refunded.

{:else}

Cancelling With No Refund

This booking is under {POLICY.PARTIAL_REFUND_THRESHOLD_HOURS} hours' notice. The full amount you have paid (£{totalPaid.toFixed(2)}) will be retained to cover the lost slot. It will also count as a no-show toward your booking history (2 no-shows within 6 months would require deposits on future bookings).

{/if}

Refunds are processed via Square and may take 3–5 business days. See our {#snippet trigger()} cancellation policy {/snippet} for full details.

{:else if hoursUntilAppt >= POLICY.PARTIAL_REFUND_THRESHOLD_HOURS}

Are you sure you want to cancel this booking?

Clean Cancellation

This booking has no payments and is being cancelled with {hoursUntilAppt > POLICY.FULL_REFUND_THRESHOLD_HOURS ? 'plenty of' : 'sufficient'} notice. It will be removed completely and will not appear in your booking history.

{:else}

Are you sure you want to cancel this booking?

Short Notice Cancellation

This booking is under {POLICY.PARTIAL_REFUND_THRESHOLD_HOURS} hours' notice. Cancelling now counts as a no-show (2 no-shows within 6 months will require deposits on future bookings).

{/if}
{ if (!v) { showTipModal = false; tipAmount = 0; selectedTipPreset = null; customTipInput = ''; } }} > Leave a Tip Show your appreciation for great service
{#each tipPresets as preset (preset.pct)} {/each}
£
Payment Method {#if tipLoadingCards}
Loading payment methods...
{:else if tipSavedCards.length > 0}
{#each tipSavedCards as card (card.id)} {/each}
{#if tipShowNewCard}
{#if tipNewCardError}
{tipNewCardError}
{/if}
{/if} {:else}
{#if tipNewCardError}
{tipNewCardError}
{/if}
{/if}