Create Admin Booking Book an appointment for a member or guest with flexible pricing and timing
{#if currentStep === 1} Select Customer Choose an existing member or create a guest booking
{#if userType === 'member'}
{ userQuery = e.currentTarget.value; }} onkeydown={(e) => { if (e.key === 'Enter') { e.preventDefault(); fetchUsers(); } }} />
{#if loadingUsers}
{#each Array(3) as _} {/each}
{:else if users.length === 0}
{userQuery ? 'No users found. Try a different search.' : 'Search for a user above to get started.'}
{:else}
    {#each users.slice(0, 4) as user (user.id)}
  • {/each}
{/if}
{:else}
(guestName = e.currentTarget.value)} />
(guestPhone = e.currentTarget.value)} />

Booking as a guest creates a temporary record. Encourage them to sign up for loyalty benefits.

{/if}
currentStep++} />
{/if} {#if currentStep === 2} Choose Services Select one or more treatments for this appointment {#if loadingServices}
{#each Array(4) as _} {/each}
{:else if services.length === 0}

No services available.

{:else} {/if} {#if selectedServices.length > 0}

Selected Services

{#each selectedServices as service (service.id)}
{service.name} {service.duration_minutes} mins • £{service.price}
{/each}
Estimated Duration: {formattedTotalDuration}
Total Cost: £{getTotalPrice()}
{/if}
{/if} {#if currentStep === 3} Customize Services Adjust pricing or duration if needed, and add appointment notes

Service Details

Override default pricing or duration for special cases (discounts, extended sessions, etc.)

{#each selectedServices as service (service.id)} {#if serviceOverrides[service.id]}
{service.name}
handlePriceInput(service.id, e.currentTarget.value)} />
handleDurationInput(service.id, e.currentTarget.value)} />
{#if serviceOverrides[service.id] && (Math.abs(parseFloat(serviceOverrides[service.id].price) - serviceOverrides[service.id].originalPrice) > 0.01 || parseInt(serviceOverrides[service.id].duration) !== serviceOverrides[service.id].originalDuration)}
{#if Math.abs(parseFloat(serviceOverrides[service.id].price) - serviceOverrides[service.id].originalPrice) > 0.01} Price modified from £{serviceOverrides[ service.id ].originalPrice.toFixed(2)} {/if} {#if Math.abs(parseFloat(serviceOverrides[service.id].price) - serviceOverrides[service.id].originalPrice) > 0.01 && parseInt(serviceOverrides[service.id].duration) !== serviceOverrides[service.id].originalDuration} • {/if} {#if parseInt(serviceOverrides[service.id].duration) !== serviceOverrides[service.id].originalDuration} Duration modified from {serviceOverrides[service.id].originalDuration} mins {/if}
{/if}
{/if} {/each}
Total Duration: {formattedTotalDuration}
Total Cost: £{getTotalPrice().toFixed(2)}
{/if} {#if currentStep === 4} Choose Date & Time {selectedServices.map((s) => s.name).join(', ')} • {formattedTotalDuration} total • £{getTotalPrice().toFixed( 2 )} {#if loadingWorkingHours}

Loading available dates...

{:else}
{ selectedDate = newDate; selectedTime = null; }} onPlaceholderChange={(newPlaceholder) => { placeholder = newPlaceholder; }} />
{/if} {#if loadingAvailableHours}

Loading times...

{:else if selectedDate}
{#if formattedSelectedDate}
{formattedSelectedDate}
{/if} {#if groupedTimeSlots.length > 0}
{#each groupedTimeSlots as slot (slot.startTime)} {#if slot.type === 'available'} {@const protection = lunchProtectionStatus().get(slot.startTime)} {#if protection?.isBlocked} {:else} {/if} {:else} {/if} {/each}
{:else}

No available slots

{/if}
{:else}

Select a date to see available times

{/if}
{/if}