@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.
- package/.env.example +8 -0
- package/README.md +29 -85
- package/app/controllers/messageController.js +133 -1
- package/app/modules/stickerPackModule/stickerPackCatalogHttp.js +559 -136
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +19 -1
- package/app/services/lidMapService.js +4 -1
- package/app/services/whatsappLoginLinkService.js +232 -0
- package/database/migrations/20260228_0021_sticker_web_google_owner_phone.sql +83 -0
- package/package.json +1 -1
- package/public/index.html +101 -10
- package/public/js/apps/createPackApp.js +59 -272
- package/public/js/apps/homeApp.js +106 -0
- package/public/js/apps/loginApp.js +459 -0
- package/public/js/apps/stickersApp.js +34 -37
- package/public/js/apps/userApp.js +244 -0
- package/public/js/runtime/react-runtime.js +1 -0
- package/public/login/index.html +333 -0
- package/public/stickers/create/index.html +2 -1
- package/public/stickers/index.html +2 -1
- package/public/user/index.html +367 -0
- package/scripts/cache-bust.mjs +65 -11
|
@@ -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
|
-
<
|
|
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-
|
|
1671
|
-
<h1 className="truncate text-2xl font-extrabold tracking-tight text-slate-100 sm:text-3xl"
|
|
1672
|
-
<p className="truncate text-xs text-slate-400"
|
|
1673
|
-
<p className="mt-0.5 text-[11px] text-slate-500"
|
|
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">
|
|
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
|
-
? '
|
|
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
|
-
${
|
|
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
|
/>
|