@kaikybrofc/omnizap-system 2.3.1 → 2.3.2

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.
Files changed (43) hide show
  1. package/README.md +20 -18
  2. package/app/controllers/messageController.js +473 -255
  3. package/app/modules/analyticsModule/messageAnalysisEventRepository.js +83 -0
  4. package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +1 -3
  5. package/app/observability/metrics.js +6 -3
  6. package/app/services/googleWebLinkService.js +77 -0
  7. package/database/index.js +2 -0
  8. package/database/migrations/20260301_0028_message_analysis_event.sql +32 -0
  9. package/database/migrations/20260301_0029_admin_action_audit.sql +16 -0
  10. package/package.json +1 -1
  11. package/public/index.html +12 -8
  12. package/public/js/apps/homeApp.js +75 -30
  13. package/public/js/apps/loginApp.js +184 -29
  14. package/public/js/apps/stickersAdminApp.js +3 -9
  15. package/public/js/apps/userApp.js +985 -55
  16. package/public/js/apps/userProfileApp.js +244 -0
  17. package/public/login/index.html +430 -100
  18. package/public/termos-de-uso/index.html +1 -1
  19. package/public/user/index.html +2 -180
  20. package/public/user/systemadm/index.html +774 -0
  21. package/server/controllers/stickerCatalog/nonCatalogHandlers.js +208 -0
  22. package/server/controllers/stickerCatalogController.js +1186 -363
  23. package/server/controllers/systemAdminController.js +141 -0
  24. package/server/controllers/userController.js +87 -0
  25. package/server/http/httpServer.js +72 -32
  26. package/server/middleware/cachePolicy.js +24 -0
  27. package/server/middleware/cachePolicyHelpers.js +2 -0
  28. package/server/middleware/rateLimit.js +82 -0
  29. package/server/middleware/requestLogger.js +16 -0
  30. package/server/middleware/requireAdminAuth.js +42 -0
  31. package/server/middleware/securityHeaders.js +6 -0
  32. package/server/routes/admin/systemAdminRouter.js +56 -0
  33. package/server/routes/health/healthRouter.js +41 -0
  34. package/server/routes/indexRouter.js +203 -0
  35. package/server/routes/metrics/metricsRouter.js +13 -0
  36. package/server/routes/stickerCatalog/catalogHandlers/catalogAdminHttp.js +44 -0
  37. package/server/routes/stickerCatalog/stickerApiRouter.js +84 -0
  38. package/server/routes/stickerCatalog/stickerDataRouter.js +140 -0
  39. package/server/routes/stickerCatalog/stickerSiteRouter.js +43 -0
  40. package/server/routes/user/userRouter.js +56 -0
  41. package/server/utils/safePath.js +26 -0
  42. package/server/routes/metricsRoute.js +0 -7
  43. package/server/routes/stickerCatalogRoute.js +0 -20
@@ -25,15 +25,15 @@
25
25
  </script>
26
26
  <style>
27
27
  :root {
28
- --bg: #0f172a;
29
- --bg-2: #111827;
30
- --card: #1e293bcf;
31
- --line: rgba(255, 255, 255, 0.05);
28
+ --bg: #070f20;
29
+ --bg-2: #0b1730;
30
+ --card: #10213dcc;
31
+ --line: rgba(148, 163, 184, 0.28);
32
32
  --text: #f8fafc;
33
- --muted: #94a3b8;
33
+ --muted: #9eb1cf;
34
34
  --primary: #22c55e;
35
35
  --error: #f87171;
36
- --radius: 18px;
36
+ --radius: 20px;
37
37
  }
38
38
 
