@greatapps/common 1.1.763 → 1.1.764
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/modals/billing/RequiredBillingDataModal.mjs +139 -129
- package/dist/components/modals/billing/RequiredBillingDataModal.mjs.map +1 -1
- package/dist/i18n/messages/en-us.mjs +1 -0
- package/dist/i18n/messages/en-us.mjs.map +1 -1
- package/dist/i18n/messages/es-es.mjs +1 -0
- package/dist/i18n/messages/es-es.mjs.map +1 -1
- package/dist/i18n/messages/pt-br.mjs +1 -0
- package/dist/i18n/messages/pt-br.mjs.map +1 -1
- package/dist/middlewares/chain.mjs +10 -1
- package/dist/middlewares/chain.mjs.map +1 -1
- package/dist/utils/geo-country.mjs +16 -0
- package/dist/utils/geo-country.mjs.map +1 -0
- package/package.json +1 -1
- package/src/components/modals/billing/RequiredBillingDataModal.tsx +163 -144
- package/src/i18n/messages/en-us.ts +2 -0
- package/src/i18n/messages/es-es.ts +2 -0
- package/src/i18n/messages/pt-br.ts +2 -0
- package/src/middlewares/chain.ts +14 -1
- package/src/utils/geo-country.ts +15 -0
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
|
+
import { GEO_COUNTRY_COOKIE, GEO_UNKNOWN_COUNTRIES } from "../utils/geo-country";
|
|
3
|
+
function publishGeoCountry(request, response) {
|
|
4
|
+
const country = request.headers.get("cf-ipcountry")?.toUpperCase();
|
|
5
|
+
if (!country || GEO_UNKNOWN_COUNTRIES.includes(country)) return;
|
|
6
|
+
if (request.cookies.get(GEO_COUNTRY_COOKIE)?.value === country) return;
|
|
7
|
+
response.cookies.set(GEO_COUNTRY_COOKIE, country, { path: "/", sameSite: "lax" });
|
|
8
|
+
}
|
|
2
9
|
function shouldProcessPath(pathname, config) {
|
|
3
10
|
const { excludePaths, includePaths } = config;
|
|
4
11
|
if (excludePaths?.length) {
|
|
@@ -28,7 +35,9 @@ function createMiddlewareChain(middlewares) {
|
|
|
28
35
|
currentRequest = result.request;
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
|
-
|
|
38
|
+
const response = NextResponse.next();
|
|
39
|
+
publishGeoCountry(request, response);
|
|
40
|
+
return response;
|
|
32
41
|
};
|
|
33
42
|
}
|
|
34
43
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/middlewares/chain.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\r\n\r\nimport { MiddlewareConfig } from './types';\r\n\r\nfunction shouldProcessPath(pathname: string, config: MiddlewareConfig): boolean {\r\n const { excludePaths, includePaths } = config;\r\n\r\n if (excludePaths?.length) {\r\n const isExcluded = excludePaths.some(\r\n (path) => pathname === path || pathname.startsWith(`${path}/`)\r\n );\r\n if (isExcluded) return false;\r\n }\r\n\r\n if (includePaths?.length) {\r\n return includePaths.some((path) => pathname === path || pathname.startsWith(`${path}/`));\r\n }\r\n\r\n return true;\r\n}\r\n\r\nexport function createMiddlewareChain(middlewares: MiddlewareConfig[]) {\r\n return async function chainedMiddleware(request: NextRequest): Promise<NextResponse> {\r\n const { pathname } = request.nextUrl;\r\n let currentRequest = request;\r\n\r\n for (const middleware of middlewares) {\r\n if (!shouldProcessPath(pathname, middleware)) {\r\n continue;\r\n }\r\n\r\n const result = await middleware.handler(currentRequest);\r\n\r\n if (result.type === 'response') {\r\n return result.response;\r\n }\r\n\r\n if (result.request) {\r\n currentRequest = result.request;\r\n }\r\n }\r\n\r\n
|
|
1
|
+
{"version":3,"sources":["../../src/middlewares/chain.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\r\n\r\nimport { MiddlewareConfig } from './types';\r\nimport { GEO_COUNTRY_COOKIE, GEO_UNKNOWN_COUNTRIES } from '../utils/geo-country';\r\n\r\nfunction publishGeoCountry(request: NextRequest, response: NextResponse): void {\r\n const country = request.headers.get('cf-ipcountry')?.toUpperCase();\r\n\r\n if (!country || GEO_UNKNOWN_COUNTRIES.includes(country)) return;\r\n if (request.cookies.get(GEO_COUNTRY_COOKIE)?.value === country) return;\r\n\r\n response.cookies.set(GEO_COUNTRY_COOKIE, country, { path: '/', sameSite: 'lax' });\r\n}\r\n\r\nfunction shouldProcessPath(pathname: string, config: MiddlewareConfig): boolean {\r\n const { excludePaths, includePaths } = config;\r\n\r\n if (excludePaths?.length) {\r\n const isExcluded = excludePaths.some(\r\n (path) => pathname === path || pathname.startsWith(`${path}/`)\r\n );\r\n if (isExcluded) return false;\r\n }\r\n\r\n if (includePaths?.length) {\r\n return includePaths.some((path) => pathname === path || pathname.startsWith(`${path}/`));\r\n }\r\n\r\n return true;\r\n}\r\n\r\nexport function createMiddlewareChain(middlewares: MiddlewareConfig[]) {\r\n return async function chainedMiddleware(request: NextRequest): Promise<NextResponse> {\r\n const { pathname } = request.nextUrl;\r\n let currentRequest = request;\r\n\r\n for (const middleware of middlewares) {\r\n if (!shouldProcessPath(pathname, middleware)) {\r\n continue;\r\n }\r\n\r\n const result = await middleware.handler(currentRequest);\r\n\r\n if (result.type === 'response') {\r\n return result.response;\r\n }\r\n\r\n if (result.request) {\r\n currentRequest = result.request;\r\n }\r\n }\r\n\r\n const response = NextResponse.next();\r\n publishGeoCountry(request, response);\r\n\r\n return response;\r\n };\r\n}\r\n"],"mappings":"AAAA,SAAsB,oBAAoB;AAG1C,SAAS,oBAAoB,6BAA6B;AAE1D,SAAS,kBAAkB,SAAsB,UAA8B;AAC7E,QAAM,UAAU,QAAQ,QAAQ,IAAI,cAAc,GAAG,YAAY;AAEjE,MAAI,CAAC,WAAW,sBAAsB,SAAS,OAAO,EAAG;AACzD,MAAI,QAAQ,QAAQ,IAAI,kBAAkB,GAAG,UAAU,QAAS;AAEhE,WAAS,QAAQ,IAAI,oBAAoB,SAAS,EAAE,MAAM,KAAK,UAAU,MAAM,CAAC;AAClF;AAEA,SAAS,kBAAkB,UAAkB,QAAmC;AAC9E,QAAM,EAAE,cAAc,aAAa,IAAI;AAEvC,MAAI,cAAc,QAAQ;AACxB,UAAM,aAAa,aAAa;AAAA,MAC9B,CAAC,SAAS,aAAa,QAAQ,SAAS,WAAW,GAAG,IAAI,GAAG;AAAA,IAC/D;AACA,QAAI,WAAY,QAAO;AAAA,EACzB;AAEA,MAAI,cAAc,QAAQ;AACxB,WAAO,aAAa,KAAK,CAAC,SAAS,aAAa,QAAQ,SAAS,WAAW,GAAG,IAAI,GAAG,CAAC;AAAA,EACzF;AAEA,SAAO;AACT;AAEO,SAAS,sBAAsB,aAAiC;AACrE,SAAO,eAAe,kBAAkB,SAA6C;AACnF,UAAM,EAAE,SAAS,IAAI,QAAQ;AAC7B,QAAI,iBAAiB;AAErB,eAAW,cAAc,aAAa;AACpC,UAAI,CAAC,kBAAkB,UAAU,UAAU,GAAG;AAC5C;AAAA,MACF;AAEA,YAAM,SAAS,MAAM,WAAW,QAAQ,cAAc;AAEtD,UAAI,OAAO,SAAS,YAAY;AAC9B,eAAO,OAAO;AAAA,MAChB;AAEA,UAAI,OAAO,SAAS;AAClB,yBAAiB,OAAO;AAAA,MAC1B;AAAA,IACF;AAEA,UAAM,WAAW,aAAa,KAAK;AACnC,sBAAkB,SAAS,QAAQ;AAEnC,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { COUNTRIES } from "./countries";
|
|
2
|
+
const GEO_COUNTRY_COOKIE = "gs-geo-country";
|
|
3
|
+
const GEO_UNKNOWN_COUNTRIES = ["XX", "T1"];
|
|
4
|
+
function readGeoCountry() {
|
|
5
|
+
if (typeof document === "undefined") return null;
|
|
6
|
+
const match = new RegExp(`(?:^|;\\s*)${GEO_COUNTRY_COOKIE}=([^;]+)`).exec(document.cookie);
|
|
7
|
+
if (!match) return null;
|
|
8
|
+
const iso = match[1].toUpperCase();
|
|
9
|
+
return COUNTRIES.some((country) => country.iso === iso) ? iso : null;
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
GEO_COUNTRY_COOKIE,
|
|
13
|
+
GEO_UNKNOWN_COUNTRIES,
|
|
14
|
+
readGeoCountry
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=geo-country.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/geo-country.ts"],"sourcesContent":["import { COUNTRIES } from './countries';\n\nexport const GEO_COUNTRY_COOKIE = 'gs-geo-country';\n\nexport const GEO_UNKNOWN_COUNTRIES = ['XX', 'T1'];\n\nexport function readGeoCountry(): string | null {\n if (typeof document === 'undefined') return null;\n\n const match = new RegExp(`(?:^|;\\\\s*)${GEO_COUNTRY_COOKIE}=([^;]+)`).exec(document.cookie);\n if (!match) return null;\n\n const iso = match[1].toUpperCase();\n return COUNTRIES.some((country: { iso: string }) => country.iso === iso) ? iso : null;\n}\n"],"mappings":"AAAA,SAAS,iBAAiB;AAEnB,MAAM,qBAAqB;AAE3B,MAAM,wBAAwB,CAAC,MAAM,IAAI;AAEzC,SAAS,iBAAgC;AAC9C,MAAI,OAAO,aAAa,YAAa,QAAO;AAE5C,QAAM,QAAQ,IAAI,OAAO,cAAc,kBAAkB,UAAU,EAAE,KAAK,SAAS,MAAM;AACzF,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,MAAM,MAAM,CAAC,EAAE,YAAY;AACjC,SAAO,UAAU,KAAK,CAAC,YAA6B,QAAQ,QAAQ,GAAG,IAAI,MAAM;AACnF;","names":[]}
|
package/package.json
CHANGED
|
@@ -21,6 +21,7 @@ import { useViaCep } from '../../../modules/accounts/hooks/useViaCep';
|
|
|
21
21
|
import { useUpdateBillingData } from '../../../modules/accounts/hooks/useAccountManagement';
|
|
22
22
|
import { useRequiredBillingData } from '../../../modules/accounts/hooks/use-required-billing-data.hook';
|
|
23
23
|
import { COUNTRIES } from '../../../utils/countries';
|
|
24
|
+
import { readGeoCountry } from '../../../utils/geo-country';
|
|
24
25
|
import { BR_STATE_OPTIONS } from '../../../utils/constants/br-states';
|
|
25
26
|
import { formatCNPJ, formatCPF, formatPostalCode } from '../../../utils/format/masks';
|
|
26
27
|
import { isValidCNPJ, isValidCPF } from '../../../utils/validators/common';
|
|
@@ -48,46 +49,48 @@ const COUNTRY_OPTIONS = COUNTRIES.map((country: { iso: string; name: string }) =
|
|
|
48
49
|
label: country.name,
|
|
49
50
|
}));
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
const BR_REQUIRED_ADDRESS_FIELDS: readonly (keyof BillingDataFormValues)[] = [
|
|
53
|
+
'street',
|
|
54
|
+
'streetNumber',
|
|
55
|
+
'neighborhood',
|
|
56
|
+
'city',
|
|
57
|
+
'state',
|
|
58
|
+
];
|
|
56
59
|
|
|
60
|
+
function buildBillingDataSchema(translate: TranslateFn) {
|
|
57
61
|
const base = z.object({
|
|
58
62
|
personType: z.enum(['pf', 'pj']),
|
|
59
63
|
document: z.string().trim(),
|
|
60
|
-
financialName:
|
|
64
|
+
financialName: z
|
|
65
|
+
.string()
|
|
66
|
+
.trim()
|
|
67
|
+
.min(1, translate('common.billing.requiredData.requiredField')),
|
|
61
68
|
financialEmail: z
|
|
62
69
|
.string()
|
|
63
70
|
.trim()
|
|
64
71
|
.min(1, translate('common.billing.requiredData.requiredField'))
|
|
65
72
|
.email(translate('common.billing.requiredData.invalidEmail')),
|
|
66
|
-
cep:
|
|
67
|
-
street:
|
|
73
|
+
cep: z.string().trim(),
|
|
74
|
+
street: z.string().trim(),
|
|
68
75
|
streetNumber: z.string().trim(),
|
|
69
76
|
complement: z.string().trim().optional(),
|
|
70
77
|
neighborhood: z.string().trim(),
|
|
71
|
-
city:
|
|
72
|
-
state:
|
|
73
|
-
country:
|
|
78
|
+
city: z.string().trim(),
|
|
79
|
+
state: z.string().trim(),
|
|
80
|
+
country: z.string().trim().min(1, translate('common.billing.requiredData.requiredField')),
|
|
74
81
|
});
|
|
75
82
|
|
|
76
83
|
return base.superRefine((data, ctx) => {
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
code: 'custom',
|
|
88
|
-
path: ['neighborhood'],
|
|
89
|
-
message: translate('common.billing.requiredData.requiredField'),
|
|
90
|
-
});
|
|
84
|
+
if (data.country !== 'BR') return;
|
|
85
|
+
|
|
86
|
+
for (const field of BR_REQUIRED_ADDRESS_FIELDS) {
|
|
87
|
+
if (!data[field]) {
|
|
88
|
+
ctx.addIssue({
|
|
89
|
+
code: 'custom',
|
|
90
|
+
path: [field],
|
|
91
|
+
message: translate('common.billing.requiredData.requiredField'),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
if (data.cep.replace(/\D/g, '').length !== 8) {
|
|
@@ -180,6 +183,7 @@ function RequiredBillingDataModalContent() {
|
|
|
180
183
|
|
|
181
184
|
const personType = watch('personType');
|
|
182
185
|
const isCompany = personType === 'pj';
|
|
186
|
+
const isBrazil = watch('country') === 'BR';
|
|
183
187
|
|
|
184
188
|
useEffect(() => {
|
|
185
189
|
if (!account || isPrefilled.current) return;
|
|
@@ -198,7 +202,7 @@ function RequiredBillingDataModalContent() {
|
|
|
198
202
|
neighborhood: account.neighborhood ?? '',
|
|
199
203
|
city: account.city ?? '',
|
|
200
204
|
state: account.state ?? '',
|
|
201
|
-
country: account.country || 'BR',
|
|
205
|
+
country: account.country || readGeoCountry() || 'BR',
|
|
202
206
|
});
|
|
203
207
|
}, [account, reset, user?.email]);
|
|
204
208
|
|
|
@@ -235,20 +239,27 @@ function RequiredBillingDataModalContent() {
|
|
|
235
239
|
};
|
|
236
240
|
|
|
237
241
|
async function onSubmit(data: BillingDataFormValues) {
|
|
238
|
-
const payload: UpdateBillingDataRequest =
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
242
|
+
const payload: UpdateBillingDataRequest =
|
|
243
|
+
data.country === 'BR'
|
|
244
|
+
? {
|
|
245
|
+
financial_document_type: data.personType === 'pf' ? 1 : 2,
|
|
246
|
+
financial_document: data.document,
|
|
247
|
+
financial_name: data.financialName,
|
|
248
|
+
financial_email: data.financialEmail,
|
|
249
|
+
zipcode: data.cep,
|
|
250
|
+
address: data.street,
|
|
251
|
+
address_number: data.streetNumber,
|
|
252
|
+
address_complement: data.complement ?? '',
|
|
253
|
+
neighborhood: data.neighborhood,
|
|
254
|
+
city: data.city,
|
|
255
|
+
state: data.state,
|
|
256
|
+
country: data.country,
|
|
257
|
+
}
|
|
258
|
+
: {
|
|
259
|
+
financial_name: data.financialName,
|
|
260
|
+
financial_email: data.financialEmail,
|
|
261
|
+
country: data.country,
|
|
262
|
+
};
|
|
252
263
|
|
|
253
264
|
try {
|
|
254
265
|
await updateBillingData(payload);
|
|
@@ -296,7 +307,9 @@ function RequiredBillingDataModalContent() {
|
|
|
296
307
|
{translate('common.billing.requiredData.title')}
|
|
297
308
|
</DialogTitle>
|
|
298
309
|
<span className="paragraph-small-regular text-zinc-600">
|
|
299
|
-
{
|
|
310
|
+
{isBrazil
|
|
311
|
+
? translate('common.billing.requiredData.description')
|
|
312
|
+
: translate('common.billing.requiredData.descriptionInternational')}
|
|
300
313
|
</span>
|
|
301
314
|
</div>
|
|
302
315
|
</DialogHeader>
|
|
@@ -308,7 +321,26 @@ function RequiredBillingDataModalContent() {
|
|
|
308
321
|
>
|
|
309
322
|
<div className="flex flex-col gap-6 p-4 lg:p-5 flex-1 overflow-y-auto">
|
|
310
323
|
<div className="flex flex-col gap-5">
|
|
311
|
-
<
|
|
324
|
+
<Controller
|
|
325
|
+
name="country"
|
|
326
|
+
control={control}
|
|
327
|
+
render={({ field }) => (
|
|
328
|
+
<SelectField
|
|
329
|
+
label={translate('common.billing.requiredData.country')}
|
|
330
|
+
placeholder={translate('common.billing.requiredData.select')}
|
|
331
|
+
value={field.value || undefined}
|
|
332
|
+
onChange={(value) =>
|
|
333
|
+
setValue('country', String(value), { shouldValidate: true })
|
|
334
|
+
}
|
|
335
|
+
options={COUNTRY_OPTIONS}
|
|
336
|
+
error={!!errors.country}
|
|
337
|
+
errorMessage={errors.country?.message}
|
|
338
|
+
/>
|
|
339
|
+
)}
|
|
340
|
+
/>
|
|
341
|
+
|
|
342
|
+
{isBrazil && (
|
|
343
|
+
<div className="flex gap-4">
|
|
312
344
|
<Controller
|
|
313
345
|
name="personType"
|
|
314
346
|
control={control}
|
|
@@ -347,11 +379,12 @@ function RequiredBillingDataModalContent() {
|
|
|
347
379
|
})}
|
|
348
380
|
/>
|
|
349
381
|
</div>
|
|
350
|
-
|
|
382
|
+
</div>
|
|
383
|
+
)}
|
|
351
384
|
|
|
352
385
|
<FormField
|
|
353
386
|
label={
|
|
354
|
-
isCompany
|
|
387
|
+
isBrazil && isCompany
|
|
355
388
|
? translate('common.billing.requiredData.companyName')
|
|
356
389
|
: translate('common.billing.requiredData.fullName')
|
|
357
390
|
}
|
|
@@ -371,113 +404,99 @@ function RequiredBillingDataModalContent() {
|
|
|
371
404
|
/>
|
|
372
405
|
</div>
|
|
373
406
|
|
|
374
|
-
|
|
407
|
+
{isBrazil && (
|
|
408
|
+
<>
|
|
409
|
+
<div className="h-px bg-zinc-200" />
|
|
375
410
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
411
|
+
<div className="flex flex-col gap-5">
|
|
412
|
+
<span className="paragraph-small-semibold text-zinc-950">
|
|
413
|
+
{translate('common.billing.requiredData.addressHeading')}
|
|
414
|
+
</span>
|
|
380
415
|
|
|
381
|
-
<FormField
|
|
382
|
-
label={translate('common.billing.requiredData.cep')}
|
|
383
|
-
placeholder="_____-___"
|
|
384
|
-
error={!!errors.cep}
|
|
385
|
-
errorMessage={errors.cep?.message}
|
|
386
|
-
{...register('cep', {
|
|
387
|
-
onChange: (event) => handleCepChange(event.target.value),
|
|
388
|
-
})}
|
|
389
|
-
/>
|
|
390
|
-
|
|
391
|
-
<div className="flex gap-4">
|
|
392
|
-
<div className="flex-[3]">
|
|
393
416
|
<FormField
|
|
394
|
-
label={translate('common.billing.requiredData.
|
|
395
|
-
placeholder=
|
|
396
|
-
error={!!errors.
|
|
397
|
-
errorMessage={errors.
|
|
398
|
-
{...register('
|
|
417
|
+
label={translate('common.billing.requiredData.cep')}
|
|
418
|
+
placeholder="_____-___"
|
|
419
|
+
error={!!errors.cep}
|
|
420
|
+
errorMessage={errors.cep?.message}
|
|
421
|
+
{...register('cep', {
|
|
422
|
+
onChange: (event) => handleCepChange(event.target.value),
|
|
423
|
+
})}
|
|
399
424
|
/>
|
|
400
|
-
</div>
|
|
401
|
-
<div className="flex-1">
|
|
402
|
-
<FormField
|
|
403
|
-
label={translate('common.billing.requiredData.streetNumber')}
|
|
404
|
-
placeholder="000"
|
|
405
|
-
error={!!errors.streetNumber}
|
|
406
|
-
errorMessage={errors.streetNumber?.message}
|
|
407
|
-
{...register('streetNumber')}
|
|
408
|
-
/>
|
|
409
|
-
</div>
|
|
410
|
-
</div>
|
|
411
425
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
426
|
+
<div className="flex gap-4">
|
|
427
|
+
<div className="flex-[3]">
|
|
428
|
+
<FormField
|
|
429
|
+
label={translate('common.billing.requiredData.street')}
|
|
430
|
+
placeholder={translate('common.billing.requiredData.typeHere')}
|
|
431
|
+
error={!!errors.street}
|
|
432
|
+
errorMessage={errors.street?.message}
|
|
433
|
+
{...register('street')}
|
|
434
|
+
/>
|
|
435
|
+
</div>
|
|
436
|
+
<div className="flex-1">
|
|
437
|
+
<FormField
|
|
438
|
+
label={translate('common.billing.requiredData.streetNumber')}
|
|
439
|
+
placeholder="000"
|
|
440
|
+
error={!!errors.streetNumber}
|
|
441
|
+
errorMessage={errors.streetNumber?.message}
|
|
442
|
+
{...register('streetNumber')}
|
|
443
|
+
/>
|
|
444
|
+
</div>
|
|
445
|
+
</div>
|
|
431
446
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
onChange={(value) =>
|
|
452
|
-
setValue('state', value as string, { shouldValidate: true })
|
|
453
|
-
}
|
|
454
|
-
options={BR_STATE_OPTIONS}
|
|
455
|
-
error={!!errors.state}
|
|
456
|
-
errorMessage={errors.state?.message}
|
|
457
|
-
/>
|
|
458
|
-
)}
|
|
459
|
-
/>
|
|
460
|
-
</div>
|
|
461
|
-
</div>
|
|
447
|
+
<div className="flex gap-4">
|
|
448
|
+
<div className="flex-1">
|
|
449
|
+
<FormField
|
|
450
|
+
label={translate('common.billing.requiredData.neighborhood')}
|
|
451
|
+
placeholder={translate('common.billing.requiredData.typeHere')}
|
|
452
|
+
error={!!errors.neighborhood}
|
|
453
|
+
errorMessage={errors.neighborhood?.message}
|
|
454
|
+
{...register('neighborhood')}
|
|
455
|
+
/>
|
|
456
|
+
</div>
|
|
457
|
+
<div className="flex-1">
|
|
458
|
+
<FormField
|
|
459
|
+
label={translate('common.billing.requiredData.complement')}
|
|
460
|
+
optional
|
|
461
|
+
placeholder={translate('common.billing.requiredData.typeHere')}
|
|
462
|
+
{...register('complement')}
|
|
463
|
+
/>
|
|
464
|
+
</div>
|
|
465
|
+
</div>
|
|
462
466
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
467
|
+
<div className="flex gap-4">
|
|
468
|
+
<div className="flex-1">
|
|
469
|
+
<FormField
|
|
470
|
+
label={translate('common.billing.requiredData.city')}
|
|
471
|
+
placeholder={translate('common.billing.requiredData.typeHere')}
|
|
472
|
+
error={!!errors.city}
|
|
473
|
+
errorMessage={errors.city?.message}
|
|
474
|
+
{...register('city')}
|
|
475
|
+
/>
|
|
476
|
+
</div>
|
|
477
|
+
<div className="flex-1">
|
|
478
|
+
<Controller
|
|
479
|
+
name="state"
|
|
480
|
+
control={control}
|
|
481
|
+
render={({ field }) => (
|
|
482
|
+
<SelectField
|
|
483
|
+
label={translate('common.billing.requiredData.state')}
|
|
484
|
+
placeholder={translate('common.billing.requiredData.select')}
|
|
485
|
+
value={field.value || undefined}
|
|
486
|
+
onChange={(value) =>
|
|
487
|
+
setValue('state', String(value), { shouldValidate: true })
|
|
488
|
+
}
|
|
489
|
+
options={BR_STATE_OPTIONS}
|
|
490
|
+
error={!!errors.state}
|
|
491
|
+
errorMessage={errors.state?.message}
|
|
492
|
+
/>
|
|
493
|
+
)}
|
|
494
|
+
/>
|
|
495
|
+
</div>
|
|
496
|
+
</div>
|
|
497
|
+
</div>
|
|
498
|
+
</>
|
|
499
|
+
)}
|
|
481
500
|
</div>
|
|
482
501
|
|
|
483
502
|
<div className="flex items-center justify-end p-4 lg:p-5 border-t border-zinc-200 shrink-0">
|
|
@@ -354,6 +354,8 @@ const messages = {
|
|
|
354
354
|
title: 'Complete your billing information',
|
|
355
355
|
description:
|
|
356
356
|
'We need this information to issue the invoice for your payments. Filling it in is required to keep using the platform.',
|
|
357
|
+
descriptionInternational:
|
|
358
|
+
'Since your billing address is outside Brazil, we only need your name and email for your payment receipts.',
|
|
357
359
|
addressHeading: 'Billing address',
|
|
358
360
|
select: 'Select',
|
|
359
361
|
typeHere: 'Type here',
|
|
@@ -355,6 +355,8 @@ const messages = {
|
|
|
355
355
|
title: 'Completa tus datos de facturación',
|
|
356
356
|
description:
|
|
357
357
|
'Necesitamos esta información para emitir la factura de tus pagos. Completarla es obligatorio para seguir usando la plataforma.',
|
|
358
|
+
descriptionInternational:
|
|
359
|
+
'Como tu facturación es fuera de Brasil, solo necesitamos tu nombre y correo para los recibos de tus pagos.',
|
|
358
360
|
addressHeading: 'Dirección de facturación',
|
|
359
361
|
select: 'Selecciona',
|
|
360
362
|
typeHere: 'Escribe aquí',
|
|
@@ -367,6 +367,8 @@ const messages = {
|
|
|
367
367
|
title: 'Complete seus dados de cobrança',
|
|
368
368
|
description:
|
|
369
369
|
'Precisamos dessas informações para emitir a nota fiscal dos seus pagamentos. O preenchimento é obrigatório para continuar usando a plataforma.',
|
|
370
|
+
descriptionInternational:
|
|
371
|
+
'Como sua cobrança é fora do Brasil, precisamos apenas do nome e do e-mail para os recibos dos seus pagamentos.',
|
|
370
372
|
addressHeading: 'Endereço de cobrança',
|
|
371
373
|
select: 'Selecione',
|
|
372
374
|
typeHere: 'Digite aqui',
|
package/src/middlewares/chain.ts
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
|
|
3
3
|
import { MiddlewareConfig } from './types';
|
|
4
|
+
import { GEO_COUNTRY_COOKIE, GEO_UNKNOWN_COUNTRIES } from '../utils/geo-country';
|
|
5
|
+
|
|
6
|
+
function publishGeoCountry(request: NextRequest, response: NextResponse): void {
|
|
7
|
+
const country = request.headers.get('cf-ipcountry')?.toUpperCase();
|
|
8
|
+
|
|
9
|
+
if (!country || GEO_UNKNOWN_COUNTRIES.includes(country)) return;
|
|
10
|
+
if (request.cookies.get(GEO_COUNTRY_COOKIE)?.value === country) return;
|
|
11
|
+
|
|
12
|
+
response.cookies.set(GEO_COUNTRY_COOKIE, country, { path: '/', sameSite: 'lax' });
|
|
13
|
+
}
|
|
4
14
|
|
|
5
15
|
function shouldProcessPath(pathname: string, config: MiddlewareConfig): boolean {
|
|
6
16
|
const { excludePaths, includePaths } = config;
|
|
@@ -40,6 +50,9 @@ export function createMiddlewareChain(middlewares: MiddlewareConfig[]) {
|
|
|
40
50
|
}
|
|
41
51
|
}
|
|
42
52
|
|
|
43
|
-
|
|
53
|
+
const response = NextResponse.next();
|
|
54
|
+
publishGeoCountry(request, response);
|
|
55
|
+
|
|
56
|
+
return response;
|
|
44
57
|
};
|
|
45
58
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { COUNTRIES } from './countries';
|
|
2
|
+
|
|
3
|
+
export const GEO_COUNTRY_COOKIE = 'gs-geo-country';
|
|
4
|
+
|
|
5
|
+
export const GEO_UNKNOWN_COUNTRIES = ['XX', 'T1'];
|
|
6
|
+
|
|
7
|
+
export function readGeoCountry(): string | null {
|
|
8
|
+
if (typeof document === 'undefined') return null;
|
|
9
|
+
|
|
10
|
+
const match = new RegExp(`(?:^|;\\s*)${GEO_COUNTRY_COOKIE}=([^;]+)`).exec(document.cookie);
|
|
11
|
+
if (!match) return null;
|
|
12
|
+
|
|
13
|
+
const iso = match[1].toUpperCase();
|
|
14
|
+
return COUNTRIES.some((country: { iso: string }) => country.iso === iso) ? iso : null;
|
|
15
|
+
}
|