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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user