This commit is contained in:
2026-06-06 15:18:37 +01:00
parent b274d2c47b
commit 9c6eeeb426
3 changed files with 30 additions and 46 deletions
@@ -942,12 +942,19 @@
<input <input
id="guest-phone" id="guest-phone"
type="tel" type="tel"
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none" class="flex h-10 w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none {guestPhoneError
? 'border-red-500'
: 'border-input'}"
placeholder="07700 900000" placeholder="07700 900000"
value={guestPhone} value={guestPhone}
oninput={(e) => { guestPhone = e.currentTarget.value; }} oninput={(e) => {
onblur={() => { if (guestPhone && !isValidUKPhone(guestPhone)) guestPhoneError = 'Invalid UK phone number'; else guestPhoneError = ''; }} guestPhone = e.currentTarget.value;
class={guestPhoneError ? 'border-red-500' : ''} }}
onblur={() => {
if (guestPhone && !isValidUKPhone(guestPhone))
guestPhoneError = 'Invalid UK phone number';
else guestPhoneError = '';
}}
/> />
{#if guestPhoneError} {#if guestPhoneError}
<p class="mt-1 text-xs text-red-600">{guestPhoneError}</p> <p class="mt-1 text-xs text-red-600">{guestPhoneError}</p>
@@ -625,12 +625,19 @@
<input <input
id="guest-phone" id="guest-phone"
type="tel" type="tel"
class="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none" class="flex h-10 w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none {guestPhoneError
placeholder="07700 900000 (optional — helps us reach you if running late)" ? 'border-red-500'
: 'border-input'}"
placeholder="07700 900000"
value={guestPhone} value={guestPhone}
oninput={(e) => { guestPhone = e.currentTarget.value; }} oninput={(e) => {
onblur={() => { guestPhoneError = guestPhone && !isValidUKPhone(guestPhone) ? 'Invalid UK phone number' : ''; }} guestPhone = e.currentTarget.value;
class={guestPhoneError ? 'border-red-500' : ''} }}
onblur={() => {
if (guestPhone && !isValidUKPhone(guestPhone))
guestPhoneError = 'Invalid UK phone number';
else guestPhoneError = '';
}}
/> />
{#if guestPhoneError} {#if guestPhoneError}
<p class="mt-1 text-xs text-red-600">{guestPhoneError}</p> <p class="mt-1 text-xs text-red-600">{guestPhoneError}</p>
+7 -37
View File
@@ -6,7 +6,7 @@
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import UserBookingModal from '$lib/components/account/UserBookingModal.svelte'; import UserBookingModal from '$lib/components/account/UserBookingModal.svelte';
import { isValidUKPhone, formatPhoneDisplay, toE164UK } from '$lib/utils/phone'; import { isValidUKPhone, formatPhoneDisplay, toE164UK } from '$lib/utils/phone';
import { savedCardsStore } from '$lib/stores/savedCards.svelte'; import { savedCardsStore, type SavedCard } from '$lib/stores/savedCards.svelte';
// zxcvbn-ts imports // zxcvbn-ts imports
import { zxcvbn, zxcvbnOptions } from '@zxcvbn-ts/core'; import { zxcvbn, zxcvbnOptions } from '@zxcvbn-ts/core';
@@ -120,38 +120,8 @@
let notifPrefs = $state({ emailEnabled: true, smsEnabled: true, browserPushEnabled: true }); let notifPrefs = $state({ emailEnabled: true, smsEnabled: true, browserPushEnabled: true });
type SavedCard = {
id: string;
brand: string;
last_4: string;
exp_month: number;
exp_year: number;
is_default: boolean;
};
let savedCards = $state<SavedCard[]>([]);
let loadingCards = $state(false); let loadingCards = $state(false);
async function fetchSavedCards() {
loadingCards = true;
try {
const res = await fetch('/api/user/payment-methods', {
headers: { Authorization: `Bearer ${authStore.currentToken}` }
});
if (res.ok) {
savedCards = await res.json();
if (savedCards.length > 0 && !buySelectedCard) {
const defaultCard = savedCards.find((c) => c.is_default) || savedCards[0];
buySelectedCard = defaultCard.id;
}
}
} catch {
toast.error('Failed to load saved cards');
} finally {
loadingCards = false;
}
}
let cardToDelete = $state<SavedCard | null>(null); let cardToDelete = $state<SavedCard | null>(null);
let showDeleteCardDialog = $state(false); let showDeleteCardDialog = $state(false);
@@ -163,7 +133,7 @@
}); });
if (res.ok) { if (res.ok) {
toast.success('Card removed'); toast.success('Card removed');
savedCards = savedCards.filter((c) => c.id !== card.id); savedCardsStore = savedCardsStore.filter((c) => c.id !== card.id);
} else { } else {
toast.error('Failed to remove card'); toast.error('Failed to remove card');
} }
@@ -444,7 +414,7 @@
} }
$effect(() => { $effect(() => {
if (savedCards.length === 0 && buySelectedCard !== '') { if (savedCardsStore.length === 0 && buySelectedCard !== '') {
buySelectedCard = ''; buySelectedCard = '';
} }
}); });
@@ -1834,14 +1804,14 @@
</Button> </Button>
</div> </div>
</div> </div>
{:else if savedCards.length === 0} {:else if savedCardsStore.length === 0}
<div class="py-8 text-center"> <div class="py-8 text-center">
<p class="text-gray-500">No saved cards yet</p> <p class="text-gray-500">No saved cards yet</p>
<Button class="mt-4" onclick={() => (showAddCard = true)}>Add a Card</Button> <Button class="mt-4" onclick={() => (showAddCard = true)}>Add a Card</Button>
</div> </div>
{:else} {:else}
<div class="space-y-3"> <div class="space-y-3">
{#each savedCards as card (card.id)} {#each savedCardsStore as card (card.id)}
<div class="flex items-center justify-between rounded-lg border p-4"> <div class="flex items-center justify-between rounded-lg border p-4">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div <div
@@ -2075,9 +2045,9 @@
<span class="block text-xs font-semibold tracking-wider text-gray-500 uppercase" <span class="block text-xs font-semibold tracking-wider text-gray-500 uppercase"
>Payment Method</span >Payment Method</span
> >
{#if savedCards.length > 0} {#if savedCardsStore.length > 0}
<div class="space-y-2"> <div class="space-y-2">
{#each savedCards as card (card.id)} {#each savedCardsStore as card (card.id)}
<button <button
type="button" type="button"
class="flex w-full items-center justify-between rounded-lg border p-3 text-left {buySelectedCard === class="flex w-full items-center justify-between rounded-lg border p-3 text-left {buySelectedCard ===