@kaikybrofc/omnizap-system 2.2.3 → 2.2.4

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.
@@ -0,0 +1,459 @@
1
+ /* global document, window, fetch, URLSearchParams */
2
+
3
+ const GOOGLE_GSI_SCRIPT_SRC = 'https://accounts.google.com/gsi/client';
4
+ const DEFAULT_API_BASE_PATH = '/api/sticker-packs';
5
+
6
+ const root = document.getElementById('login-app-root');
7
+
8
+ if (root) {
9
+ const ui = {
10
+ status: document.getElementById('login-status'),
11
+ hint: document.getElementById('login-hint'),
12
+ error: document.getElementById('login-error'),
13
+ googleArea: document.getElementById('google-login-area'),
14
+ googleButton: document.querySelector('[data-google-login-button]'),
15
+ googleState: document.getElementById('google-login-state'),
16
+ summary: document.getElementById('login-summary'),
17
+ summaryOwner: document.getElementById('login-summary-owner'),
18
+ whatsappCta: document.getElementById('whatsapp-cta'),
19
+ whatsappCtaLink: document.getElementById('whatsapp-cta-link'),
20
+ whatsappCtaMeta: document.getElementById('whatsapp-cta-meta'),
21
+ successActions: document.getElementById('login-success-actions'),
22
+ successChat: document.getElementById('login-success-chat'),
23
+ successHome: document.getElementById('login-success-home'),
24
+ };
25
+
26
+ const state = {
27
+ apiBasePath: String(root.dataset.apiBasePath || DEFAULT_API_BASE_PATH).trim() || DEFAULT_API_BASE_PATH,
28
+ googleClientId: '',
29
+ googleEnabled: false,
30
+ googleReady: false,
31
+ busy: false,
32
+ authenticated: false,
33
+ botPhone: '',
34
+ sessionOwnerPhone: '',
35
+ hint: readWhatsAppHintFromUrl(window.location.search),
36
+ };
37
+
38
+ const sessionApiPath = `${state.apiBasePath}/auth/google/session`;
39
+ const createConfigPath = `${state.apiBasePath}/create-config`;
40
+
41
+ const setText = (element, value) => {
42
+ if (!element) return;
43
+ element.textContent = String(value || '');
44
+ };
45
+
46
+ const normalizeDigits = (value) => String(value || '').replace(/\D+/g, '');
47
+
48
+ const showError = (message) => {
49
+ if (!ui.error) return;
50
+ const safe = String(message || '').trim();
51
+ ui.error.hidden = !safe;
52
+ if (safe) ui.error.textContent = safe;
53
+ };
54
+
55
+ const setBusy = (value) => {
56
+ state.busy = Boolean(value);
57
+ if (ui.googleButton) {
58
+ ui.googleButton.style.opacity = state.busy ? '0.55' : '1';
59
+ ui.googleButton.style.pointerEvents = state.busy ? 'none' : 'auto';
60
+ }
61
+ setText(ui.googleState, state.busy ? 'Finalizando login...' : state.googleReady ? '' : 'Carregando login Google...');
62
+ };
63
+
64
+ const formatPhone = (digits) => {
65
+ const value = normalizeDigits(digits);
66
+ if (!value) return '';
67
+ if (value.length <= 4) return value;
68
+ return `${value.slice(0, 2)} ${value.slice(2, -4)}-${value.slice(-4)}`.trim();
69
+ };
70
+
71
+ const buildWhatsappStartUrl = (phoneDigits) => {
72
+ const command = 'iniciar';
73
+ const digits = normalizeDigits(phoneDigits);
74
+ const params = new URLSearchParams({
75
+ text: command,
76
+ type: 'custom_url',
77
+ app_absent: '0',
78
+ });
79
+ if (digits) params.set('phone', digits);
80
+ return `https://api.whatsapp.com/send/?${params.toString()}`;
81
+ };
82
+
83
+ const buildWhatsappMenuUrl = (phoneDigits) => {
84
+ const command = '/menu';
85
+ const digits = normalizeDigits(phoneDigits);
86
+ const params = new URLSearchParams({
87
+ text: command,
88
+ type: 'custom_url',
89
+ app_absent: '0',
90
+ });
91
+ if (digits) params.set('phone', digits);
92
+ return `https://api.whatsapp.com/send/?${params.toString()}`;
93
+ };
94
+
95
+ const extractPhoneFromJid = (jid) => {
96
+ const raw = String(jid || '').trim();
97
+ if (!raw.includes('@')) return '';
98
+ const userPart = raw.split('@')[0] || '';
99
+ const user = userPart.split(':')[0] || '';
100
+ return normalizeDigits(user);
101
+ };
102
+
103
+ const canUseGoogleLogin = () => Boolean(state.hint.phone);
104
+
105
+ const renderSuccessActions = (sessionData) => {
106
+ if (!ui.successActions) return;
107
+ const authenticated = Boolean(sessionData?.authenticated);
108
+ ui.successActions.hidden = !authenticated;
109
+ if (!authenticated) return;
110
+
111
+ if (ui.successChat) {
112
+ ui.successChat.href = buildWhatsappMenuUrl(state.botPhone);
113
+ }
114
+ if (ui.successHome) {
115
+ ui.successHome.href = '/';
116
+ }
117
+ };
118
+
119
+ const renderWhatsAppCta = () => {
120
+ if (!ui.whatsappCta || !ui.whatsappCtaLink) return;
121
+
122
+ if (state.hint.phone) {
123
+ ui.whatsappCta.hidden = true;
124
+ return;
125
+ }
126
+
127
+ if (state.sessionOwnerPhone) {
128
+ ui.whatsappCta.hidden = true;
129
+ return;
130
+ }
131
+
132
+ if (state.authenticated) {
133
+ ui.whatsappCta.hidden = true;
134
+ return;
135
+ }
136
+
137
+ ui.whatsappCta.hidden = false;
138
+
139
+ ui.whatsappCtaLink.href = buildWhatsappStartUrl(state.botPhone);
140
+ if (state.botPhone) {
141
+ setText(ui.whatsappCtaMeta, `Bot detectado: +${formatPhone(state.botPhone)}.`);
142
+ } else {
143
+ setText(ui.whatsappCtaMeta, 'Se necessário, escolha o contato do bot no WhatsApp e envie "iniciar".');
144
+ }
145
+ };
146
+
147
+ const renderHint = () => {
148
+ if (!state.hint.hasPayload) {
149
+ setText(
150
+ ui.hint,
151
+ 'Voce abriu esta pagina direto. Por seguranca, gere seu link no WhatsApp clicando no botao abaixo e enviando "iniciar".',
152
+ );
153
+ return;
154
+ }
155
+
156
+ if (!state.hint.phone) {
157
+ setText(
158
+ ui.hint,
159
+ 'Este link nao trouxe um numero de WhatsApp valido. Ele pode ter sido alterado ou expirado. Gere um novo link enviando "iniciar".',
160
+ );
161
+ return;
162
+ }
163
+
164
+ setText(ui.hint, `Numero detectado para vinculo: +${formatPhone(state.hint.phone)}.`);
165
+ };
166
+
167
+ const renderSessionSummary = (sessionData) => {
168
+ if (!canUseGoogleLogin()) {
169
+ state.authenticated = false;
170
+ state.sessionOwnerPhone = '';
171
+ if (ui.summary) ui.summary.hidden = true;
172
+ renderSuccessActions(null);
173
+ renderWhatsAppCta();
174
+ return;
175
+ }
176
+
177
+ const authenticated = Boolean(sessionData?.authenticated);
178
+ state.authenticated = authenticated;
179
+ renderSuccessActions(sessionData);
180
+ if (ui.summary) ui.summary.hidden = !authenticated;
181
+ if (!authenticated) {
182
+ state.sessionOwnerPhone = '';
183
+ renderWhatsAppCta();
184
+ return;
185
+ }
186
+
187
+ const ownerPhone = String(sessionData?.owner_phone || '').trim();
188
+ state.sessionOwnerPhone = ownerPhone;
189
+ if (ownerPhone) {
190
+ setText(ui.summaryOwner, `WhatsApp vinculado: +${formatPhone(ownerPhone)}`);
191
+ renderWhatsAppCta();
192
+ return;
193
+ }
194
+
195
+ if (state.hint.phone) {
196
+ setText(
197
+ ui.summaryOwner,
198
+ `Numero detectado: +${formatPhone(state.hint.phone)}. Clique no botao do Google para concluir o vinculo.`,
199
+ );
200
+ renderWhatsAppCta();
201
+ return;
202
+ }
203
+
204
+ setText(ui.summaryOwner, 'Conta Google ativa. Vincule o WhatsApp abrindo o link pelo "iniciar".');
205
+ renderWhatsAppCta();
206
+ };
207
+
208
+ const fetchJson = async (url, init = {}) => {
209
+ const response = await fetch(url, {
210
+ credentials: 'include',
211
+ ...init,
212
+ });
213
+ let payload = null;
214
+ try {
215
+ payload = await response.json();
216
+ } catch {
217
+ payload = null;
218
+ }
219
+
220
+ if (!response.ok) {
221
+ const err = new Error(payload?.error || `Falha HTTP ${response.status}`);
222
+ err.statusCode = response.status;
223
+ err.payload = payload;
224
+ throw err;
225
+ }
226
+ return payload || {};
227
+ };
228
+
229
+ const buildSessionPayload = (googleCredential) => {
230
+ const payload = {
231
+ google_id_token: String(googleCredential || '').trim(),
232
+ };
233
+
234
+ if (state.hint.phone) {
235
+ payload.wa = state.hint.phone;
236
+ if (state.hint.ts) payload.wa_ts = state.hint.ts;
237
+ if (state.hint.sig) payload.wa_sig = state.hint.sig;
238
+ }
239
+
240
+ return payload;
241
+ };
242
+
243
+ const handleGoogleCredential = async (credential) => {
244
+ const token = String(credential || '').trim();
245
+ if (!token) {
246
+ showError('Falha ao receber token do Google. Tente novamente.');
247
+ return;
248
+ }
249
+
250
+ setBusy(true);
251
+ showError('');
252
+ try {
253
+ const sessionPayload = await fetchJson(sessionApiPath, {
254
+ method: 'POST',
255
+ headers: {
256
+ 'Content-Type': 'application/json; charset=utf-8',
257
+ },
258
+ body: JSON.stringify(buildSessionPayload(token)),
259
+ });
260
+ const sessionData = sessionPayload?.data || {};
261
+ if (!sessionData?.authenticated) {
262
+ throw new Error('Nao foi possivel criar a sessao Google.');
263
+ }
264
+ setText(ui.status, 'Login concluido. Seu acesso foi vinculado com sucesso.');
265
+ renderSessionSummary(sessionData);
266
+ setText(ui.googleState, 'Login Google ativo.');
267
+ } catch (error) {
268
+ showError(error?.message || 'Falha ao concluir login Google.');
269
+ setText(ui.status, 'Nao foi possivel concluir o login agora.');
270
+ renderSessionSummary(null);
271
+ } finally {
272
+ setBusy(false);
273
+ }
274
+ };
275
+
276
+ const loadGoogleScript = () =>
277
+ new Promise((resolve, reject) => {
278
+ if (window.google?.accounts?.id) {
279
+ resolve(window.google.accounts.id);
280
+ return;
281
+ }
282
+
283
+ const existing = document.querySelector(`script[src="${GOOGLE_GSI_SCRIPT_SRC}"]`);
284
+ if (existing) {
285
+ existing.addEventListener('load', () => resolve(window.google?.accounts?.id || null), { once: true });
286
+ existing.addEventListener('error', () => reject(new Error('Falha ao carregar SDK Google.')), { once: true });
287
+ return;
288
+ }
289
+
290
+ const script = document.createElement('script');
291
+ script.src = GOOGLE_GSI_SCRIPT_SRC;
292
+ script.async = true;
293
+ script.defer = true;
294
+ script.onload = () => resolve(window.google?.accounts?.id || null);
295
+ script.onerror = () => reject(new Error('Falha ao carregar SDK Google.'));
296
+ document.head.appendChild(script);
297
+ });
298
+
299
+ const mountGoogleButton = async () => {
300
+ if (!canUseGoogleLogin()) return;
301
+ if (!state.googleEnabled || !state.googleClientId) {
302
+ setText(ui.googleState, 'Login Google desabilitado neste ambiente.');
303
+ return;
304
+ }
305
+ if (!ui.googleButton) return;
306
+
307
+ setText(ui.googleState, 'Carregando login Google...');
308
+ try {
309
+ const accounts = await loadGoogleScript();
310
+ if (!accounts) throw new Error('SDK Google nao disponivel.');
311
+
312
+ accounts.initialize({
313
+ client_id: state.googleClientId,
314
+ callback: (response) => {
315
+ void handleGoogleCredential(response?.credential);
316
+ },
317
+ auto_select: false,
318
+ cancel_on_tap_outside: true,
319
+ });
320
+
321
+ ui.googleButton.innerHTML = '';
322
+ const measuredWidth = Math.floor(Number(ui.googleButton.clientWidth || 0));
323
+ const buttonWidth = Math.max(180, Math.min(320, measuredWidth || 320));
324
+ accounts.renderButton(ui.googleButton, {
325
+ type: 'standard',
326
+ theme: 'filled_black',
327
+ size: 'large',
328
+ text: 'signin_with',
329
+ shape: 'pill',
330
+ width: buttonWidth,
331
+ });
332
+
333
+ state.googleReady = true;
334
+ setText(ui.googleState, '');
335
+ } catch (error) {
336
+ state.googleReady = false;
337
+ setText(ui.googleState, '');
338
+ showError(error?.message || 'Falha ao carregar login Google.');
339
+ }
340
+ };
341
+
342
+ const loadConfig = async () => {
343
+ try {
344
+ const payload = await fetchJson(createConfigPath, { method: 'GET' });
345
+ const google = payload?.data?.auth?.google || {};
346
+ state.googleEnabled = Boolean(google.enabled);
347
+ state.googleClientId = String(google.client_id || '').trim();
348
+ } catch {
349
+ state.googleEnabled = false;
350
+ state.googleClientId = '';
351
+ }
352
+ };
353
+
354
+ const loadBotPhone = async () => {
355
+ let phone = '';
356
+ try {
357
+ const contactPayload = await fetchJson(`${state.apiBasePath}/bot-contact`, { method: 'GET' });
358
+ phone = normalizeDigits(contactPayload?.data?.phone || '');
359
+ } catch {
360
+ phone = '';
361
+ }
362
+
363
+ try {
364
+ if (!phone) {
365
+ const summaryPayload = await fetchJson(`${state.apiBasePath}/system-summary`, { method: 'GET' });
366
+ const botPhone = normalizeDigits(summaryPayload?.data?.bot?.phone || '');
367
+ if (botPhone) {
368
+ phone = botPhone;
369
+ } else {
370
+ const botJid = String(summaryPayload?.data?.bot?.jid || '').trim();
371
+ phone = extractPhoneFromJid(botJid);
372
+ }
373
+ }
374
+ } catch {
375
+ if (!phone) phone = '';
376
+ }
377
+
378
+ if (!phone) {
379
+ try {
380
+ const payload = await fetchJson(`${state.apiBasePath}?visibility=public&limit=1`, { method: 'GET' });
381
+ const firstPack = Array.isArray(payload?.data) ? payload.data[0] : null;
382
+ phone = normalizeDigits(firstPack?.whatsapp?.phone || '');
383
+ } catch {
384
+ phone = '';
385
+ }
386
+ }
387
+
388
+ state.botPhone = phone;
389
+ renderWhatsAppCta();
390
+ renderSuccessActions({ authenticated: Boolean(state.sessionOwnerPhone) });
391
+ };
392
+
393
+ const loadCurrentSession = async () => {
394
+ if (!canUseGoogleLogin()) {
395
+ renderSessionSummary(null);
396
+ return;
397
+ }
398
+ try {
399
+ const payload = await fetchJson(sessionApiPath, { method: 'GET' });
400
+ const sessionData = payload?.data || {};
401
+ if (sessionData?.authenticated) {
402
+ setText(ui.status, 'Sessao Google ativa neste navegador.');
403
+ renderSessionSummary(sessionData);
404
+ } else {
405
+ setText(ui.status, 'Use o login Google abaixo para entrar no OmniZap.');
406
+ renderSessionSummary(null);
407
+ }
408
+ } catch {
409
+ setText(ui.status, 'Nao foi possivel validar sua sessao atual.');
410
+ renderSessionSummary(null);
411
+ }
412
+ };
413
+
414
+ const renderGoogleLoginGate = () => {
415
+ const allowed = canUseGoogleLogin();
416
+ if (ui.googleArea) {
417
+ ui.googleArea.hidden = !allowed;
418
+ }
419
+ if (!allowed) {
420
+ setText(ui.status, 'Para fazer login, abra esta pagina pelo link do WhatsApp (envie "iniciar" no bot).');
421
+ setText(ui.googleState, '');
422
+ if (ui.summary) ui.summary.hidden = true;
423
+ renderSuccessActions(null);
424
+ showError('');
425
+ }
426
+ return allowed;
427
+ };
428
+
429
+ const init = async () => {
430
+ renderHint();
431
+ if (ui.whatsappCta && state.hint.phone) {
432
+ ui.whatsappCta.hidden = true;
433
+ }
434
+ renderWhatsAppCta();
435
+ renderSuccessActions(null);
436
+ const allowGoogleLogin = renderGoogleLoginGate();
437
+ await loadBotPhone();
438
+ if (!allowGoogleLogin) return;
439
+ await loadConfig();
440
+ await loadCurrentSession();
441
+ await mountGoogleButton();
442
+ };
443
+
444
+ void init();
445
+ }
446
+
447
+ function readWhatsAppHintFromUrl(search) {
448
+ const params = new URLSearchParams(search || '');
449
+ const phone = String(params.get('wa') || '').replace(/\D+/g, '');
450
+ const ts = String(params.get('wa_ts') || '').trim();
451
+ const sig = String(params.get('wa_sig') || '').trim();
452
+ const hasPayload = Boolean(phone || ts || sig);
453
+ return {
454
+ hasPayload,
455
+ phone,
456
+ ts,
457
+ sig,
458
+ };
459
+ }
@@ -1561,8 +1561,6 @@ function CreatorProfileDashboard({
1561
1561
  googleAuthBusy,
1562
1562
  googleAuthError,
1563
1563
  googleSessionChecked,
1564
- googleAuthUiReady,
1565
- googleButtonRef,
1566
1564
  myPacks,
1567
1565
  myPacksLoading,
1568
1566
  myPacksError,
@@ -1573,7 +1571,6 @@ function CreatorProfileDashboard({
1573
1571
  onOpenPublicPack,
1574
1572
  onOpenPackActions,
1575
1573
  onOpenManagePack,
1576
- onProfileAction,
1577
1574
  onRequestDeletePack,
1578
1575
  packActionBusyByKey = {},
1579
1576
  }) {
@@ -1649,8 +1646,7 @@ function CreatorProfileDashboard({
1649
1646
  <div className="flex flex-wrap items-center justify-between gap-2">
1650
1647
  <button type="button" onClick=${onBack} className="inline-flex items-center gap-2 rounded-xl border border-slate-700 px-3 py-2 text-sm text-slate-200 hover:bg-slate-800">← Catálogo</button>
1651
1648
  <div className="flex items-center gap-2">
1652
- <button type="button" onClick=${() => onProfileAction?.('edit-profile')} className="inline-flex h-10 items-center rounded-xl border border-emerald-500/35 bg-emerald-500/10 px-3 text-xs font-semibold text-emerald-100 hover:bg-emerald-500/20">✏️ Editar perfil</button>
1653
- <button type="button" onClick=${() => onProfileAction?.('settings')} className="inline-flex h-10 items-center rounded-xl border border-slate-700 px-3 text-xs text-slate-200 hover:bg-slate-800">⚙️</button>
1649
+ <a href="/user/" className="inline-flex h-10 items-center rounded-xl border border-emerald-500/35 bg-emerald-500/10 px-3 text-xs font-semibold text-emerald-100 hover:bg-emerald-500/20">👤 Minha conta</a>
1654
1650
  <button type="button" onClick=${onRefresh} disabled=${myPacksLoading || googleAuthBusy} className="inline-flex h-10 items-center rounded-xl border border-cyan-500/35 bg-cyan-500/10 px-3 text-xs font-semibold text-cyan-100 hover:bg-cyan-500/20 disabled:opacity-60">${myPacksLoading ? '...' : '⟳'}</button>
1655
1651
  </div>
1656
1652
  </div>
@@ -1667,10 +1663,10 @@ function CreatorProfileDashboard({
1667
1663
  className="h-20 w-20 rounded-2xl border border-slate-700 bg-slate-900 object-cover sm:h-24 sm:w-24"
1668
1664
  />
1669
1665
  <div className="min-w-0">
1670
- <div className="mb-1 inline-flex items-center gap-1 rounded-full border border-emerald-400/25 bg-emerald-500/10 px-2 py-0.5 text-[11px] font-semibold text-emerald-100">✅ Criador verificado</div>
1671
- <h1 className="truncate text-2xl font-extrabold tracking-tight text-slate-100 sm:text-3xl">${googleAuth?.user?.name || 'Meu perfil de packs'}</h1>
1672
- <p className="truncate text-xs text-slate-400">${googleAuth?.user?.email || 'Faça login com Google para vincular seus packs.'}</p>
1673
- <p className="mt-0.5 text-[11px] text-slate-500">Sessão ${googleAuth?.expiresAt ? `até ${new Date(googleAuth.expiresAt).toLocaleString('pt-BR')}` : hasGoogleLogin ? 'ativa' : 'não autenticada'}</p>
1666
+ <div className="mb-1 inline-flex items-center gap-1 rounded-full border border-cyan-400/25 bg-cyan-500/10 px-2 py-0.5 text-[11px] font-semibold text-cyan-100">🧩 Gestão de stickers</div>
1667
+ <h1 className="truncate text-2xl font-extrabold tracking-tight text-slate-100 sm:text-3xl">Gerenciamento de Packs</h1>
1668
+ <p className="truncate text-xs text-slate-400">Organize uploads, capa, ordem e publicação dos seus stickers.</p>
1669
+ <p className="mt-0.5 text-[11px] text-slate-500">Área exclusiva para gerenciamento dos seus packs.</p>
1674
1670
  </div>
1675
1671
  </div>
1676
1672
  ${hasGoogleLogin
@@ -1715,24 +1711,15 @@ function CreatorProfileDashboard({
1715
1711
  <section className="rounded-2xl border border-slate-800 bg-slate-900/80 p-3">
1716
1712
  <div className="space-y-2.5">
1717
1713
  <div className="rounded-xl border border-cyan-500/20 bg-cyan-500/5 p-3">
1718
- <p className="text-sm font-semibold text-cyan-200">Entrar com Google</p>
1714
+ <p className="text-sm font-semibold text-cyan-200">Sessão necessária para gerenciamento</p>
1719
1715
  <p className="mt-1 text-xs text-slate-300">
1720
1716
  ${googleLoginEnabled
1721
- ? 'Use a mesma conta Google usada na criação de packs para carregar e gerenciar tudo aqui.'
1717
+ ? 'Esta área é exclusiva para gerenciar seus packs e stickers. Redirecionando para o login...'
1722
1718
  : 'Login Google indisponível no momento.'}
1723
1719
  </p>
1724
1720
  </div>
1725
- ${googleLoginEnabled
1726
- ? html`
1727
- <div ref=${googleButtonRef} className="min-h-[42px] w-full max-w-[340px] overflow-hidden"></div>
1728
- ${!googleSessionChecked
1729
- ? html`<p className="text-xs text-slate-400">Verificando sessão Google...</p>`
1730
- : googleAuthBusy
1731
- ? html`<p className="text-xs text-slate-400">Conectando conta Google...</p>`
1732
- : !googleAuthUiReady && !googleAuthError
1733
- ? html`<p className="text-xs text-slate-400">Carregando login Google...</p>`
1734
- : null}
1735
- `
1721
+ ${!googleSessionChecked
1722
+ ? html`<p className="text-xs text-slate-400">Verificando sessão...</p>`
1736
1723
  : null}
1737
1724
  ${googleAuthError ? html`<p className="text-xs text-rose-300">${googleAuthError}</p>` : null}
1738
1725
  </div>
@@ -2444,6 +2431,7 @@ function StickersApp() {
2444
2431
  webPath: root?.dataset.webPath || '/stickers',
2445
2432
  apiBasePath: root?.dataset.apiBasePath || '/api/sticker-packs',
2446
2433
  orphanApiPath: root?.dataset.orphanApiPath || '/api/sticker-packs/orphan-stickers',
2434
+ loginPath: root?.dataset.loginPath || '/login',
2447
2435
  limit: parseIntSafe(root?.dataset.defaultLimit, 24),
2448
2436
  orphanLimit: parseIntSafe(root?.dataset.defaultOrphanLimit, 24),
2449
2437
  }),
@@ -2979,8 +2967,16 @@ function StickersApp() {
2979
2967
  if (!silent) setGoogleSessionChecked(false);
2980
2968
  try {
2981
2969
  const payload = await fetchJson(myProfileApiPath, { retry: 1 });
2982
- applyMyProfileData(payload);
2970
+ const applied = applyMyProfileData(payload);
2971
+ if (!applied?.authenticated) {
2972
+ redirectToLogin(`${config.webPath}/perfil`);
2973
+ return;
2974
+ }
2983
2975
  } catch (err) {
2976
+ if (Number(err?.status || 0) === 401 || Number(err?.status || 0) === 403) {
2977
+ redirectToLogin(`${config.webPath}/perfil`);
2978
+ return;
2979
+ }
2984
2980
  setMyPacks([]);
2985
2981
  setMyProfileStats({ total: 0, published: 0, drafts: 0, private: 0, unlisted: 0, public: 0 });
2986
2982
  setGoogleSessionChecked(true);
@@ -3510,7 +3506,22 @@ function StickersApp() {
3510
3506
  window.scrollTo({ top: 0, behavior: 'smooth' });
3511
3507
  };
3512
3508
 
3509
+ const buildLoginRedirectUrl = (nextPath = '') => {
3510
+ const next = String(nextPath || '').trim() || `${config.webPath}/perfil`;
3511
+ const loginUrl = new URL(config.loginPath || '/login', window.location.origin);
3512
+ loginUrl.searchParams.set('next', next);
3513
+ return `${loginUrl.pathname}${loginUrl.search}`;
3514
+ };
3515
+
3516
+ const redirectToLogin = (nextPath = '') => {
3517
+ window.location.assign(buildLoginRedirectUrl(nextPath));
3518
+ };
3519
+
3513
3520
  const openProfile = (push = true) => {
3521
+ if (!hasGoogleLogin) {
3522
+ redirectToLogin(`${config.webPath}/perfil`);
3523
+ return;
3524
+ }
3514
3525
  googlePromptAttemptedRef.current = false;
3515
3526
  if (push) window.history.pushState({}, '', `${config.webPath}/perfil`);
3516
3527
  setCurrentView('profile');
@@ -3613,17 +3624,6 @@ function StickersApp() {
3613
3624
  }
3614
3625
  };
3615
3626
 
3616
- const handleProfileAction = (actionKey) => {
3617
- if (actionKey === 'edit-profile') {
3618
- pushProfileToast('Edição de perfil do criador será adicionada na próxima etapa.', 'warning');
3619
- return;
3620
- }
3621
- if (actionKey === 'settings') {
3622
- pushProfileToast('Configurações do criador ainda não têm tela dedicada.', 'warning');
3623
- return;
3624
- }
3625
- };
3626
-
3627
3627
  const handlePackActionsSheetAction = async (actionKey, pack) => {
3628
3628
  if (!pack?.pack_key) return;
3629
3629
  if (actionKey === 'manage' || actionKey === 'edit') {
@@ -4348,8 +4348,6 @@ function StickersApp() {
4348
4348
  googleAuthBusy=${googleAuthBusy}
4349
4349
  googleAuthError=${googleAuthError}
4350
4350
  googleSessionChecked=${googleSessionChecked}
4351
- googleAuthUiReady=${googleAuthUiReady}
4352
- googleButtonRef=${googleButtonRef}
4353
4351
  myPacks=${myPacks}
4354
4352
  myPacksLoading=${myPacksLoading}
4355
4353
  myPacksError=${myPacksError}
@@ -4360,7 +4358,6 @@ function StickersApp() {
4360
4358
  onOpenPublicPack=${openPack}
4361
4359
  onOpenPackActions=${openPackActionsSheet}
4362
4360
  onOpenManagePack=${(pack) => openManagePackByKey(pack?.pack_key || '')}
4363
- onProfileAction=${handleProfileAction}
4364
4361
  onRequestDeletePack=${requestDeletePack}
4365
4362
  packActionBusyByKey=${packActionBusyByKey}
4366
4363
  />