chore: apply project-wide formatting and UI library updates

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-05-29 20:23:14 +01:00
co-authored by Sisyphus
parent ef650e013a
commit 6e901690fd
131 changed files with 2437 additions and 1571 deletions
@@ -92,32 +92,31 @@
let expiryParts = $derived(parseExpiryParts(newCardExpiry));
let isExpiryInPast = $derived(
expiryParts !== null && (() => {
const expiryDate = new SvelteDate(expiryParts.year, expiryParts.month);
return expiryDate < new SvelteDate();
})()
expiryParts !== null &&
(() => {
const expiryDate = new SvelteDate(expiryParts.year, expiryParts.month);
return expiryDate < new SvelteDate();
})()
);
let hasInvalidMonth = $derived(
/^\d{2}\/\d{2}$/.test(newCardExpiry) && expiryParts === null
);
let hasInvalidMonth = $derived(/^\d{2}\/\d{2}$/.test(newCardExpiry) && expiryParts === null);
let cardFormValid = $derived(
newCardNumber.replace(/\s/g, '').length >= 13 &&
expiryParts !== null &&
newCardCVC.length >= 3 &&
!isExpiryInPast
expiryParts !== null &&
newCardCVC.length >= 3 &&
!isExpiryInPast
);
let cardSelected = $derived(
(selectedPaymentMethod !== null && paymentMethods.length > 0) ||
(showNewCardForm && cardFormValid) ||
(paymentMethods.length === 0 && cardFormValid)
(showNewCardForm && cardFormValid) ||
(paymentMethods.length === 0 && cardFormValid)
);
let cardValidationError = $derived(
!cardSelected ? (
selectedPaymentMethod === null && paymentMethods.length > 0 && !showNewCardForm
!cardSelected
? selectedPaymentMethod === null && paymentMethods.length > 0 && !showNewCardForm
? 'Please select a card'
: newCardNumber.replace(/\s/g, '').length < 13 && newCardNumber.length > 0
? 'Card number too short'
@@ -132,7 +131,7 @@
: paymentMethods.length === 0 && !showNewCardForm && newCardNumber.length === 0
? 'Please enter card details'
: 'Please complete all card fields'
) : null
: null
);
// Partial payment amount (in pounds, user enters)
@@ -143,9 +142,7 @@
let pollingInterval: ReturnType<typeof setInterval> | null = null;
// Derived values
let depositOutstanding = $derived(
booking.deposit_required && !booking.deposit_paid
);
let depositOutstanding = $derived(booking.deposit_required && !booking.deposit_paid);
let totalPaid = $derived(
booking.payments
@@ -157,23 +154,21 @@
let isScenarioA = $derived(depositOutstanding);
let isScenarioB = $derived(
!depositOutstanding && totalPaid < booking.total_amount
);
let isScenarioB = $derived(!depositOutstanding && totalPaid < booking.total_amount);
let partialAmountNum = $derived(partialAmount === '' ? 0 : parseFloat(partialAmount));
let partialAmountValid = $derived(
paymentType === 'partial' &&
partialAmount !== '' &&
!isNaN(partialAmountNum) &&
partialAmountNum > 0 &&
partialAmountNum <= amountRemaining &&
/^\d+(\.\d{0,2})?$/.test(partialAmount)
partialAmount !== '' &&
!isNaN(partialAmountNum) &&
partialAmountNum > 0 &&
partialAmountNum <= amountRemaining &&
/^\d+(\.\d{0,2})?$/.test(partialAmount)
);
let partialValidationError = $derived(
paymentType === 'partial' && !partialAmountValid ? (
partialAmount === ''
paymentType === 'partial' && !partialAmountValid
? partialAmount === ''
? 'Enter an amount'
: !/^\d+(\.\d{0,2})?$/.test(partialAmount)
? 'Invalid amount format'
@@ -182,7 +177,7 @@
: partialAmountNum > amountRemaining
? 'Amount exceeds balance'
: 'Invalid amount'
) : null
: null
);
let payButtonDisabled = $derived(
@@ -190,9 +185,13 @@
);
let payButtonError = $derived(
status === 'processing' ? null : (
!cardSelected ? cardValidationError : (paymentType === 'partial' ? partialValidationError : null)
)
status === 'processing'
? null
: !cardSelected
? cardValidationError
: paymentType === 'partial'
? partialValidationError
: null
);
function formatCurrency(pence: number): string {
@@ -388,7 +387,7 @@
</script>
<Dialog.Root open={true} onOpenChange={(open) => !open && handleClose()}>
<Dialog.Content class="max-w-md max-h-[90vh] overflow-y-auto">
<Dialog.Content class="max-h-[90vh] max-w-md overflow-y-auto">
<Dialog.Header>
<Dialog.Title class="text-xl font-semibold">Make a Payment</Dialog.Title>
{#if booking.id}
@@ -406,7 +405,11 @@
<div class="flex justify-between text-sm">
<span class="text-gray-600">{service.service_name || 'Unknown Service'}</span>
<span class="font-medium">
{service.override_price ? formatCurrency(Math.round(service.override_price * 100)) : service.price ? formatCurrency(Math.round(service.price * 100)) : '-'}
{service.override_price
? formatCurrency(Math.round(service.override_price * 100))
: service.price
? formatCurrency(Math.round(service.price * 100))
: '-'}
</span>
</div>
{/each}
@@ -417,7 +420,8 @@
<div class="space-y-2 rounded-md border border-gray-200 bg-white p-4">
<div class="flex justify-between text-sm">
<span class="text-gray-600">Total</span>
<span class="font-medium">{formatCurrency(Math.round(booking.total_amount * 100))}</span>
<span class="font-medium">{formatCurrency(Math.round(booking.total_amount * 100))}</span
>
</div>
<div class="flex justify-between text-sm">
<span class="text-gray-600">Amount Paid</span>
@@ -441,9 +445,13 @@
{#if selectedPaymentMethod}
{#each paymentMethods as method (method.id)}
{#if method.id === selectedPaymentMethod}
<div class="flex items-center justify-between rounded-lg border border-input bg-fuchsia-100 p-3">
<div
class="flex items-center justify-between rounded-lg border border-input bg-fuchsia-100 p-3"
>
<div class="flex items-center gap-3">
<div class="flex h-10 min-w-14 items-center justify-center rounded bg-gray-100 px-2 text-xs font-medium">
<div
class="flex h-10 min-w-14 items-center justify-center rounded bg-gray-100 px-2 text-xs font-medium"
>
{method.brand}
</div>
<div class="text-sm">
@@ -474,7 +482,7 @@
{#if canSaveCards}
<button
type="button"
class="flex w-full items-center justify-between rounded-lg border p-3 hover:border-gray-300 transition-colors"
class="flex w-full items-center justify-between rounded-lg border p-3 transition-colors hover:border-gray-300"
onclick={() => {
showNewCardForm = true;
showCardList = false;
@@ -482,7 +490,13 @@
}}
>
<div class="text-sm font-medium text-gray-700">Enter card details</div>
<svg class="h-4 w-4 text-gray-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<svg
class="h-4 w-4 text-gray-400"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M9 18l6-6-6-6" />
</svg>
</button>
@@ -490,7 +504,10 @@
{#each paymentMethods as method (method.id)}
<button
type="button"
class="flex w-full items-center justify-between rounded-lg border p-3 {selectedPaymentMethod === method.id ? 'border-input bg-fuchsia-100' : 'border-input hover:bg-fuchsia-50'}"
class="flex w-full items-center justify-between rounded-lg border p-3 {selectedPaymentMethod ===
method.id
? 'border-input bg-fuchsia-100'
: 'border-input hover:bg-fuchsia-50'}"
onclick={() => {
selectedPaymentMethod = method.id;
showNewCardForm = false;
@@ -498,7 +515,9 @@
}}
>
<div class="flex items-center gap-3">
<div class="flex h-10 min-w-14 items-center justify-center rounded bg-gray-100 px-2 text-xs font-medium">
<div
class="flex h-10 min-w-14 items-center justify-center rounded bg-gray-100 px-2 text-xs font-medium"
>
{method.brand}
</div>
<div class="text-sm">
@@ -522,7 +541,9 @@
<h4 class="mb-3 text-sm font-medium text-gray-700">Card Details</h4>
<div class="space-y-3">
<div>
<label for="cardNumber" class="text-sm font-medium text-gray-700">Card Number</label>
<label for="cardNumber" class="text-sm font-medium text-gray-700"
>Card Number</label
>
<Input
id="cardNumber"
type="text"
@@ -536,7 +557,9 @@
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label for="cardExpiry" class="text-sm font-medium text-gray-700">Expiry (MM/YY)</label>
<label for="cardExpiry" class="text-sm font-medium text-gray-700"
>Expiry (MM/YY)</label
>
<Input
id="cardExpiry"
type="text"
@@ -582,14 +605,20 @@
<div class="grid grid-cols-2 gap-3">
<button
type="button"
class="rounded-lg border py-3 text-center text-sm font-semibold transition-colors {paymentType === 'deposit' ? 'border-input bg-fuchsia-100 text-foreground' : 'border-input hover:bg-fuchsia-50'}"
class="rounded-lg border py-3 text-center text-sm font-semibold transition-colors {paymentType ===
'deposit'
? 'border-input bg-fuchsia-100 text-foreground'
: 'border-input hover:bg-fuchsia-50'}"
onclick={() => (paymentType = 'deposit')}
>
Pay Deposit
</button>
<button
type="button"
class="rounded-lg border py-3 text-center text-sm font-semibold transition-colors {paymentType === 'full' ? 'border-input bg-fuchsia-100 text-foreground' : 'border-input hover:bg-fuchsia-50'}"
class="rounded-lg border py-3 text-center text-sm font-semibold transition-colors {paymentType ===
'full'
? 'border-input bg-fuchsia-100 text-foreground'
: 'border-input hover:bg-fuchsia-50'}"
onclick={() => (paymentType = 'full')}
>
Pay in Full
@@ -601,13 +630,17 @@
<p class="text-sm text-red-600">{payButtonError}</p>
{/if}
<Button
onclick={() => paymentType === 'deposit' ? handlePayDeposit() : handlePayFull()}
onclick={() => (paymentType === 'deposit' ? handlePayDeposit() : handlePayFull())}
class="w-full"
loading={status === 'processing'}
disabled={payButtonDisabled}
>
{#if paymentType === 'deposit'}
Pay Deposit ({formatCurrency(booking.deposit_amount ? Math.round(booking.deposit_amount * 100) : Math.round(booking.total_amount * 0.2 * 100))})
Pay Deposit ({formatCurrency(
booking.deposit_amount
? Math.round(booking.deposit_amount * 100)
: Math.round(booking.total_amount * 0.2 * 100)
)})
{:else}
Pay {formatCurrency(Math.round(booking.amount_due * 100))}
{/if}
@@ -622,14 +655,20 @@
<div class="grid grid-cols-2 gap-3">
<button
type="button"
class="rounded-lg border py-3 text-center text-sm font-semibold transition-colors {paymentType === 'full' ? 'border-input bg-fuchsia-100 text-foreground' : 'border-input hover:bg-fuchsia-50'}"
class="rounded-lg border py-3 text-center text-sm font-semibold transition-colors {paymentType ===
'full'
? 'border-input bg-fuchsia-100 text-foreground'
: 'border-input hover:bg-fuchsia-50'}"
onclick={() => (paymentType = 'full')}
>
Pay in Full
</button>
<button
type="button"
class="rounded-lg border py-3 text-center text-sm font-semibold transition-colors {paymentType === 'partial' ? 'border-input bg-fuchsia-100 text-foreground' : 'border-input hover:bg-fuchsia-50'}"
class="rounded-lg border py-3 text-center text-sm font-semibold transition-colors {paymentType ===
'partial'
? 'border-input bg-fuchsia-100 text-foreground'
: 'border-input hover:bg-fuchsia-50'}"
onclick={() => (paymentType = 'partial')}
>
Pay Part
@@ -643,7 +682,7 @@
Partial Payment Amount
</label>
<div class="relative mt-1">
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500">£</span>
<span class="absolute top-1/2 left-3 -translate-y-1/2 text-gray-500">£</span>
<Input
id="partial-amount"
type="text"
@@ -664,13 +703,15 @@
<p class="text-sm text-red-600">{payButtonError}</p>
{/if}
<Button
onclick={() => paymentType === 'partial' ? handlePayPartial() : handlePayFull()}
onclick={() => (paymentType === 'partial' ? handlePayPartial() : handlePayFull())}
class="w-full"
loading={status === 'processing'}
disabled={payButtonDisabled}
>
{#if paymentType === 'partial'}
Pay {partialAmountValid ? formatCurrency(Math.round(partialAmountNum * 100)) : 'Part'}
Pay {partialAmountValid
? formatCurrency(Math.round(partialAmountNum * 100))
: 'Part'}
{:else}
Pay {formatCurrency(Math.round(booking.amount_due * 100))}
{/if}
@@ -685,19 +726,24 @@
{/if}
<!-- Close button -->
<Button variant="ghost" onclick={handleClose} class="w-full" disabled={status === 'processing'}>
<Button
variant="ghost"
onclick={handleClose}
class="w-full"
disabled={status === 'processing'}
>
Close
</Button>
</div>
{:else if status === 'polling'}
<!-- Polling State -->
<div class="flex flex-col items-center justify-center py-8">
<div class="mb-4 h-12 w-12 animate-spin rounded-full border-4 border-gray-200 border-t-green-600"></div>
<div
class="mb-4 h-12 w-12 animate-spin rounded-full border-4 border-gray-200 border-t-green-600"
></div>
<p class="text-lg font-medium text-gray-700">Processing payment...</p>
<p class="mt-2 text-sm text-gray-500">This may take a few moments</p>
<Button variant="ghost" onclick={handleClose} class="mt-6">
Cancel
</Button>
<Button variant="ghost" onclick={handleClose} class="mt-6">Cancel</Button>
</div>
{:else if status === 'success' && paymentResult}
<!-- Success State -->
@@ -750,9 +796,7 @@
</div>
</div>
<Button onclick={handleClose} class="w-full">
Done
</Button>
<Button onclick={handleClose} class="w-full">Done</Button>
</div>
{/if}
</Dialog.Content>