@greatapps/common 1.1.779 → 1.1.781
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/account/sections/PreferencesSection.mjs +3 -1
- package/dist/components/account/sections/PreferencesSection.mjs.map +1 -1
- package/dist/components/layouts/AppMobileNavBar.mjs +64 -5
- package/dist/components/layouts/AppMobileNavBar.mjs.map +1 -1
- package/dist/components/layouts/AppNavBar.mjs +2 -0
- package/dist/components/layouts/AppNavBar.mjs.map +1 -1
- package/dist/components/layouts/GreatDomainIcon.mjs +40 -0
- package/dist/components/layouts/GreatDomainIcon.mjs.map +1 -0
- package/dist/components/layouts/NavBar.mjs +109 -52
- package/dist/components/layouts/NavBar.mjs.map +1 -1
- package/dist/hooks/useDomMutationGuardHit.mjs +36 -0
- package/dist/hooks/useDomMutationGuardHit.mjs.map +1 -0
- package/dist/i18n/messages/en-us.mjs +3 -1
- package/dist/i18n/messages/en-us.mjs.map +1 -1
- package/dist/i18n/messages/es-es.mjs +3 -1
- package/dist/i18n/messages/es-es.mjs.map +1 -1
- package/dist/i18n/messages/pt-br.mjs +3 -1
- package/dist/i18n/messages/pt-br.mjs.map +1 -1
- package/dist/i18n/resolve-locale.mjs +10 -0
- package/dist/i18n/resolve-locale.mjs.map +1 -1
- package/dist/index.mjs +4 -0
- package/dist/index.mjs.map +1 -1
- package/dist/modules/auth/services/auth.service.mjs +12 -6
- package/dist/modules/auth/services/auth.service.mjs.map +1 -1
- package/dist/utils/dom/dom-mutation-guard.mjs +20 -0
- package/dist/utils/dom/dom-mutation-guard.mjs.map +1 -0
- package/dist/utils/intl/locales.mjs +6 -1
- package/dist/utils/intl/locales.mjs.map +1 -1
- package/package.json +7 -8
- package/src/components/account/sections/PreferencesSection.tsx +3 -2
- package/src/components/layouts/AppMobileNavBar.tsx +90 -6
- package/src/components/layouts/AppNavBar.tsx +5 -1
- package/src/components/layouts/GreatDomainIcon.tsx +43 -0
- package/src/components/layouts/NavBar.tsx +158 -66
- package/src/hooks/useDomMutationGuardHit.ts +55 -0
- 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/i18n/resolve-locale.ts +19 -4
- package/src/index.ts +3 -0
- package/src/modules/auth/services/auth.service.ts +14 -6
- package/src/utils/dom/dom-mutation-guard.ts +46 -0
- package/src/utils/intl/locales.ts +7 -0
|
@@ -6,6 +6,7 @@ import Link from "next/link";
|
|
|
6
6
|
import { useTranslations } from "next-intl";
|
|
7
7
|
import { IconChevronRight } from "@tabler/icons-react";
|
|
8
8
|
import { Separator } from "../ui/data-display/Separator";
|
|
9
|
+
import { GreatDomainIcon } from "./GreatDomainIcon";
|
|
9
10
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/overlay/Tooltip";
|
|
10
11
|
import {
|
|
11
12
|
useDesktopSidebarStore,
|
|
@@ -20,7 +21,9 @@ import { cn } from "../../infra/utils/clsx";
|
|
|
20
21
|
type NavBarProps = {
|
|
21
22
|
logoHref: string;
|
|
22
23
|
appIconHref: string;
|
|
23
|
-
|
|
24
|
+
/** Sem href o ícone do GreatDomain não é renderizado: é a flag do app. */
|
|
25
|
+
domainIconHref?: string;
|
|
26
|
+
activeApp?: "gapps" | "gpages" | "gdomain";
|
|
24
27
|
showExpandButton?: boolean;
|
|
25
28
|
onExpandClick?: () => void;
|
|
26
29
|
children?: React.ReactNode;
|
|
@@ -30,6 +33,7 @@ type NavBarProps = {
|
|
|
30
33
|
function NavBar({
|
|
31
34
|
logoHref,
|
|
32
35
|
appIconHref,
|
|
36
|
+
domainIconHref,
|
|
33
37
|
activeApp,
|
|
34
38
|
showExpandButton = false,
|
|
35
39
|
onExpandClick,
|
|
@@ -130,7 +134,9 @@ function NavBar({
|
|
|
130
134
|
<IconChevronRight size={16} className="text-gray-500" />
|
|
131
135
|
</button>
|
|
132
136
|
</TooltipTrigger>
|
|
133
|
-
<TooltipContent side="right">
|
|
137
|
+
<TooltipContent side="right">
|
|
138
|
+
{translate("common.layout.navbar.expandMenu")}
|
|
139
|
+
</TooltipContent>
|
|
134
140
|
</Tooltip>
|
|
135
141
|
)}
|
|
136
142
|
{/* Desktop expand button */}
|
|
@@ -144,78 +150,164 @@ function NavBar({
|
|
|
144
150
|
<IconChevronRight size={16} className="text-gray-500" />
|
|
145
151
|
</button>
|
|
146
152
|
</TooltipTrigger>
|
|
147
|
-
<TooltipContent side="right">
|
|
153
|
+
<TooltipContent side="right">
|
|
154
|
+
{translate("common.layout.navbar.expandMenu")}
|
|
155
|
+
</TooltipContent>
|
|
148
156
|
</Tooltip>
|
|
149
157
|
)}
|
|
150
158
|
</div>
|
|
151
159
|
|
|
152
|
-
{/*
|
|
153
|
-
<div className="
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
160
|
+
{/* Ícones de app — 8px entre eles (Figma 5929:2676) */}
|
|
161
|
+
<div className="flex flex-col gap-2 items-center">
|
|
162
|
+
{/* GreatPages icon */}
|
|
163
|
+
<div className="relative">
|
|
164
|
+
<Tooltip>
|
|
165
|
+
<TooltipTrigger asChild>
|
|
166
|
+
<Link
|
|
167
|
+
href={appIconHref}
|
|
168
|
+
className={cn(
|
|
169
|
+
"size-10 rounded-lg cursor-pointer flex items-center justify-center border-2 ring-2 transition-colors duration-300",
|
|
170
|
+
activeApp === "gpages"
|
|
171
|
+
? isDefaultWl
|
|
172
|
+
? "bg-cyan-100 border-white ring-cyan-300"
|
|
173
|
+
: "border-white ring-transparent"
|
|
174
|
+
: isDefaultWl
|
|
175
|
+
? "hover:bg-gray-100 border-transparent ring-transparent"
|
|
176
|
+
: "border-transparent ring-transparent",
|
|
177
|
+
)}
|
|
178
|
+
style={
|
|
179
|
+
!isDefaultWl
|
|
180
|
+
? {
|
|
181
|
+
backgroundColor: primaryColor,
|
|
182
|
+
boxShadow:
|
|
183
|
+
activeApp === "gpages"
|
|
184
|
+
? `0 0 0 2px ${primaryColor}40`
|
|
185
|
+
: undefined,
|
|
186
|
+
opacity: activeApp !== "gpages" ? 0.2 : undefined,
|
|
187
|
+
}
|
|
188
|
+
: undefined
|
|
189
|
+
}
|
|
190
|
+
>
|
|
191
|
+
{isDefaultWl ? (
|
|
192
|
+
<Image
|
|
193
|
+
src="/icons/icon-navbar.svg"
|
|
194
|
+
alt="GreatPages"
|
|
195
|
+
width={24}
|
|
196
|
+
height={24}
|
|
197
|
+
/>
|
|
198
|
+
) : (
|
|
199
|
+
<svg
|
|
200
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
201
|
+
width="24"
|
|
202
|
+
height="24"
|
|
203
|
+
viewBox="0 0 24 24"
|
|
204
|
+
fill="none"
|
|
205
|
+
stroke="white"
|
|
206
|
+
strokeWidth="2"
|
|
207
|
+
strokeLinecap="round"
|
|
208
|
+
strokeLinejoin="round"
|
|
209
|
+
>
|
|
210
|
+
<path d="M4 8h16" />
|
|
211
|
+
<path d="M4 6a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2l0 -12" />
|
|
212
|
+
<path d="M8 4v4" />
|
|
213
|
+
</svg>
|
|
214
|
+
)}
|
|
215
|
+
</Link>
|
|
216
|
+
</TooltipTrigger>
|
|
217
|
+
<TooltipContent side="right">
|
|
218
|
+
{isDefaultWl
|
|
219
|
+
? translate("common.layout.navbar.greatPages")
|
|
220
|
+
: translate("common.layout.navbar.pages")}
|
|
221
|
+
</TooltipContent>
|
|
222
|
+
</Tooltip>
|
|
223
|
+
<div
|
|
224
|
+
className={cn(
|
|
225
|
+
"absolute -left-4 top-1/2 -translate-y-1/2 z-10 w-[3px] h-5 rounded-r-xl transition-opacity duration-300",
|
|
226
|
+
isDefaultWl ? "bg-cyan-300" : "",
|
|
227
|
+
activeApp === "gpages" ? "opacity-100" : "opacity-0",
|
|
228
|
+
)}
|
|
229
|
+
style={
|
|
230
|
+
!isDefaultWl ? { backgroundColor: primaryColor } : undefined
|
|
231
|
+
}
|
|
232
|
+
/>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
{/* GreatDomain icon */}
|
|
236
|
+
{domainIconHref ? (
|
|
237
|
+
<div className="relative">
|
|
238
|
+
<Tooltip>
|
|
239
|
+
<TooltipTrigger asChild>
|
|
240
|
+
<Link
|
|
241
|
+
href={domainIconHref}
|
|
242
|
+
aria-label={
|
|
243
|
+
isDefaultWl
|
|
244
|
+
? translate("common.layout.navbar.greatDomain")
|
|
245
|
+
: translate("common.layout.navbar.domains")
|
|
246
|
+
}
|
|
247
|
+
className={cn(
|
|
248
|
+
"size-10 rounded-[10px] cursor-pointer flex items-center justify-center border-2 ring-2 transition-colors duration-300",
|
|
249
|
+
activeApp === "gdomain"
|
|
250
|
+
? isDefaultWl
|
|
251
|
+
? "bg-[#EDE9FE] border-white ring-[#C092FF]"
|
|
252
|
+
: "border-white ring-transparent"
|
|
253
|
+
: isDefaultWl
|
|
254
|
+
? "hover:bg-gray-100 border-transparent ring-transparent"
|
|
255
|
+
: "border-transparent ring-transparent",
|
|
256
|
+
)}
|
|
257
|
+
style={
|
|
258
|
+
!isDefaultWl
|
|
259
|
+
? {
|
|
260
|
+
backgroundColor: primaryColor,
|
|
261
|
+
boxShadow:
|
|
262
|
+
activeApp === "gdomain"
|
|
263
|
+
? `0 0 0 2px ${primaryColor}40`
|
|
264
|
+
: undefined,
|
|
265
|
+
opacity: activeApp !== "gdomain" ? 0.2 : undefined,
|
|
266
|
+
}
|
|
267
|
+
: undefined
|
|
268
|
+
}
|
|
269
|
+
>
|
|
270
|
+
{isDefaultWl ? (
|
|
271
|
+
<GreatDomainIcon active={activeApp === "gdomain"} />
|
|
272
|
+
) : (
|
|
273
|
+
<svg
|
|
274
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
275
|
+
width="24"
|
|
276
|
+
height="24"
|
|
277
|
+
viewBox="0 0 24 24"
|
|
278
|
+
fill="none"
|
|
279
|
+
stroke="white"
|
|
280
|
+
strokeWidth="2"
|
|
281
|
+
strokeLinecap="round"
|
|
282
|
+
strokeLinejoin="round"
|
|
283
|
+
>
|
|
284
|
+
<path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0" />
|
|
285
|
+
<path d="M3.6 9h16.8" />
|
|
286
|
+
<path d="M3.6 15h16.8" />
|
|
287
|
+
<path d="M11.5 3a17 17 0 0 0 0 18" />
|
|
288
|
+
<path d="M12.5 3a17 17 0 0 1 0 18" />
|
|
289
|
+
</svg>
|
|
290
|
+
)}
|
|
291
|
+
</Link>
|
|
292
|
+
</TooltipTrigger>
|
|
293
|
+
<TooltipContent side="right">
|
|
294
|
+
{isDefaultWl
|
|
295
|
+
? translate("common.layout.navbar.greatDomain")
|
|
296
|
+
: translate("common.layout.navbar.domains")}
|
|
297
|
+
</TooltipContent>
|
|
298
|
+
</Tooltip>
|
|
299
|
+
<div
|
|
158
300
|
className={cn(
|
|
159
|
-
"
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
? "bg-cyan-100 border-white ring-cyan-300"
|
|
163
|
-
: "border-white ring-transparent"
|
|
164
|
-
: isDefaultWl
|
|
165
|
-
? "hover:bg-gray-100 border-transparent ring-transparent"
|
|
166
|
-
: "border-transparent ring-transparent",
|
|
301
|
+
"absolute -left-4 top-1/2 -translate-y-1/2 z-10 w-[3px] h-5 rounded-r-xl transition-opacity duration-300",
|
|
302
|
+
isDefaultWl ? "bg-[#984DFF]" : "",
|
|
303
|
+
activeApp === "gdomain" ? "opacity-100" : "opacity-0",
|
|
167
304
|
)}
|
|
168
305
|
style={
|
|
169
|
-
!isDefaultWl
|
|
170
|
-
? {
|
|
171
|
-
backgroundColor: primaryColor,
|
|
172
|
-
boxShadow:
|
|
173
|
-
activeApp === "gpages"
|
|
174
|
-
? `0 0 0 2px ${primaryColor}40`
|
|
175
|
-
: undefined,
|
|
176
|
-
opacity: activeApp !== "gpages" ? 0.2 : undefined,
|
|
177
|
-
}
|
|
178
|
-
: undefined
|
|
306
|
+
!isDefaultWl ? { backgroundColor: primaryColor } : undefined
|
|
179
307
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
src="/icons/icon-navbar.svg"
|
|
184
|
-
alt="GreatPages"
|
|
185
|
-
width={24}
|
|
186
|
-
height={24}
|
|
187
|
-
/>
|
|
188
|
-
) : (
|
|
189
|
-
<svg
|
|
190
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
191
|
-
width="24"
|
|
192
|
-
height="24"
|
|
193
|
-
viewBox="0 0 24 24"
|
|
194
|
-
fill="none"
|
|
195
|
-
stroke="white"
|
|
196
|
-
strokeWidth="2"
|
|
197
|
-
strokeLinecap="round"
|
|
198
|
-
strokeLinejoin="round"
|
|
199
|
-
>
|
|
200
|
-
<path d="M4 8h16" />
|
|
201
|
-
<path d="M4 6a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2l0 -12" />
|
|
202
|
-
<path d="M8 4v4" />
|
|
203
|
-
</svg>
|
|
204
|
-
)}
|
|
205
|
-
</Link>
|
|
206
|
-
</TooltipTrigger>
|
|
207
|
-
<TooltipContent side="right">
|
|
208
|
-
{isDefaultWl ? translate('common.layout.navbar.greatPages') : translate('common.layout.navbar.pages')}
|
|
209
|
-
</TooltipContent>
|
|
210
|
-
</Tooltip>
|
|
211
|
-
<div
|
|
212
|
-
className={cn(
|
|
213
|
-
"absolute -left-4 top-1/2 -translate-y-1/2 z-10 w-[3px] h-5 rounded-r-xl transition-opacity duration-300",
|
|
214
|
-
isDefaultWl ? "bg-cyan-300" : "",
|
|
215
|
-
activeApp === "gpages" ? "opacity-100" : "opacity-0",
|
|
216
|
-
)}
|
|
217
|
-
style={!isDefaultWl ? { backgroundColor: primaryColor } : undefined}
|
|
218
|
-
/>
|
|
308
|
+
/>
|
|
309
|
+
</div>
|
|
310
|
+
) : null}
|
|
219
311
|
</div>
|
|
220
312
|
</div>
|
|
221
313
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
|
|
5
|
+
export type DomMutationGuardHit = {
|
|
6
|
+
kind: 'removeChild' | 'insertBefore';
|
|
7
|
+
language: string;
|
|
8
|
+
languages: string;
|
|
9
|
+
documentLang: string;
|
|
10
|
+
translatedNodes: number;
|
|
11
|
+
path: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Reporta quando a guarda de mutação de DOM (`DOM_MUTATION_GUARD_SCRIPT`) entra em ação.
|
|
16
|
+
*
|
|
17
|
+
* Sem isso a correção fica cega: o app para de cair, mas ninguém descobre quantos clientes estão
|
|
18
|
+
* navegando com o tradutor do navegador ligado. Reporta UMA vez por sessão — o objetivo é medir
|
|
19
|
+
* quantas sessões são afetadas, não quantas operações de DOM foram salvas.
|
|
20
|
+
*/
|
|
21
|
+
export function useDomMutationGuardHit(onHit: (hit: DomMutationGuardHit) => void): void {
|
|
22
|
+
const onHitRef = useRef(onHit);
|
|
23
|
+
onHitRef.current = onHit;
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
let reported = false;
|
|
27
|
+
|
|
28
|
+
const report = (kind: 'removeChild' | 'insertBefore') => {
|
|
29
|
+
if (reported) return;
|
|
30
|
+
reported = true;
|
|
31
|
+
window.__gpDomGuardOnHit = null;
|
|
32
|
+
onHitRef.current({
|
|
33
|
+
kind,
|
|
34
|
+
language: navigator.language,
|
|
35
|
+
languages: navigator.languages?.join(',') ?? '',
|
|
36
|
+
documentLang: document.documentElement.lang,
|
|
37
|
+
// <font> sem classe é a assinatura do Google Tradutor
|
|
38
|
+
translatedNodes: document.querySelectorAll('font[style]').length,
|
|
39
|
+
path: window.location.pathname,
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Disparos que aconteceram antes deste componente montar.
|
|
44
|
+
const already = window.__gpDomGuard;
|
|
45
|
+
if (already && (already.removeChild > 0 || already.insertBefore > 0)) {
|
|
46
|
+
report(already.insertBefore > 0 ? 'insertBefore' : 'removeChild');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
window.__gpDomGuardOnHit = report;
|
|
51
|
+
return () => {
|
|
52
|
+
window.__gpDomGuardOnHit = null;
|
|
53
|
+
};
|
|
54
|
+
}, []);
|
|
55
|
+
}
|
|
@@ -39,14 +39,24 @@ export interface ResolveLocaleOptions {
|
|
|
39
39
|
userLanguage?: string | null;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
function localeFromAcceptLanguage(header: string | null): Locale | null {
|
|
43
|
+
if (!header) return null;
|
|
44
|
+
for (const entry of header.split(',')) {
|
|
45
|
+
const locale = normalizeLocale(entry.split(';')[0].trim());
|
|
46
|
+
if (locale) return locale;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
42
51
|
/**
|
|
43
52
|
* Resolve o locale da requisição (server-side). Ordem de prioridade:
|
|
44
53
|
*
|
|
45
54
|
* 1. **Whitelabel não-padrão** (`id_wl != 1`) → `pt` (por enquanto, whitelabels não são localizadas).
|
|
46
55
|
* 2. **Preferência da conta** (`userLanguage`), quando fornecida.
|
|
47
56
|
* 3. **Cookie `NEXT_LOCALE`** — cache da preferência ou escolha manual do visitante.
|
|
48
|
-
* 4. **
|
|
49
|
-
* 5. **
|
|
57
|
+
* 4. **Header `Accept-Language`** do navegador.
|
|
58
|
+
* 5. **Inferência por país** — JWT (`session.location.country`) ou header `CF-IPCountry`.
|
|
59
|
+
* 6. **Fallback** → {@link DEFAULT_LOCALE}.
|
|
50
60
|
*/
|
|
51
61
|
export async function resolveLocale(options: ResolveLocaleOptions = {}): Promise<Locale> {
|
|
52
62
|
const cookieStore = await cookies();
|
|
@@ -67,12 +77,17 @@ export async function resolveLocale(options: ResolveLocaleOptions = {}): Promise
|
|
|
67
77
|
const fromCookie = normalizeLocale(cookieStore.get(LOCALE_COOKIE)?.value);
|
|
68
78
|
if (fromCookie) return fromCookie;
|
|
69
79
|
|
|
70
|
-
// 4. Inferência por país (geo)
|
|
71
80
|
const headersList = await headers();
|
|
81
|
+
|
|
82
|
+
// 4. Header Accept-Language do navegador
|
|
83
|
+
const fromAcceptLanguage = localeFromAcceptLanguage(headersList.get('accept-language'));
|
|
84
|
+
if (fromAcceptLanguage) return fromAcceptLanguage;
|
|
85
|
+
|
|
86
|
+
// 5. Inferência por país (geo)
|
|
72
87
|
const country = jwt?.country ?? headersList.get(GEO_HEADER);
|
|
73
88
|
const fromGeo = localeFromCountry(country);
|
|
74
89
|
if (fromGeo) return fromGeo;
|
|
75
90
|
|
|
76
|
-
//
|
|
91
|
+
// 6. Fallback
|
|
77
92
|
return DEFAULT_LOCALE;
|
|
78
93
|
}
|
package/src/index.ts
CHANGED
|
@@ -515,6 +515,9 @@ export { default as usePasswordVisibility } from "./hooks/usePasswordVisibility"
|
|
|
515
515
|
export { default as useCountdownTimer } from "./hooks/useCountdownTimer";
|
|
516
516
|
export { default as useIsMobile } from "./hooks/useIsMobile";
|
|
517
517
|
export { useDebounce } from "./hooks/useDebounce";
|
|
518
|
+
export { useDomMutationGuardHit } from "./hooks/useDomMutationGuardHit";
|
|
519
|
+
export type { DomMutationGuardHit } from "./hooks/useDomMutationGuardHit";
|
|
520
|
+
export { DOM_MUTATION_GUARD_SCRIPT } from "./utils/dom/dom-mutation-guard";
|
|
518
521
|
export { useDebouncedEffect } from "./hooks/useDebouncedEffect";
|
|
519
522
|
export { useDebounceState } from "./hooks/useDebounceState";
|
|
520
523
|
|
|
@@ -41,6 +41,8 @@ import {
|
|
|
41
41
|
VerifyEmailResponse,
|
|
42
42
|
} from "../schema";
|
|
43
43
|
import { findWhitelabel } from "../../../server";
|
|
44
|
+
import { resolveLocale } from "../../../i18n/resolve-locale";
|
|
45
|
+
import { localeToApiLanguage } from "../../../utils/intl/locales";
|
|
44
46
|
|
|
45
47
|
const AUTH_COOKIE_NAME = "greatapps";
|
|
46
48
|
const COOKIE_MAX_AGE = 60 * 60 * 24 * 30;
|
|
@@ -295,12 +297,15 @@ class AuthService {
|
|
|
295
297
|
|
|
296
298
|
const idPlan = await this.resolvePlanId(data.idPlan);
|
|
297
299
|
|
|
300
|
+
const whitelabel = await findWhitelabel().catch(() => null);
|
|
301
|
+
const language = localeToApiLanguage(await resolveLocale({ whitelabelId: whitelabel?.id ?? null }));
|
|
302
|
+
|
|
298
303
|
const payload = {
|
|
299
304
|
onboarding_token: data.onboardingToken,
|
|
300
305
|
name: data.user.name,
|
|
301
306
|
bussiness_type: data.businessType ?? 0,
|
|
302
|
-
language
|
|
303
|
-
timezone:
|
|
307
|
+
language,
|
|
308
|
+
timezone: clientInfo.timezone,
|
|
304
309
|
currency: "BRL",
|
|
305
310
|
location: clientInfo.location,
|
|
306
311
|
ip: clientInfo.ip,
|
|
@@ -317,7 +322,7 @@ class AuthService {
|
|
|
317
322
|
ddi: data.user.ddi || "55",
|
|
318
323
|
phone: data.user.phone || "",
|
|
319
324
|
profile: "owner",
|
|
320
|
-
language
|
|
325
|
+
language,
|
|
321
326
|
},
|
|
322
327
|
subscription:
|
|
323
328
|
isFreePlan(idPlan)
|
|
@@ -411,11 +416,14 @@ class AuthService {
|
|
|
411
416
|
|
|
412
417
|
const idPlan = await this.resolvePlanId(data.idPlan);
|
|
413
418
|
|
|
419
|
+
const whitelabel = await findWhitelabel().catch(() => null);
|
|
420
|
+
const language = localeToApiLanguage(await resolveLocale({ whitelabelId: whitelabel?.id ?? null }));
|
|
421
|
+
|
|
414
422
|
const payload: RegisterApiRequest = {
|
|
415
423
|
name: data.accountName,
|
|
416
424
|
bussiness_type: data.businessType ?? 0,
|
|
417
|
-
language
|
|
418
|
-
timezone:
|
|
425
|
+
language,
|
|
426
|
+
timezone: clientInfo.timezone,
|
|
419
427
|
currency: "BRL",
|
|
420
428
|
location: clientInfo.location,
|
|
421
429
|
ip: clientInfo.ip,
|
|
@@ -433,7 +441,7 @@ class AuthService {
|
|
|
433
441
|
phone: data.phone,
|
|
434
442
|
password: data.password,
|
|
435
443
|
profile: "owner",
|
|
436
|
-
language
|
|
444
|
+
language,
|
|
437
445
|
},
|
|
438
446
|
subscription:
|
|
439
447
|
isFreePlan(idPlan)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guarda contra mutação externa do DOM (tradutor do navegador, extensões).
|
|
3
|
+
*
|
|
4
|
+
* PROBLEMA: o Google Tradutor do Chrome substitui cada text node por `<font><font>…</font></font>`.
|
|
5
|
+
* O React continua guardando a referência do text node ORIGINAL no fiber. No commit seguinte ele
|
|
6
|
+
* chama `parent.insertBefore(novo, textNodeAntigo)` ou `parent.removeChild(textNodeAntigo)` — e o nó
|
|
7
|
+
* já não é mais filho daquele pai. O DOM levanta `NotFoundError`, o erro sobe até o root e o app
|
|
8
|
+
* inteiro cai no `global-error.tsx` (evento `render.root_crashed` no Sentry).
|
|
9
|
+
*
|
|
10
|
+
* Isso derrubou clientes reais em CO/PE/MX/AR/FR (zero casos no BR, onde a página já está no idioma
|
|
11
|
+
* do usuário e o Chrome não oferece tradução), inclusive em cima da tela de trocar cartão — o cliente
|
|
12
|
+
* ficava sem conseguir pagar.
|
|
13
|
+
*
|
|
14
|
+
* SOLUÇÃO: transformar essas duas operações em no-op quando o nó de referência já não pertence mais
|
|
15
|
+
* ao pai. O React segue adiante, a página não morre, e o trecho traduzido apenas deixa de receber
|
|
16
|
+
* aquela atualização pontual (o próximo re-render completo normaliza).
|
|
17
|
+
*
|
|
18
|
+
* Precisa rodar ANTES da hidratação, por isso é injetado como `<script>` inline no `<head>` do root
|
|
19
|
+
* layout em vez de virar um `useEffect`.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
declare global {
|
|
23
|
+
interface Window {
|
|
24
|
+
/** Contadores de quantas vezes a guarda entrou em ação nesta sessão. */
|
|
25
|
+
__gpDomGuard?: { removeChild: number; insertBefore: number };
|
|
26
|
+
/** Hook opcional preenchido pelo reporter para avisar o Sentry no primeiro disparo. */
|
|
27
|
+
__gpDomGuardOnHit?: ((kind: 'removeChild' | 'insertBefore') => void) | null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const DOM_MUTATION_GUARD_SCRIPT = `(function(){
|
|
32
|
+
if(typeof Node==="undefined"||!Node.prototype)return;
|
|
33
|
+
if(window.__gpDomGuard)return;
|
|
34
|
+
var s={removeChild:0,insertBefore:0};
|
|
35
|
+
window.__gpDomGuard=s;
|
|
36
|
+
window.__gpDomGuardOnHit=window.__gpDomGuardOnHit||null;
|
|
37
|
+
function hit(k){s[k]++;try{if(window.__gpDomGuardOnHit)window.__gpDomGuardOnHit(k)}catch(e){}}
|
|
38
|
+
var removeChild=Node.prototype.removeChild;
|
|
39
|
+
Node.prototype.removeChild=function(child){
|
|
40
|
+
if(child&&child.parentNode!==this){hit("removeChild");return child}
|
|
41
|
+
return removeChild.apply(this,arguments)};
|
|
42
|
+
var insertBefore=Node.prototype.insertBefore;
|
|
43
|
+
Node.prototype.insertBefore=function(newNode,referenceNode){
|
|
44
|
+
if(referenceNode&&referenceNode.parentNode!==this){hit("insertBefore");return newNode}
|
|
45
|
+
return insertBefore.apply(this,arguments)};
|
|
46
|
+
})();`;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Locale } from '../../i18n/config';
|
|
2
|
+
|
|
1
3
|
export type LocaleOption = {
|
|
2
4
|
value: string;
|
|
3
5
|
label: string;
|
|
@@ -14,3 +16,8 @@ export const LANGUAGE_OPTIONS: LocaleOption[] = [
|
|
|
14
16
|
export function buildLocaleOptions(): LocaleOption[] {
|
|
15
17
|
return LANGUAGE_OPTIONS;
|
|
16
18
|
}
|
|
19
|
+
|
|
20
|
+
export function localeToApiLanguage(locale: Locale): string {
|
|
21
|
+
const option = LANGUAGE_OPTIONS.find((o) => o.value === locale);
|
|
22
|
+
return option?.apiValue ?? locale;
|
|
23
|
+
}
|