106 lines
2.6 KiB
Svelte
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>
|