✅ admin side for now | 🔄 started FE
This commit is contained in:
@@ -0,0 +1,322 @@
|
||||
<script lang="ts">
|
||||
import LabelWrapper from "$lib/components/atoms/label-wrapper.svelte";
|
||||
import Title from "$lib/components/atoms/title.svelte";
|
||||
import Input from "$lib/components/ui/input/input.svelte";
|
||||
import * as Select from "$lib/components/ui/select";
|
||||
import { COUNTRIES_SELECT } from "$lib/core/countries";
|
||||
import type { SelectOption } from "$lib/core/data.types";
|
||||
import { capitalize } from "$lib/core/string.utils";
|
||||
import type { CustomerInfo } from "$lib/domains/ticket/data/entities/create.entities";
|
||||
import { Gender } from "$lib/domains/ticket/data/entities/index";
|
||||
import { PHONE_COUNTRY_CODES } from "@pkg/logic/core/data/phonecc";
|
||||
|
||||
let { info = $bindable(), idx }: { info: CustomerInfo; idx: number } =
|
||||
$props();
|
||||
|
||||
const genderOpts = [
|
||||
{ label: capitalize(Gender.Male), value: Gender.Male },
|
||||
{ label: capitalize(Gender.Female), value: Gender.Female },
|
||||
{ label: capitalize(Gender.Other), value: Gender.Other },
|
||||
] as SelectOption[];
|
||||
|
||||
function onSubmit(e: SubmitEvent) {
|
||||
e.preventDefault();
|
||||
passengerInfoVM.validatePII(info, idx);
|
||||
}
|
||||
|
||||
let validationTimeout = $state(undefined as undefined | NodeJS.Timer);
|
||||
|
||||
function debounceValidate() {
|
||||
if (validationTimeout) {
|
||||
clearTimeout(validationTimeout);
|
||||
}
|
||||
validationTimeout = setTimeout(() => {
|
||||
passengerInfoVM.validatePII(info, idx);
|
||||
}, 500);
|
||||
}
|
||||
</script>
|
||||
|
||||
<form action="#" class="flex w-full flex-col gap-4" onsubmit={onSubmit}>
|
||||
<div class="flex flex-col gap-4 md:flex-row">
|
||||
<LabelWrapper
|
||||
label="First Name"
|
||||
error={passengerInfoVM.piiErrors[idx].firstName}
|
||||
>
|
||||
<Input
|
||||
placeholder="First Name"
|
||||
bind:value={info.firstName}
|
||||
required
|
||||
oninput={() => debounceValidate()}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
|
||||
<LabelWrapper
|
||||
label="Middle Name"
|
||||
error={passengerInfoVM.piiErrors[idx].middleName}
|
||||
>
|
||||
<Input
|
||||
placeholder="Middle Name"
|
||||
bind:value={info.middleName}
|
||||
oninput={() => debounceValidate()}
|
||||
required
|
||||
/>
|
||||
</LabelWrapper>
|
||||
|
||||
<LabelWrapper
|
||||
label="Last Name"
|
||||
error={passengerInfoVM.piiErrors[idx].lastName}
|
||||
>
|
||||
<Input
|
||||
placeholder="Last Name"
|
||||
bind:value={info.lastName}
|
||||
required
|
||||
oninput={() => debounceValidate()}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
</div>
|
||||
|
||||
<LabelWrapper label="Email" error={passengerInfoVM.piiErrors[idx].email}>
|
||||
<Input
|
||||
placeholder="Email"
|
||||
bind:value={info.email}
|
||||
type="email"
|
||||
oninput={() => debounceValidate()}
|
||||
required
|
||||
/>
|
||||
</LabelWrapper>
|
||||
|
||||
<div class="flex flex-col gap-4 md:flex-row lg:flex-col xl:flex-row">
|
||||
<LabelWrapper
|
||||
label="Phone Number"
|
||||
error={passengerInfoVM.piiErrors[idx].phoneNumber}
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<Select.Root
|
||||
type="single"
|
||||
required
|
||||
onValueChange={(code) => {
|
||||
info.phoneCountryCode = code;
|
||||
}}
|
||||
name="phoneCode"
|
||||
>
|
||||
<Select.Trigger class="w-20">
|
||||
{#if info.phoneCountryCode}
|
||||
{info.phoneCountryCode}
|
||||
{:else}
|
||||
Select
|
||||
{/if}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each PHONE_COUNTRY_CODES as { country, phoneCode }}
|
||||
<Select.Item value={phoneCode}>
|
||||
<span class="flex items-center gap-2">
|
||||
{phoneCode} ({country})
|
||||
</span>
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
|
||||
<Input
|
||||
placeholder="Phone Number"
|
||||
type="tel"
|
||||
bind:value={info.phoneNumber}
|
||||
required
|
||||
oninput={() => debounceValidate()}
|
||||
class="flex-1"
|
||||
/>
|
||||
</div>
|
||||
</LabelWrapper>
|
||||
|
||||
<LabelWrapper
|
||||
label="Passport Expiry"
|
||||
error={passengerInfoVM.piiErrors[idx].passportExpiry}
|
||||
>
|
||||
<Input
|
||||
placeholder="Passport Expiry"
|
||||
value={info.passportExpiry}
|
||||
type="date"
|
||||
required
|
||||
oninput={(v) => {
|
||||
// @ts-ignore
|
||||
info.passportExpiry = v.target.value;
|
||||
debounceValidate();
|
||||
}}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
<LabelWrapper
|
||||
label="Passport/ID No"
|
||||
error={passengerInfoVM.piiErrors[idx].passportNo}
|
||||
>
|
||||
<Input
|
||||
placeholder="Passport or ID card no."
|
||||
bind:value={info.passportNo}
|
||||
minlength={1}
|
||||
maxlength={20}
|
||||
required
|
||||
oninput={() => debounceValidate()}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-4 md:flex-row">
|
||||
<LabelWrapper
|
||||
label="Nationality"
|
||||
error={passengerInfoVM.piiErrors[idx].nationality}
|
||||
>
|
||||
<Select.Root
|
||||
type="single"
|
||||
required
|
||||
onValueChange={(e) => {
|
||||
info.nationality = e;
|
||||
debounceValidate();
|
||||
}}
|
||||
name="role"
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
{capitalize(
|
||||
info.nationality.length > 0 ? info.nationality : "Select",
|
||||
)}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each COUNTRIES_SELECT as country}
|
||||
<Select.Item value={country.value}>
|
||||
{country.label}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</LabelWrapper>
|
||||
<LabelWrapper
|
||||
label="Gender"
|
||||
error={passengerInfoVM.piiErrors[idx].gender}
|
||||
>
|
||||
<Select.Root
|
||||
type="single"
|
||||
required
|
||||
onValueChange={(e) => {
|
||||
info.gender = e as Gender;
|
||||
debounceValidate();
|
||||
}}
|
||||
name="role"
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
{capitalize(info.gender.length > 0 ? info.gender : "Select")}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each genderOpts as gender}
|
||||
<Select.Item value={gender.value}>
|
||||
{gender.label}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</LabelWrapper>
|
||||
|
||||
<LabelWrapper
|
||||
label="Date of Birth"
|
||||
error={passengerInfoVM.piiErrors[idx].dob}
|
||||
>
|
||||
<Input
|
||||
placeholder="Date of Birth"
|
||||
bind:value={info.dob}
|
||||
type="date"
|
||||
required
|
||||
oninput={() => debounceValidate()}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
</div>
|
||||
|
||||
<!-- and now for the address info - country, state, city, zip code, address and address 2 -->
|
||||
|
||||
<Title size="h5">Address Info</Title>
|
||||
|
||||
<div class="flex flex-col gap-4 md:flex-row">
|
||||
<LabelWrapper
|
||||
label="Country"
|
||||
error={passengerInfoVM.piiErrors[idx].country}
|
||||
>
|
||||
<Select.Root
|
||||
type="single"
|
||||
required
|
||||
onValueChange={(e) => {
|
||||
info.country = e;
|
||||
debounceValidate();
|
||||
}}
|
||||
name="role"
|
||||
>
|
||||
<Select.Trigger class="w-full">
|
||||
{capitalize(
|
||||
info.country.length > 0 ? info.country : "Select",
|
||||
)}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each COUNTRIES_SELECT as country}
|
||||
<Select.Item value={country.value}>
|
||||
{country.label}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</LabelWrapper>
|
||||
|
||||
<LabelWrapper label="State" error={passengerInfoVM.piiErrors[idx].state}>
|
||||
<Input
|
||||
placeholder="State"
|
||||
bind:value={info.state}
|
||||
required
|
||||
oninput={() => debounceValidate()}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
</div>
|
||||
<div class="flex flex-col gap-4 md:flex-row">
|
||||
<LabelWrapper label="City" error={passengerInfoVM.piiErrors[idx].city}>
|
||||
<Input
|
||||
placeholder="City"
|
||||
bind:value={info.city}
|
||||
required
|
||||
minlength={1}
|
||||
maxlength={80}
|
||||
oninput={() => debounceValidate()}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
|
||||
<LabelWrapper
|
||||
label="Zip Code"
|
||||
error={passengerInfoVM.piiErrors[idx].zipCode}
|
||||
>
|
||||
<Input
|
||||
placeholder="Zip Code"
|
||||
bind:value={info.zipCode}
|
||||
required
|
||||
minlength={1}
|
||||
oninput={() => debounceValidate()}
|
||||
maxlength={12}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
</div>
|
||||
|
||||
<LabelWrapper label="Address" error={passengerInfoVM.piiErrors[idx].address}>
|
||||
<Input
|
||||
placeholder="Address"
|
||||
bind:value={info.address}
|
||||
required
|
||||
minlength={1}
|
||||
maxlength={128}
|
||||
oninput={() => debounceValidate()}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
|
||||
<LabelWrapper
|
||||
label="Address 2"
|
||||
error={passengerInfoVM.piiErrors[idx].address2}
|
||||
>
|
||||
<Input
|
||||
placeholder="Address 2"
|
||||
bind:value={info.address2}
|
||||
required
|
||||
minlength={1}
|
||||
maxlength={128}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
</form>
|
||||
Reference in New Issue
Block a user