Skip to content

Commit 7961e16

Browse files
committed
add AddressForm
1 parent d275706 commit 7961e16

File tree

5 files changed

+219
-1
lines changed

5 files changed

+219
-1
lines changed
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import { component$, useSignal, useStore, useTask$ } from '@builder.io/qwik';
2+
import { Form } from '@builder.io/qwik-city';
3+
import {
4+
SfButton,
5+
SfCheckbox,
6+
SfInput,
7+
SfLoaderCircular,
8+
SfSelect,
9+
} from 'qwik-storefront-ui';
10+
import { useAddressForm } from '~/routes/layout';
11+
import { useTranslation } from '~/shared/utils';
12+
import FormHelperText from '../Form/FormHelperText';
13+
import FormLabel from '../Form/FormLabel';
14+
import type { AddressFormFields, AddressFormProps } from './types';
15+
16+
export default component$<AddressFormProps>(({ type, savedAddress }) => {
17+
const addressFormAction = useAddressForm();
18+
const { t } = useTranslation('address');
19+
const formRefSig = useSignal<HTMLFormElement>();
20+
useStore<{ value: AddressFormFields }>({
21+
// const addressStore =
22+
value: {
23+
firstName: savedAddress?.firstName || '',
24+
lastName: savedAddress?.lastName || '',
25+
phone: savedAddress?.phone || '',
26+
country: savedAddress?.country || '',
27+
streetName: savedAddress?.streetName || '',
28+
streetNumber: savedAddress?.streetNumber || '',
29+
city: savedAddress?.city || '',
30+
state: savedAddress?.state || '',
31+
postalCode: savedAddress?.postalCode || '',
32+
},
33+
});
34+
const countries = ['US'];
35+
const states = ['California'];
36+
37+
useTask$(({ track }) => {
38+
const success = track(() => addressFormAction.value?.success);
39+
if (success) {
40+
if (formRefSig.value) {
41+
formRefSig.value.reset();
42+
}
43+
}
44+
});
45+
46+
return (
47+
<Form
48+
class='grid grid-cols-1 md:grid-cols-[50%_1fr_120px] gap-4'
49+
data-testid='address-form'
50+
action={addressFormAction}
51+
ref={formRefSig}
52+
>
53+
<label>
54+
<FormLabel>{t('form.firstNameLabel')}</FormLabel>
55+
<SfInput
56+
name='firstName'
57+
autoComplete='given-name'
58+
// defaultValue={addressStore.firstName} <-- fix lib <-- fix lib
59+
required
60+
/>
61+
</label>
62+
<label class='md:col-span-2'>
63+
<FormLabel>{t('form.lastNameLabel')}</FormLabel>
64+
<SfInput
65+
name='lastName'
66+
autoComplete='family-name'
67+
// defaultValue={addressStore.lastName} <-- fix lib
68+
required
69+
/>
70+
</label>
71+
<label class='md:col-span-3'>
72+
<FormLabel>{t('form.phoneLabel')}</FormLabel>
73+
<SfInput
74+
name='phone'
75+
type='tel'
76+
autoComplete='tel'
77+
// defaultValue={addressStore.phone} <-- fix lib
78+
required
79+
/>
80+
</label>
81+
<label class='md:col-span-3'>
82+
<FormLabel>{t('form.countryLabel')}</FormLabel>
83+
<SfSelect
84+
name='country'
85+
placeholder={t('form.selectPlaceholder')}
86+
autoComplete='country-name'
87+
// defaultValue={addressStore.country} <-- fix lib
88+
required
89+
>
90+
{countries.map((country) => (
91+
<option key={country}>{country}</option>
92+
))}
93+
</SfSelect>
94+
</label>
95+
<label class='md:col-span-2'>
96+
<FormLabel>{t('form.streetNameLabel')}</FormLabel>
97+
<SfInput
98+
name='streetName'
99+
autoComplete='address-line1'
100+
// defaultValue={// addressStore.streetName} <-- fix lib
101+
required
102+
/>
103+
<FormHelperText>{t('form.streetNameHelp')}</FormHelperText>
104+
</label>
105+
<label>
106+
<FormLabel>{t('form.streetNumberLabel')}</FormLabel>
107+
<SfInput
108+
name='streetNumber'
109+
// defaultValue={// addressStore.streetNumber} <-- fix lib
110+
/>
111+
<FormHelperText>{t('form.streetNumberHelp')}</FormHelperText>
112+
</label>
113+
<label class='md:col-span-3'>
114+
<FormLabel>{t('form.cityLabel')}</FormLabel>
115+
<SfInput
116+
name='city'
117+
autoComplete='address-level2'
118+
// defaultValue={// addressStore.city} <-- fix lib
119+
required
120+
/>
121+
</label>
122+
<label class='md:col-span-2'>
123+
<FormLabel>{t('form.stateLabel')}</FormLabel>
124+
<SfSelect
125+
name='state'
126+
autoComplete='address-level1'
127+
// defaultValue={// addressStore.state} <-- fix lib
128+
placeholder={t('form.selectPlaceholder')}
129+
required
130+
>
131+
{states.map((state) => (
132+
<option key={state}>{state}</option>
133+
))}
134+
</SfSelect>
135+
</label>
136+
<label>
137+
<FormLabel>{t('form.postalCodeLabel')}</FormLabel>
138+
<SfInput
139+
name='postalCode'
140+
autoComplete='postal-code'
141+
// defaultValue={// addressStore.postalCode} <-- fix lib
142+
required
143+
/>
144+
</label>
145+
146+
{type === 'billingAddress' && (
147+
<label class='md:col-span-3 flex items-center gap-2'>
148+
<SfCheckbox name='useAsShipping' />
149+
{t('form.useAsShippingLabel')}
150+
</label>
151+
)}
152+
153+
<div class='md:col-span-3 flex justify-end gap-4'>
154+
<SfButton
155+
type='reset'
156+
onClick$={() => {
157+
if (formRefSig.value) {
158+
formRefSig.value.reset();
159+
}
160+
}}
161+
class='max-md:w-1/2'
162+
variant='secondary'
163+
>
164+
{t('checkout:contactInfo.clearAll')}
165+
</SfButton>
166+
<SfButton
167+
type='submit'
168+
class='w-1/2 md:w-1/6'
169+
disabled={addressFormAction.isRunning}
170+
>
171+
{addressFormAction.isRunning ? (
172+
<SfLoaderCircular
173+
class='flex justify-center items-center'
174+
size='sm'
175+
/>
176+
) : (
177+
t('checkout:contactInfo.save')
178+
)}
179+
</SfButton>
180+
</div>
181+
</Form>
182+
);
183+
});

