Files
domain-wall/apps/frontend/src/lib/domains/checkout/payment-info-section/payment-form.svelte
2025-10-21 16:07:17 +03:00

106 lines
2.6 KiB
Svelte

<script lang="ts">
import LabelWrapper from "$lib/components/atoms/label-wrapper.svelte";
import Input from "$lib/components/ui/input/input.svelte";
import { paymentInfoVM } from "./payment.info.vm.svelte";
function formatCardNumberForDisplay(value: string) {
// return in format "XXXX XXXX XXXX XXXX" from "XXXXXXXXXXXXXXXX"
const numbers = value.replace(/\D/g, "");
if (numbers.length > 4) {
return `${numbers.slice(0, 4)} ${numbers.slice(4, 8)} ${numbers.slice(
8,
12,
)} ${numbers.slice(12, numbers.length)}`;
}
return numbers.slice(0, 19);
}
function cleanupCardNo(value: string) {
return value.replace(/\D/g, "").slice(0, 16);
}
function formatExpiryDate(value: string) {
const numbers = value.replace(/\D/g, "");
if (numbers.length > 2) {
return `${numbers.slice(0, 2)}/${numbers.slice(2, 4)}`;
}
return numbers;
}
function formatCVV(value: string) {
return value.replace(/\D/g, "").slice(0, 4);
}
let validationTimeout = $state(undefined as undefined | NodeJS.Timer);
function debounceValidate() {
if (validationTimeout) {
clearTimeout(validationTimeout);
}
validationTimeout = setTimeout(() => {
paymentInfoVM.validateAndSubmit();
}, 500);
}
</script>
<form class="flex flex-col gap-4">
<LabelWrapper
label="Name on Card"
error={paymentInfoVM.errors.cardholderName}
>
<Input
type="text"
placeholder="John Doe"
bind:value={paymentInfoVM.cardDetails.cardholderName}
oninput={() => debounceValidate()}
/>
</LabelWrapper>
<LabelWrapper label="Card Number" error={paymentInfoVM.errors.cardNumber}>
<Input
type="text"
placeholder="1234 5678 9012 3456"
maxlength={19}
value={formatCardNumberForDisplay(
paymentInfoVM.cardDetails.cardNumber,
)}
oninput={(e) => {
paymentInfoVM.cardDetails.cardNumber = cleanupCardNo(
e.currentTarget.value,
);
debounceValidate();
}}
/>
</LabelWrapper>
<div class="grid grid-cols-2 gap-4">
<LabelWrapper label="Expiry Date" error={paymentInfoVM.errors.expiry}>
<Input
type="text"
placeholder="MM/YY"
bind:value={paymentInfoVM.cardDetails.expiry}
oninput={(e) => {
paymentInfoVM.cardDetails.expiry = formatExpiryDate(
e.currentTarget.value,
);
debounceValidate();
}}
/>
</LabelWrapper>
<LabelWrapper label="CVV" error={paymentInfoVM.errors.cvv}>
<Input
type="text"
placeholder="123"
bind:value={paymentInfoVM.cardDetails.cvv}
oninput={(e) => {
paymentInfoVM.cardDetails.cvv = formatCVV(
e.currentTarget.value,
);
debounceValidate();
}}
/>
</LabelWrapper>
</div>
</form>