My Account
Manage your profile, bookings, and settings
{#if activeTab === 'general'}
Profile Information
Your personal details and account information
{#if userData}
{@const initials =
userData.firstName && userData.lastName
? userData.firstName
.split(' ')
.map((n) => n[0])
.join('') +
userData.lastName
.split(' ')
.map((n) => n[0])
.join('')
: ''}
{@const hasImage = !!userData.profilePicUrl || !!previewUrl}
{@const displayUrl = previewUrl || userData.profilePicUrl || ''}
{#if hasImage || initials}
{#if hasImage}
{:else}
{initials}
{/if}
{:else}
{/if}
document.getElementById('profile-pic-input')?.click()}
>
Upload profile picture
{/if}
Crop Profile Picture
{#if cropImageUrl}
{
cropArea = e.pixels;
}}
/>
{/if}
Cancel
Save
{#if loadingUser}
{#each range(6) as i (i)}
{/each}
{:else if userData}
First Name
{#if editingFirstName}
{#if firstNameError}
{firstNameError}
{/if}
{savingName ? 'Saving...' : 'Save'}
Cancel
{:else}
{userData.firstName}
Edit
{/if}
Last Name
{#if editingLastName}
{#if lastNameError}
{lastNameError}
{/if}
{savingName ? 'Saving...' : 'Save'}
Cancel
{:else}
{/if}
Phone
{#if editingPhone}
{savingPhone ? 'Saving...' : 'Save'}
Cancel
{:else}
{userData.phone || '—'}
Edit
{/if}
{#if authStore.currentUser?.role !== 'admin'}
{#if stamps >= 10}
Card Completed — 10% Off Your Next Booking!
{/if}
{#if userData}
Loyalty Stamp Card
{stamps < 10
? `Collect ${10 - stamps} more stamp${10 - stamps === 1 ? '' : 's'} to get 10% off your next booking.`
: (() => {
const fc = Math.floor(stamps / 10);
return `You have ${fc === 1 ? 'a' : fc} full stampcard${fc === 1 ? '' : 's'} ready to take advantage of at your next booking!`;
})()}
{#each range(10) as i (i)}
{@const slotNum = i + 1}
{@const rot = ((slotNum * 37 + 13) % 7) - 3}
{#if slotNum <= (stamps > 0 ? stamps % 10 || 10 : 0)}
{:else}
{/if}
{/each}
{/if}
{/if}
{/if}
{:else if activeTab === 'history'}
{#if upcomingBookings.length > 0}
Upcoming Appointments
Next {upcomingBookings.length < 3 ? upcomingBookings.length : 3} upcoming bookings
{#if loadingUpcoming}
{#each range(3) as i (i)}
{/each}
{:else}
{#each upcomingBookings as b (b.id)}
{formatDateTime(b.start_time)}
{b.status}
{#if b.services && b.services.length > 0}
- {(() => {
const services = b.services.map(
(s) => s.service_name || 'Unknown Service'
);
if (services.length === 1) return services[0];
if (services.length === 2) return services.join(' and ');
return `${services[0]} and ${services.length - 1} other${services.length - 1 > 1 ? 's' : ''}`;
})()}
{/if}
openBookingModal(b.id)}>View
{/each}
{/if}
{/if}
Past Appointments
Previous bookings
{#if loadingPast}
{#each range(5) as i (i)}
{/each}
{:else if pastBookings.length === 0}
No past bookings
{:else}
{#each pastBookings as b (b.id)}
{formatDateTime(b.start_time)}
{#if (b.amount_due || 0) > 0}
Unpaid
{/if}
{#if b.services && b.services.length > 0}
- {(() => {
const services = b.services.map(
(s) => s.service_name || 'Unknown Service'
);
if (services.length === 1) return services[0];
if (services.length === 2) return services.join(' and ');
return `${services[0]} and ${services.length - 1} other${services.length - 1 > 1 ? 's' : ''}`;
})()}
{/if}
{#if b.total_amount}
— £{b.total_amount.toFixed(2)}
{/if}
openBookingModal(b.id)}>View
{/each}
{/if}
{#if pastTotalPages > 1}
goToPastPage(pastPage - 1)}>Prev
{pastPage} / {pastTotalPages}
goToPastPage(pastPage + 1)}>Next
{/if}
{:else if activeTab === 'referral'}
Referral Program
Share your code and earn rewards
{#if loadingUser}
{:else if userData?.referralCode}
Your Referral Code
{#each userData.referralCode.match(/.{1,4}/g) as part, i (i)}{part} {#if i < 2}– {/if}{/each}
Copy Code
{userData.referralCodeUses || 0}
Friends Referred
£{(userData.referralSavings || 0).toFixed(2)}
Total Saved
How it works:
• Share your referral code with friends
• They get 10% off their first booking
• You get 10% off your next booking after they claim
• You earn 3 loyalty stamp for each use to keep the savings going
{:else}
No referral code available
{/if}
{:else if activeTab === 'cards'}
Saved Cards
Manage your saved payment methods
{#if loadingCards}
{:else if savedCardsStore.cards.length === 0}
{:else}
{#each savedCardsStore.cards as card (card.id)}
**** {card.last_4}
Expires {String(card.exp_month).padStart(2, '0')}/{card.exp_year}
{
cardToDelete = card;
showDeleteCardDialog = true;
}}
>
Remove
{/each}
{/if}
Redeem Gift Card
Redeem a gift card directly to your account balance.
Your Balance
{loadingBalance ? '...' : formatCurrency(giftCardBalance)}
Redeem Gift Card
Claiming this gift card will add its remaining balance directly to your account
balance, which can be used toward future bookings.
What happens when I claim?
The gift card value is added to your account balance.
Account balances do not expire, but gift card codes become invalid once
redeemed.
This action is final and cannot be reversed.
Legal & GDPR Information
Your personal data (name, email, transaction history) is processed in
accordance with UK data protection law.
Financial records are retained for 7 years as required by HMRC, after which
personally identifiable information is anonymised.
You can request a full copy of your data or deletion of your account at any
time via your account settings.
By redeeming this gift card, you agree to our Terms & Conditions.
Link T&Cs here once available.
Cancel
Confirm & Redeem
Buy a Gift Card
Purchase a gift card online for yourself or a friend.
{#if purchaseResultCode}
{#if buyRecipientType === 'self'}
Your purchase of {formatCurrency(buyAmount)} has been automatically
added to your account balance!
{:else}
Here is your gift card code:
{formatCardCode(purchaseResultCode)}
⚠️ Please save this code and send it to your friend — no email was sent.
{/if}
(purchaseResultCode = null)}
class="w-full"
>
Buy Another Card
{:else}
Select Value
{#each [10, 20, 50] as amount (amount)}
(buyAmount = amount as 10 | 20 | 50)}
>
{formatCurrency(amount)}
{/each}
Recipient
(buyRecipientType = 'self')}
>
For Myself (Auto-Redeem)
(buyRecipientType = 'friend')}
>
For a Friend (Gift Code)
{#if buyRecipientType === 'friend'}
Friend's Email (Optional)
{/if}
Payment Method
{#if savedCardsStore.cards.length > 0}
{#each savedCardsStore.cards as card (card.id)}
(buySelectedCard = card.id)}
>
**** {card.last_4}
Exp {String(card.exp_month).padStart(2, '0')}/{card.exp_year}
{#if buySelectedCard === card.id}
Selected
{/if}
{/each}
{:else}
{/if}
{buyingGiftCard ? 'Processing Payment...' : `Pay ${formatCurrency(buyAmount)}`}
{/if}
{:else if activeTab === 'admin'}
Account Settings
Manage your security and account preferences
Password
Update your password to keep your account secure
(showPasswordModal = true)} variant="outline">
Change Password
Session
Log out of this account on this device.
authStore.logout()} variant="outline">
Log Out
Data Privacy
View and export all personal data we hold about you
goto('/gdpr')} variant="outline">
Export My Data
{#if gdprCountdown}
{gdprCountdown}
{/if}
{#if authStore.currentUser?.role !== 'admin'}
Notifications
Choose how you receive booking reminders and updates
Email
Booking confirmations and reminders
{
notifPrefs.emailEnabled = !notifPrefs.emailEnabled;
await saveNotifPrefs();
}}
/>
SMS
Text message reminders
{
notifPrefs.smsEnabled = !notifPrefs.smsEnabled;
await saveNotifPrefs();
}}
/>
Browser
In-browser notifications
{
notifPrefs.browserPushEnabled = !notifPrefs.browserPushEnabled;
await saveNotifPrefs();
}}
/>
{/if}
Policies
View our cancellation, deposit, and no-show policies
{#snippet trigger()}
Cancellation & Deposit Policy
{/snippet}
{#if authStore.currentUser?.role !== 'admin'}
Danger Zone
Once you delete your account, there is no going back. Please be certain.
(showDeleteAlert = true)} variant="destructive">
Delete Account
{/if}
{/if}