src/components/AddressForm/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './AddressForm';
2+
export * from './types';

src/components/AddressForm/types.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
type AddressFields =
2+
| 'firstName'
3+
| 'lastName'
4+
| 'phone'
5+
| 'country'
6+
| 'streetName'
7+
| 'streetNumber'
8+
| 'city'
9+
| 'state'
10+
| 'postalCode';
11+
12+
export type AddressFormFields = Record<AddressFields, string>;
13+
14+
export type AddressFormProps = {
15+
type: 'billingAddress' | 'shippingAddress';
16+
savedAddress?: AddressFormFields | undefined;
17+
};

src/routes/layout.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { component$, Slot } from '@builder.io/qwik';
2-
import type { RequestHandler } from '@builder.io/qwik-city';
2+
import { routeAction$, type RequestHandler } from '@builder.io/qwik-city';
33
import { SfButton, SfIconExpandMore } from 'qwik-storefront-ui';
44
import { NavbarTop } from '~/components/navbar-top/navbar-top';
5+
import { sleep } from '~/shared/utils';
56

67
export const onGet: RequestHandler = async ({ cacheControl }) => {
78
// Control caching for this request for best performance and to reduce hosting costs:
@@ -14,6 +15,11 @@ export const onGet: RequestHandler = async ({ cacheControl }) => {
1415
});
1516
};
1617

18+
export const useAddressForm = routeAction$(async () => {
19+
await sleep(5_000);
20+
return { success: true };
21+
});
22+
1723
export default component$(() => {
1824
return (
1925
<>

src/shared/utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const useTranslation = (fileName: string) => {
2+
console.log(fileName);
3+
return { t: (label: string) => label };
4+
};
5+
6+
export const sleep = (timeout: number) => {
7+
return new Promise((resolve) => {
8+
setTimeout(resolve, timeout);
9+
});
10+
};

0 commit comments

Comments
 (0)