39
39
  * {
@@ -50,28 +50,31 @@
50
50
  body {
51
51
  color: var(--text);
52
52
  font-family: 'Manrope', system-ui, sans-serif;
53
- background: radial-gradient(55rem 22rem at -10% -10%, #2563eb30, transparent 60%), radial-gradient(55rem 22rem at 110% -8%, #7c3aed22, transparent 56%), linear-gradient(160deg, var(--bg), var(--bg-2));
53
+ background:
54
+ radial-gradient(56rem 25rem at -8% -16%, #2563eb2f, transparent 62%),
55
+ radial-gradient(54rem 22rem at 108% -8%, #22c55e1f, transparent 60%),
56
+ linear-gradient(165deg, var(--bg), var(--bg-2));
54
57
  min-height: 100vh;
55
58
  display: flex;
56
59
  align-items: center;
57
60
  justify-content: center;
58
- padding: 24px 14px;
61
+ padding: 28px 14px;
59
62
  }
60
63
 
61
64
  .page {
62
- width: min(760px, 100%);
63
- border: 1px solid rgba(255, 255, 255, 0.05);
64
- border-radius: 24px;
65
- background: linear-gradient(150deg, #111827e8, #1e293bee);
65
+ width: min(780px, 100%);
66
+ border: 1px solid rgba(148, 163, 184, 0.18);
67
+ border-radius: 26px;
68
+ background: linear-gradient(150deg, #0a1429f0, #0f1f3ce8);
66
69
  box-shadow:
67
- 0 18px 44px #0209166e,
68
- inset 0 1px 0 #95c1ff1a;
70
+ 0 24px 52px #02091682,
71
+ inset 0 1px 0 #95c1ff1c;
69
72
  overflow: hidden;
70
73
  }
71
74
 
72
75
  .head {
73
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
74
- padding: 20px 22px;
76
+ border-bottom: 1px solid rgba(148, 163, 184, 0.16);
77
+ padding: 18px 24px;
75
78
  display: flex;
76
79
  align-items: center;
77
80
  justify-content: flex-start;
@@ -94,56 +97,79 @@
94
97
  width: 30px;
95
98
  height: 30px;
96
99
  border-radius: 50%;
97
- border: 1px solid rgba(255, 255, 255, 0.05);
100
+ border: 1px solid rgba(148, 163, 184, 0.24);
98
101
  object-fit: cover;
99
102
  }
100
103
 
101
104
  .btn {
102
- border: 1px solid var(--line);
103
- border-radius: 12px;
104
- background: #101a2f;
105
- color: var(--text);
105
+ border: 1px solid #385180;
106
+ border-radius: 13px;
107
+ background: #122446;
108
+ color: #d6e8ff;
106
109
  text-decoration: none;
107
- padding: 9px 12px;
110
+ padding: 11px 14px;
108
111
  font-size: 14px;
109
- font-weight: 600;
112
+ font-weight: 700;
113
+ text-align: center;
110
114
  transition:
111
115
  transform 0.2s ease,
112
116
  border-color 0.2s ease,
113
- box-shadow 0.2s ease;
117
+ box-shadow 0.2s ease,
118
+ background-color 0.2s ease;
114
119
  cursor: pointer;
115
120
  }
116
121
 
117
122
  .btn:hover {
118
- transform: translateY(-1px);
119
- border-color: #2563eb;
120
- box-shadow: 0 10px 20px #02091650;
123
+ transform: translateY(-2px);
124
+ border-color: #60a5fa;
125
+ box-shadow: 0 14px 28px #02091666;
121
126
  }
122
127
 
123
128
  .btn.primary {
124
- border-color: transparent;
125
- background: #22c55e;
126
- color: #0f172a;
127
- box-shadow: 0 10px 22px #22c55e30;
129
+ border-color: #2fdc74;
130
+ background: linear-gradient(98deg, #22c55e, #1ea853);
131
+ color: #0a1a11;
132
+ box-shadow:
133
+ 0 0 0 1px #6ee7b733 inset,
134
+ 0 14px 26px #22c55e42,
135
+ 0 0 22px #2563eb2f;
128
136
  }
129
137
 
130
138
  .btn.primary:hover {
131
- background: #16a34a;
139
+ background: linear-gradient(98deg, #2ad668, #1fa857);
140
+ box-shadow:
141
+ 0 0 0 1px #86efac44 inset,
142
+ 0 18px 30px #22c55e4f,
143
+ 0 0 24px #2563eb40;
132
144
  }
133
145
 
134
146
  .content {
135
- padding: 24px 22px;
147
+ padding: 30px 24px 26px;
136
148
  display: grid;
137
- gap: 14px;
149
+ gap: 16px;
150
+ }
151
+
152
+ .eyebrow {
153
+ margin: 0;
154
+ width: fit-content;
155
+ border: 1px solid #315486;
156
+ border-radius: 999px;
157
+ padding: 6px 11px;
158
+ background: #10264a;
159
+ color: #c0d9ff;
160
+ font-size: 12px;
161
+ font-weight: 700;
162
+ letter-spacing: 0.04em;
163
+ text-transform: uppercase;
138
164
  }
139
165
 
140
166
  h1 {
141
167
  margin: 0;
142
168
  font-family: 'Sora', sans-serif;
143
- font-size: clamp(26px, 3.6vw, 36px);
144
- line-height: 1.08;
169
+ font-size: clamp(30px, 4vw, 40px);
170
+ line-height: 1.04;
145
171
  letter-spacing: -0.02em;
146
- background: linear-gradient(90deg, #eef5ff 0%, #60a5fa 46%, #a78bfa 100%);
172
+ background: linear-gradient(90deg, #f5f9ff 0%, #9cc8ff 46%, #bbd8ff 100%);
147
173
  -webkit-background-clip: text;
148
174
  background-clip: text;
149
175
  color: transparent;
@@ -151,26 +177,47 @@
151
177
 
152
178
  .lead {
153
179
  margin: 0;
154
- color: #bfd1ea;
155
- line-height: 1.6;
156
- font-size: 16px;
180
+ color: #c8d9f4;
181
+ line-height: 1.55;
182
+ font-size: 17px;
157
183
  }
158
184
 
159
185
  .card {
160
- border: 1px solid #334f7bc7;
186
+ border: 1px solid #365584c4;
161
187
  border-radius: var(--radius);
162
- background: var(--card);
163
- padding: 16px;
188
+ background: linear-gradient(150deg, var(--card), #0d1c37d9);
189
+ padding: 20px;
164
190
  display: grid;
165
- gap: 10px;
166
- backdrop-filter: blur(8px);
191
+ gap: 12px;
192
+ backdrop-filter: blur(12px);
193
+ box-shadow:
194
+ 0 16px 32px #02091652,
195
+ inset 0 1px 0 #9cc8ff1c;
167
196
  }
168
197
 
169
198
  .status {
170
199
  margin: 0;
171
- color: #dce8fa;
172
- font-size: 15px;
200
+ width: fit-content;
201
+ border: 1px solid #4170aa;
202
+ border-radius: 999px;
203
+ background: #0f274b;
204
+ color: #e6f1ff;
205
+ padding: 8px 12px;
206
+ font-size: 14px;
207
+ font-weight: 700;
173
208
  line-height: 1.5;
209
+ display: inline-flex;
210
+ align-items: center;
211
+ gap: 8px;
212
+ }
213
+
214
+ .status::before {
215
+ content: '';
216
+ width: 8px;
217
+ height: 8px;
218
+ border-radius: 50%;
219
+ background: #60a5fa;
220
+ box-shadow: 0 0 0 4px #60a5fa24;
174
221
  }
175
222
 
176
223
  .hint {
@@ -182,57 +229,188 @@
182
229
 
183
230
  .error {
184
231
  margin: 0;
185
- border: 1px solid #a74949;
232
+ border: 1px solid #b15050;
186
233
  border-radius: 12px;
187
- background: #3b181899;
234
+ background: #411b1b9e;
188
235
  color: #ffd7d7;
189
- padding: 9px 10px;
236
+ padding: 10px 12px;
190
237
  font-size: 14px;
191
238
  }
192
239
 
240
+ .already-logged {
241
+ border: 1px solid #2f9461bf;
242
+ border-radius: 14px;
243
+ background: linear-gradient(150deg, #0f2f21cc, #103126d9);
244
+ padding: 14px;
245
+ display: grid;
246
+ gap: 6px;
247
+ box-shadow:
248
+ 0 14px 26px #22c55e1e,
249
+ inset 0 1px 0 #7af3ab33;
250
+ }
251
+
252
+ .already-logged-title {
253
+ margin: 0;
254
+ font-family: 'Sora', sans-serif;
255
+ font-size: clamp(20px, 2.4vw, 24px);
256
+ line-height: 1.15;
257
+ color: #e7fff1;
258
+ letter-spacing: -0.01em;
259
+ }
260
+
261
+ .already-logged-detail {
262
+ margin: 0;
263
+ color: #c5f5d9;
264
+ font-size: 14px;
265
+ line-height: 1.5;
266
+ }
267
+
193
268
  .google-area {
194
- border: 1px dashed #3c5b8d;
269
+ border: 1px dashed #456da4;
195
270
  border-radius: 14px;
196
- background: #111827a3;
197
- padding: 12px;
271
+ background: #0d1b34cc;
272
+ padding: 14px;
198
273
  display: grid;
199
- gap: 10px;
274
+ gap: 12px;
200
275
  align-items: stretch;
201
276
  }
202
277
 
203
- html:not([data-login-has-wa='1']) #google-login-area {
204
- display: none !important;
278
+ .google-button-shell {
279
+ border: 1px solid #bfd5ff6b;
280
+ border-radius: 14px;
281
+ background: #ffffff;
282
+ padding: 8px;
283
+ box-shadow:
284
+ 0 8px 18px #02091624,
285
+ inset 0 1px 0 #ffffff;
286
+ transition:
287
+ transform 0.2s ease,
288
+ box-shadow 0.2s ease;
289
+ }
290
+
291
+ .google-button-shell:hover {
292
+ transform: translateY(-1px);
293
+ box-shadow:
294
+ 0 12px 22px #0209163b,
295
+ inset 0 1px 0 #ffffff;
205
296
  }
206
297
 
207
298
  [data-google-login-button] {
208
299
  width: 100%;
209
- min-height: 40px;
300
+ min-height: 44px;
210
301
  }
211
302
 
212
303
  .google-state {
213
304
  margin: 0;
214
- color: var(--muted);
215
- font-size: 13px;
305
+ color: #9db6d9;
306
+ font-size: 12px;
216
307
  }
217
308
 
218
- .summary {
219
- border: 1px solid #2b8b5d9c;
309
+ .consent {
310
+ border: 1px solid #3d5b87;
220
311
  border-radius: 12px;
221
- background: #11302294;
222
- padding: 10px;
312
+ background: #0f213fcf;
313
+ padding: 12px;
223
314
  display: grid;
224
315
  gap: 6px;
225
- color: #dfffe8;
316
+ transition:
317
+ border-color 0.2s ease,
318
+ box-shadow 0.2s ease,
319
+ background-color 0.2s ease;
320
+ }
321
+
322
+ .consent:hover {
323
+ border-color: #6ea4ef;
324
+ }
325
+
326
+ .consent.is-checked {
327
+ border-color: #22c55e;
328
+ background: #102f21d4;
329
+ box-shadow: inset 0 0 0 1px #22c55e33;
330
+ }
331
+
332
+ .consent-row {
333
+ display: grid;
334
+ grid-template-columns: 22px 1fr;
335
+ align-items: start;
336
+ gap: 10px;
337
+ margin: 0;
338
+ color: #deebff;
339
+ font-size: 14px;
340
+ line-height: 1.5;
341
+ cursor: pointer;
342
+ }
343
+
344
+ .consent-row input {
345
+ margin: 0;
346
+ width: 18px;
347
+ height: 18px;
348
+ accent-color: #22c55e;
349
+ margin-top: 1px;
350
+ }
351
+
352
+ .consent-row a {
353
+ color: #a7ccff;
354
+ font-weight: 600;
355
+ }
356
+
357
+ .consent-row a:hover {
358
+ color: #d5e7ff;
359
+ }
360
+
361
+ .consent-error {
362
+ margin: 0;
363
+ color: #fecaca;
364
+ font-size: 12px;
365
+ }
366
+
367
+ html:not([data-login-has-wa='1']) #google-login-area {
368
+ display: none !important;
369
+ }
370
+
371
+ .summary {
372
+ border: 1px solid #2f9461bf;
373
+ border-radius: 12px;
374
+ background: linear-gradient(150deg, #113223cf, #0f2c1fd4);
375
+ padding: 12px;
376
+ display: grid;
377
+ gap: 4px;
378
+ color: #dcffe9;
226
379
  font-size: 14px;
227
380
  }
228
381
 
382
+ .summary-title {
383
+ margin: 0;
384
+ display: inline-flex;
385
+ align-items: center;
386
+ gap: 8px;
387
+ font-size: 13px;
388
+ font-weight: 700;
389
+ letter-spacing: 0.02em;
390
+ color: #b8f7d1;
391
+ }
392
+
393
+ .summary-title::before {
394
+ content: '';
395
+ width: 8px;
396
+ height: 8px;
397
+ border-radius: 50%;
398
+ background: #22c55e;
399
+ box-shadow: 0 0 0 4px #22c55e2b;
400
+ }
401
+
402
+ .summary[data-state='pending'] .summary-title::before {
403
+ background: #facc15;
404
+ box-shadow: 0 0 0 4px #facc152b;
405
+ }
406
+
229
407
  .whatsapp-cta {
230
- border: 1px solid #2d7d57a8;
408
+ border: 1px solid #355d90bf;
231
409
  border-radius: 14px;
232
- background: #1130228f;
233
- padding: 12px;
410
+ background: linear-gradient(150deg, #0d1f3bcc, #12223fa8);
411
+ padding: 13px;
234
412
  display: grid;
235
- gap: 10px;
413
+ gap: 11px;
236
414
  }
237
415
 
238
416
  html[data-login-has-wa='1'] #whatsapp-cta {
@@ -241,28 +419,135 @@
241
419
 
242
420
  .whatsapp-cta p {
243
421
  margin: 0;
244
- color: #d5ffdf;
422
+ color: #d5e6ff;
245
423
  font-size: 14px;
246
424
  line-height: 1.5;
247
425
  }
248
426
 
249
427
  .btn.whatsapp {
250
- border-color: #1e9a5f;
251
- background: linear-gradient(90deg, #22c55e, #16a34a);
252
- color: #04230f;
428
+ border-color: #3a67a6;
429
+ background: #16315a;
430
+ color: #d9ebff;
253
431
  text-align: center;
254
432
  }
255
433
 
256
434
  .whatsapp-meta {
257
435
  margin: 0;
258
- color: #b7e9ca;
436
+ color: #9eb5d8;
259
437
  font-size: 13px;
260
438
  }
261
439
 
262
440
  .actions {
263
- display: flex;
264
- flex-wrap: wrap;
265
- gap: 8px;
441
+ display: grid;
442
+ gap: 10px;
443
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
444
+ }
445
+
446
+ .success-celebration {
447
+ position: fixed;
448
+ inset: 0;
449
+ z-index: 80;
450
+ display: grid;
451
+ place-items: center;
452
+ padding: 16px;
453
+ pointer-events: none;
454
+ opacity: 0;
455
+ visibility: hidden;
456
+ transition:
457
+ opacity 0.25s ease,
458
+ visibility 0.25s ease;
459
+ }
460
+
461
+ .success-celebration::before {
462
+ content: '';
463
+ position: absolute;
464
+ inset: 0;
465
+ background: radial-gradient(circle at center, #22c55e24 0%, #0b152bcc 52%, #070f20eb 100%);
466
+ backdrop-filter: blur(2px);
467
+ }
468
+
469
+ .success-celebration.is-visible {
470
+ opacity: 1;
471
+ visibility: visible;
472
+ }
473
+
474
+ .success-card {
475
+ position: relative;
476
+ width: min(360px, 100%);
477
+ border: 1px solid #4a76b4;
478
+ border-radius: 18px;
479
+ background: linear-gradient(155deg, #112241f0, #102238e8);
480
+ box-shadow:
481
+ 0 22px 40px #02091675,
482
+ inset 0 1px 0 #8cb8ff36;
483
+ padding: 18px 16px;
484
+ display: grid;
485
+ justify-items: center;
486
+ gap: 10px;
487
+ transform: translateY(18px) scale(0.95);
488
+ opacity: 0;
489
+ transition:
490
+ transform 0.35s cubic-bezier(0.2, 0.8, 0.2, 1),
491
+ opacity 0.35s ease;
492
+ }
493
+
494
+ .success-celebration.is-visible .success-card {
495
+ transform: translateY(0) scale(1);
496
+ opacity: 1;
497
+ }
498
+
499
+ .success-icon {
500
+ width: 76px;
501
+ height: 76px;
502
+ border-radius: 50%;
503
+ border: 1px solid #44c97a;
504
+ background: linear-gradient(140deg, #0e3122, #12492f);
505
+ display: grid;
506
+ place-items: center;
507
+ box-shadow:
508
+ 0 0 0 10px #22c55e14,
509
+ 0 10px 22px #22c55e3b;
510
+ }
511
+
512
+ .success-check-svg {
513
+ width: 34px;
514
+ height: 34px;
515
+ }
516
+
517
+ .success-check {
518
+ fill: none;
519
+ stroke: #ecfff3;
520
+ stroke-width: 2.6;
521
+ stroke-linecap: round;
522
+ stroke-linejoin: round;
523
+ stroke-dasharray: 40;
524
+ stroke-dashoffset: 40;
525
+ }
526
+
527
+ .success-celebration.is-visible .success-check {
528
+ animation: loginSuccessDraw 0.48s ease 0.12s forwards;
529
+ }
530
+
531
+ .success-title {
532
+ margin: 0;
533
+ font-family: 'Sora', sans-serif;
534
+ font-size: 20px;
535
+ font-weight: 700;
536
+ color: #effaf3;
537
+ letter-spacing: -0.02em;
538
+ }
539
+
540
+ .success-subtitle {
541
+ margin: 0;
542
+ font-size: 14px;
543
+ color: #c0d4f2;
544
+ text-align: center;
545
+ }
546
+
547
+ @keyframes loginSuccessDraw {
548
+ to {
549
+ stroke-dashoffset: 0;
550
+ }
266
551
  }
267
552
 
268
553
  @media (max-width: 620px) {
@@ -271,12 +556,27 @@
271
556
  }
272
557
 
273
558
  .content {
274
- padding: 18px 16px;
559
+ padding: 22px 16px 18px;
275
560
  }
276
561
 
277
- .actions .btn {
278
- width: 100%;
279
- text-align: center;
562
+ .card {
563
+ padding: 16px;
564
+ }
565
+
566
+ .actions {
567
+ grid-template-columns: 1fr;
568
+ }
569
+ }
570
+
571
+ @media (prefers-reduced-motion: reduce) {
572
+ .success-celebration,
573
+ .success-card {
574
+ transition: none;
575
+ }
576
+
577
+ .success-celebration.is-visible .success-check {
578
+ animation: none;
579
+ stroke-dashoffset: 0;
280
580
  }
281
581
  }
282
582
  </style>
@@ -291,37 +591,67 @@
291
591
  </header>
292
592
 
293
593
  <section class="content">
294
- <h1>Login da Plataforma</h1>
295
- <p class="lead">Entre com Google para vincular sua conta ao seu numero do WhatsApp e liberar os recursos web do OmniZap.</p>
594
+ <p class="eyebrow">Login seguro OmniZap</p>
595
+ <h1>Acesse sua conta</h1>
596
+ <p class="lead">Vincule seu WhatsApp e libere os recursos do OmniZap.</p>
296
597
 
297
598
  <article class="card">
298
- <p id="login-status" class="status">Carregando estado de login...</p>
599
+ <p id="login-status" class="status">Verificando conta Google...</p>
299
600
  <p id="login-hint" class="hint"></p>
300
601
  <p id="login-error" class="error" hidden></p>
602
+ <div id="already-logged-banner" class="already-logged" hidden aria-live="polite" aria-atomic="true">
603
+ <p id="already-logged-title" class="already-logged-title">Voce ja esta logado neste navegador.</p>
604
+ <p id="already-logged-detail" class="already-logged-detail">Nao e necessario fazer login novamente. Escolha uma opcao abaixo.</p>
605
+ </div>
301
606
 
302
607
  <div id="google-login-area" class="google-area" hidden>
303
- <div data-google-login-button></div>
608
+ <div class="google-button-shell">
609
+ <div data-google-login-button></div>
610
+ </div>
304
611
  <p id="google-login-state" class="google-state">Carregando login Google...</p>
305
- </div>
306
-
307
- <div id="login-summary" class="summary" hidden>
308
- <div id="login-summary-owner">WhatsApp vinculado:</div>
309
- </div>
310
-
311
- <div id="whatsapp-cta" class="whatsapp-cta" hidden>
312
- <p>Abriu esta página direto? Inicie a conversa no WhatsApp para gerar o link com seu número automaticamente.</p>
313
- <a id="whatsapp-cta-link" class="btn whatsapp" href="https://wa.me/?text=iniciar" target="_blank" rel="noreferrer noopener"> Abrir WhatsApp e enviar "iniciar" </a>
314
- <p id="whatsapp-cta-meta" class="whatsapp-meta"></p>
315
- </div>
316
-
317
- <div id="login-success-actions" class="actions" hidden>
318
- <a id="login-success-chat" class="btn primary" href="https://wa.me/?text=%2Fmenu" target="_blank" rel="noreferrer noopener"> Voltar ao chat do bot (/menu) </a>
319
- <a id="login-success-home" class="btn" href="/">Voltar para Home</a>
612
+ <div id="login-consent-box" class="consent">
613
+ <label class="consent-row" for="login-consent-checkbox">
614
+ <input id="login-consent-checkbox" type="checkbox" />
615
+ <span>
616
+ Li e aceito os <a href="/termos-de-uso/" target="_blank" rel="noreferrer noopener">Termos de Uso</a> e a
617
+ <a href="/termos-de-uso/#politica-de-privacidade" target="_blank" rel="noreferrer noopener">Politica de Privacidade</a>.
618
+ </span>
619
+ </label>
620
+ <p id="login-consent-error" class="consent-error" hidden></p>
621
+ </div>
320
622
  </div>
321
623
  </article>
624
+
625
+ <div id="login-summary" class="summary" hidden>
626
+ <p id="login-summary-title" class="summary-title">WhatsApp conectado</p>
627
+ <div id="login-summary-owner"></div>
628
+ </div>
629
+
630
+ <div id="whatsapp-cta" class="whatsapp-cta" hidden>
631
+ <p>Para liberar o login neste navegador, inicie no WhatsApp e gere seu link seguro.</p>
632
+ <a id="whatsapp-cta-link" class="btn whatsapp" href="https://wa.me/?text=iniciar" target="_blank" rel="noreferrer noopener"> Gerar link de login no WhatsApp </a>
633
+ <p id="whatsapp-cta-meta" class="whatsapp-meta"></p>
634
+ </div>
635
+
636
+ <div id="login-success-actions" class="actions" hidden>
637
+ <a id="login-success-home" class="btn primary" href="/user/">Ir para o painel</a>
638
+ <a id="login-success-chat" class="btn" href="https://wa.me/?text=%2Fmenu" target="_blank" rel="noreferrer noopener">Abrir WhatsApp do bot</a>
639
+ </div>
322
640
  </section>
323
641
  </main>
324
642
 
325
- <script type="module" src="/js/apps/loginApp.js?v=20260228f"></script>
643
+ <div id="login-success-celebration" class="success-celebration" hidden aria-live="polite" aria-atomic="true">
644
+ <div class="success-card" role="status">
645
+ <div class="success-icon" aria-hidden="true">
646
+ <svg class="success-check-svg" viewBox="0 0 24 24" focusable="false">
647
+ <path class="success-check" d="M5 12.5l4.2 4.2L19 7.3" />
648
+ </svg>
649
+ </div>
650
+ <p class="success-title">Login concluido</p>
651
+ <p class="success-subtitle">Sua conta foi vinculada com sucesso.</p>
652
+ </div>
653
+ </div>
654
+
655
+ <script type="module" src="/js/apps/loginApp.js?v=20260301d"></script>
326
656
  </body>
327
657
  </html>