Skip to content

Commit 6ac9fff

Browse files
committed
feat(core): render address fields for customer registration
1 parent 589c91a commit 6ac9fff

7 files changed

Lines changed: 260 additions & 96 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const ADDRESS_FIELDS_NAME_PREFIX = 'customAddress_';
2+
export const CUSTOMER_FIELDS_NAME_PREFIX = 'customCustomer_';

core/app/[locale]/(default)/(auth)/register/_actions/register-customer.ts

Lines changed: 176 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { FieldNameToFieldId } from '~/data-transformers/form-field-transformer/u
1414
import { redirect } from '~/i18n/routing';
1515
import { getCartId } from '~/lib/cart';
1616

17+
import { ADDRESS_FIELDS_NAME_PREFIX, CUSTOMER_FIELDS_NAME_PREFIX } from './prefixes';
18+
1719
const RegisterCustomerMutation = graphql(`
1820
mutation RegisterCustomerMutation(
1921
$input: RegisterCustomerInput!
@@ -47,10 +49,70 @@ const RegisterCustomerMutation = graphql(`
4749
const stringToNumber = z.string().pipe(z.coerce.number());
4850

4951
const inputSchema = z.object({
50-
email: z.string(),
51-
password: z.string(),
5252
firstName: z.string(),
5353
lastName: z.string(),
54+
email: z.string(),
55+
password: z.string(),
56+
phone: z.string().optional(),
57+
company: z.string().optional(),
58+
address: z
59+
.object({
60+
firstName: z.string(),
61+
lastName: z.string(),
62+
address1: z.string(),
63+
address2: z.string().optional(),
64+
city: z.string(),
65+
company: z.string().optional(),
66+
countryCode: z.string(),
67+
stateOrProvince: z.string().optional(),
68+
phone: z.string().optional(),
69+
postalCode: z.string().optional(),
70+
formFields: z.object({
71+
checkboxes: z.array(
72+
z.object({
73+
fieldEntityId: stringToNumber,
74+
fieldValueEntityIds: z.array(stringToNumber),
75+
}),
76+
),
77+
multipleChoices: z.array(
78+
z.object({
79+
fieldEntityId: stringToNumber,
80+
fieldValueEntityId: stringToNumber,
81+
}),
82+
),
83+
numbers: z.array(
84+
z.object({
85+
fieldEntityId: stringToNumber,
86+
number: stringToNumber,
87+
}),
88+
),
89+
dates: z.array(
90+
z.object({
91+
fieldEntityId: stringToNumber,
92+
date: z.string(),
93+
}),
94+
),
95+
passwords: z.array(
96+
z.object({
97+
fieldEntityId: stringToNumber,
98+
password: z.string(),
99+
}),
100+
),
101+
multilineTexts: z.array(
102+
z.object({
103+
fieldEntityId: stringToNumber,
104+
multilineText: z.string(),
105+
}),
106+
),
107+
texts: z.array(
108+
z.object({
109+
fieldEntityId: stringToNumber,
110+
text: z.string(),
111+
}),
112+
),
113+
}),
114+
})
115+
.optional(),
54116
formFields: z.object({
55117
checkboxes: z.array(
56118
z.object({
@@ -111,69 +173,162 @@ function parseRegisterCustomerInput(
111173
String(FieldNameToFieldId.confirmPassword),
112174
String(FieldNameToFieldId.firstName),
113175
String(FieldNameToFieldId.lastName),
176+
String(FieldNameToFieldId.address1),
177+
String(FieldNameToFieldId.address2),
178+
String(FieldNameToFieldId.city),
179+
String(FieldNameToFieldId.company),
180+
String(FieldNameToFieldId.countryCode),
181+
String(FieldNameToFieldId.stateOrProvince),
182+
String(FieldNameToFieldId.phone),
183+
String(FieldNameToFieldId.postalCode),
114184
].includes(field.name),
115185
);
186+
187+
const customAddressFields = customFields.filter((field) =>
188+
field.name.startsWith(ADDRESS_FIELDS_NAME_PREFIX),
189+
);
190+
const customCustomerFields = customFields.filter((field) =>
191+
field.name.startsWith(CUSTOMER_FIELDS_NAME_PREFIX),
192+
);
193+
116194
const mappedInput = {
117-
email: value[FieldNameToFieldId.email],
118-
password: value[FieldNameToFieldId.password],
119-
firstName: value[FieldNameToFieldId.firstName],
120-
lastName: value[FieldNameToFieldId.lastName],
195+
firstName: value.firstName,
196+
lastName: value.lastName,
197+
email: value.email,
198+
password: value.password,
199+
phone: value.phone,
200+
company: value.company,
201+
address: {
202+
firstName: value.firstName,
203+
lastName: value.lastName,
204+
address1: value.address1,
205+
address2: value.address2,
206+
city: value.city,
207+
company: value.company,
208+
countryCode: value.countryCode,
209+
stateOrProvince: value.stateOrProvince,
210+
phone: value.phone,
211+
postalCode: value.postalCode,
212+
formFields: {
213+
checkboxes: customAddressFields
214+
.filter((field) => ['checkbox-group'].includes(field.type))
215+
.filter((field) => Boolean(value[field.name]))
216+
.map((field) => {
217+
return {
218+
fieldEntityId: field.id,
219+
fieldValueEntityIds: Array.isArray(value[field.name])
220+
? value[field.name]
221+
: [value[field.name]],
222+
};
223+
}),
224+
multipleChoices: customAddressFields
225+
.filter((field) => ['radio-group', 'button-radio-group'].includes(field.type))
226+
.filter((field) => Boolean(value[field.name]))
227+
.map((field) => {
228+
return {
229+
fieldEntityId: field.id,
230+
fieldValueEntityId: value[field.name],
231+
};
232+
}),
233+
numbers: customAddressFields
234+
.filter((field) => ['number'].includes(field.type))
235+
.filter((field) => Boolean(value[field.name]))
236+
.map((field) => {
237+
return {
238+
fieldEntityId: field.id,
239+
number: value[field.name],
240+
};
241+
}),
242+
dates: customAddressFields
243+
.filter((field) => ['date'].includes(field.type))
244+
.filter((field) => Boolean(value[field.name]))
245+
.map((field) => {
246+
return {
247+
fieldEntityId: field.id,
248+
date: new Date(String(value[field.name])).toISOString(),
249+
};
250+
}),
251+
passwords: customAddressFields
252+
.filter((field) => ['password'].includes(field.type))
253+
.filter((field) => Boolean(value[field.name]))
254+
.map((field) => ({
255+
fieldEntityId: field.id,
256+
password: value[field.name],
257+
})),
258+
multilineTexts: customAddressFields
259+
.filter((field) => ['textarea'].includes(field.type))
260+
.filter((field) => Boolean(value[field.name]))
261+
.map((field) => ({
262+
fieldEntityId: field.id,
263+
multilineText: value[field.name],
264+
})),
265+
texts: customAddressFields
266+
.filter((field) => ['text'].includes(field.type))
267+
.filter((field) => Boolean(value[field.name]))
268+
.map((field) => ({
269+
fieldEntityId: field.id,
270+
text: value[field.name],
271+
})),
272+
},
273+
},
121274
formFields: {
122-
checkboxes: customFields
275+
checkboxes: customCustomerFields
123276
.filter((field) => ['checkbox-group'].includes(field.type))
124277
.filter((field) => Boolean(value[field.name]))
125278
.map((field) => {
126279
return {
127-
fieldEntityId: field.name,
128-
fieldValueEntityIds: value[field.name],
280+
fieldEntityId: field.id,
281+
fieldValueEntityIds: Array.isArray(value[field.name])
282+
? value[field.name]
283+
: [value[field.name]],
129284
};
130285
}),
131-
multipleChoices: customFields
286+
multipleChoices: customCustomerFields
132287
.filter((field) => ['radio-group', 'button-radio-group'].includes(field.type))
133288
.filter((field) => Boolean(value[field.name]))
134289
.map((field) => {
135290
return {
136-
fieldEntityId: field.name,
291+
fieldEntityId: field.id,
137292
fieldValueEntityId: value[field.name],
138293
};
139294
}),
140-
numbers: customFields
295+
numbers: customCustomerFields
141296
.filter((field) => ['number'].includes(field.type))
142297
.filter((field) => Boolean(value[field.name]))
143298
.map((field) => {
144299
return {
145-
fieldEntityId: field.name,
300+
fieldEntityId: field.id,
146301
number: value[field.name],
147302
};
148303
}),
149-
dates: customFields
304+
dates: customCustomerFields
150305
.filter((field) => ['date'].includes(field.type))
151306
.filter((field) => Boolean(value[field.name]))
152307
.map((field) => {
153308
return {
154-
fieldEntityId: field.name,
309+
fieldEntityId: field.id,
155310
date: new Date(String(value[field.name])).toISOString(),
156311
};
157312
}),
158-
passwords: customFields
313+
passwords: customCustomerFields
159314
.filter((field) => ['password'].includes(field.type))
160315
.filter((field) => Boolean(value[field.name]))
161316
.map((field) => ({
162-
fieldEntityId: field.name,
317+
fieldEntityId: field.id,
163318
password: value[field.name],
164319
})),
165-
multilineTexts: customFields
320+
multilineTexts: customCustomerFields
166321
.filter((field) => ['textarea'].includes(field.type))
167322
.filter((field) => Boolean(value[field.name]))
168323
.map((field) => ({
169-
fieldEntityId: field.name,
324+
fieldEntityId: field.id,
170325
multilineText: value[field.name],
171326
})),
172-
texts: customFields
327+
texts: customCustomerFields
173328
.filter((field) => ['text'].includes(field.type))
174329
.filter((field) => Boolean(value[field.name]))
175330
.map((field) => ({
176-
fieldEntityId: field.name,
331+
fieldEntityId: field.id,
177332
text: value[field.name],
178333
})),
179334
},

core/app/[locale]/(default)/(auth)/register/page-data.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ const RegisterCustomerQuery = graphql(
3232
}
3333
}
3434
}
35+
geography {
36+
countries {
37+
code
38+
name
39+
}
40+
}
3541
}
3642
`,
3743
[FormFieldsFragment],
@@ -68,6 +74,7 @@ export const getRegisterCustomerQuery = cache(async ({ address, customer }: Prop
6874

6975
const addressFields = response.data.site.settings?.formFields.shippingAddress;
7076
const customerFields = response.data.site.settings?.formFields.customer;
77+
const countries = response.data.geography.countries;
7178

7279
const reCaptchaSettings = await bypassReCaptcha(response.data.site.settings?.reCaptcha);
7380

@@ -79,5 +86,6 @@ export const getRegisterCustomerQuery = cache(async ({ address, customer }: Prop
7986
addressFields,
8087
customerFields,
8188
reCaptchaSettings,
89+
countries,
8290
};
8391
});

core/app/[locale]/(default)/(auth)/register/page.tsx

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@ import { getTranslations, setRequestLocale } from 'next-intl/server';
66
// import { bypassReCaptcha } from '~/lib/bypass-recaptcha';
77

88
import { DynamicFormSection } from '@/vibes/soul/sections/dynamic-form-section';
9-
import { formFieldTransformer } from '~/data-transformers/form-field-transformer';
9+
import {
10+
formFieldTransformer,
11+
injectCountryCodeOptions,
12+
} from '~/data-transformers/form-field-transformer';
1013
import {
1114
CUSTOMER_FIELDS_TO_EXCLUDE,
12-
FULL_NAME_FIELDS,
15+
REGISTER_CUSTOMER_FORM_LAYOUT,
16+
transformFieldsToLayout,
1317
} from '~/data-transformers/form-field-transformer/utils';
1418
import { exists } from '~/lib/utils';
1519

20+
import { ADDRESS_FIELDS_NAME_PREFIX, CUSTOMER_FIELDS_NAME_PREFIX } from './_actions/prefixes';
1621
import { registerCustomer } from './_actions/register-customer';
1722
import { getRegisterCustomerQuery } from './page-data';
1823

@@ -46,22 +51,55 @@ export default async function Register({ params }: Props) {
4651
notFound();
4752
}
4853

49-
const { addressFields, customerFields } = registerCustomerData;
54+
const { addressFields, customerFields, countries } = registerCustomerData;
5055
// const reCaptcha = await bypassReCaptcha(reCaptchaSettings);
5156

57+
const fields = transformFieldsToLayout(
58+
[
59+
...addressFields.map((field) => {
60+
if (!field.isBuiltIn) {
61+
return {
62+
...field,
63+
name: `${ADDRESS_FIELDS_NAME_PREFIX}${field.label}`,
64+
};
65+
}
66+
67+
return field;
68+
}),
69+
...customerFields.map((field) => {
70+
if (!field.isBuiltIn) {
71+
return {
72+
...field,
73+
name: `${CUSTOMER_FIELDS_NAME_PREFIX}${field.label}`,
74+
};
75+
}
76+
77+
return field;
78+
}),
79+
].filter((field) => !CUSTOMER_FIELDS_TO_EXCLUDE.includes(field.entityId)),
80+
REGISTER_CUSTOMER_FORM_LAYOUT,
81+
)
82+
.map((field) => {
83+
if (Array.isArray(field)) {
84+
return field.map(formFieldTransformer).filter(exists);
85+
}
86+
87+
return formFieldTransformer(field);
88+
})
89+
.filter(exists)
90+
.map((field) => {
91+
if (Array.isArray(field)) {
92+
return field.map((f) => injectCountryCodeOptions(f, countries ?? []));
93+
}
94+
95+
return injectCountryCodeOptions(field, countries ?? []);
96+
})
97+
.filter(exists);
98+
5299
return (
53100
<DynamicFormSection
54101
action={registerCustomer}
55-
fields={[
56-
addressFields
57-
.filter((field) => FULL_NAME_FIELDS.includes(field.entityId))
58-
.map(formFieldTransformer)
59-
.filter(exists),
60-
...customerFields
61-
.filter((field) => !CUSTOMER_FIELDS_TO_EXCLUDE.includes(field.entityId))
62-
.map(formFieldTransformer)
63-
.filter(exists),
64-
]}
102+
fields={fields}
65103
submitLabel={t('cta')}
66104
title={t('heading')}
67105
/>

0 commit comments

Comments
 (0)