@kaikybrofc/omnizap-system 2.2.10 → 2.3.0
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/README.md +20 -18
- package/app/config/adminIdentity.js +1 -3
- package/app/connection/socketController.js +10 -20
- package/app/controllers/messageController.js +7 -28
- package/app/modules/aiModule/catCommand.js +29 -192
- package/app/modules/broadcastModule/noticeCommand.js +28 -97
- package/app/modules/gameModule/diceCommand.js +6 -32
- package/app/modules/playModule/playCommand.js +57 -258
- package/app/modules/quoteModule/quoteCommand.js +2 -4
- package/app/modules/rpgPokemonModule/rpgPokemonRepository.js +1 -13
- package/app/modules/statsModule/noMessageCommand.js +16 -84
- package/app/modules/statsModule/rankingCommand.js +5 -25
- package/app/modules/statsModule/rankingCommon.js +1 -9
- package/app/modules/stickerModule/convertToWebp.js +4 -27
- package/app/modules/stickerModule/stickerCommand.js +13 -24
- package/app/modules/stickerModule/stickerTextCommand.js +13 -25
- package/app/modules/stickerPackModule/autoPackCollectorService.js +16 -7
- package/app/modules/stickerPackModule/domainEventOutboxRepository.js +20 -36
- package/app/modules/stickerPackModule/domainEvents.js +2 -11
- package/app/modules/stickerPackModule/semanticReclassificationEngine.js +13 -50
- package/app/modules/stickerPackModule/semanticReclassificationEngine.test.js +2 -15
- package/app/modules/stickerPackModule/semanticThemeClusterService.js +14 -41
- package/app/modules/stickerPackModule/stickerAssetClassificationRepository.js +25 -95
- package/app/modules/stickerPackModule/stickerAssetRepository.js +12 -31
- package/app/modules/stickerPackModule/stickerAssetReprocessQueueRepository.js +13 -18
- package/app/modules/stickerPackModule/stickerAutoPackByTagsRuntime.js +284 -709
- package/app/modules/stickerPackModule/stickerClassificationBackgroundRuntime.js +27 -106
- package/app/modules/stickerPackModule/stickerClassificationService.js +46 -77
- package/app/modules/stickerPackModule/stickerDedicatedTaskWorkerRuntime.js +13 -53
- package/app/modules/stickerPackModule/stickerDomainEventBus.js +10 -16
- package/app/modules/stickerPackModule/stickerDomainEventConsumerRuntime.js +13 -34
- package/app/modules/stickerPackModule/stickerMarketplaceDriftService.js +1 -4
- package/app/modules/stickerPackModule/stickerObjectStorageService.js +26 -26
- package/app/modules/stickerPackModule/stickerPackCommandHandlers.js +32 -187
- package/app/modules/stickerPackModule/stickerPackInteractionEventRepository.js +6 -15
- package/app/modules/stickerPackModule/stickerPackItemRepository.js +6 -32
- package/app/modules/stickerPackModule/stickerPackMarketplaceService.js +12 -36
- package/app/modules/stickerPackModule/stickerPackMessageService.js +12 -40
- package/app/modules/stickerPackModule/stickerPackRepository.js +23 -66
- package/app/modules/stickerPackModule/stickerPackScoreSnapshotRepository.js +9 -21
- package/app/modules/stickerPackModule/stickerPackScoreSnapshotRuntime.js +10 -40
- package/app/modules/stickerPackModule/stickerPackService.js +50 -115
- package/app/modules/stickerPackModule/stickerPackServiceRuntime.js +2 -21
- package/app/modules/stickerPackModule/stickerPackUtils.js +13 -3
- package/app/modules/stickerPackModule/stickerStorageService.js +16 -65
- package/app/modules/stickerPackModule/stickerWorkerPipelineRuntime.js +4 -22
- package/app/modules/stickerPackModule/stickerWorkerTaskQueueRepository.js +14 -29
- package/app/modules/systemMetricsModule/pingCommand.js +9 -39
- package/app/modules/tiktokModule/tiktokCommand.js +17 -109
- package/app/modules/userModule/userCommand.js +2 -88
- package/app/observability/metrics.js +5 -16
- package/app/services/captchaService.js +1 -6
- package/app/services/dbWriteQueue.js +3 -18
- package/app/services/featureFlagService.js +2 -8
- package/app/services/newsBroadcastService.js +0 -1
- package/app/services/queueUtils.js +2 -4
- package/app/services/whatsappLoginLinkService.js +7 -9
- package/app/store/premiumUserStore.js +1 -2
- package/app/utils/antiLink/antiLinkModule.js +3 -233
- package/app/utils/logger/loggerModule.js +9 -34
- package/app/utils/systemMetrics/systemMetricsModule.js +1 -4
- package/database/init.js +1 -8
- package/docker-compose.yml +27 -27
- package/docs/seo/omnizap-seo-playbook-br-2026-02-28.md +26 -0
- package/docs/seo/satellite-page-template.md +2 -0
- package/docs/seo/satellite-pages-phase1.json +40 -177
- package/eslint.config.js +2 -15
- package/index.js +8 -36
- package/ml/clip_classifier/README.md +4 -6
- package/observability/alert-rules.yml +12 -12
- package/observability/grafana/provisioning/dashboards/dashboards.yml +1 -1
- package/package.json +6 -3
- package/public/api-docs/index.html +220 -193
- package/public/bot-whatsapp-para-grupo/index.html +291 -261
- package/public/bot-whatsapp-sem-programar/index.html +291 -261
- package/public/comandos/index.html +421 -406
- package/public/como-automatizar-avisos-no-whatsapp/index.html +291 -261
- package/public/como-criar-comandos-whatsapp/index.html +291 -261
- package/public/como-evitar-spam-no-whatsapp/index.html +291 -261
- package/public/como-moderar-grupo-whatsapp/index.html +291 -261
- package/public/como-organizar-comunidade-whatsapp/index.html +291 -261
- package/public/css/github-project-panel.css +13 -8
- package/public/css/stickers-admin.css +25 -9
- package/public/css/styles.css +23 -16
- package/public/index.html +1117 -994
- package/public/js/apps/apiDocsApp.js +17 -167
- package/public/js/apps/createPackApp.js +69 -332
- package/public/js/apps/homeApp.js +94 -74
- package/public/js/apps/loginApp.js +3 -12
- package/public/js/apps/stickersAdminApp.js +190 -181
- package/public/js/apps/stickersApp.js +496 -1397
- package/public/js/catalog.js +11 -74
- package/public/js/github-panel/components/ErrorState.js +1 -8
- package/public/js/github-panel/components/GithubProjectPanel.js +2 -9
- package/public/js/github-panel/components/SkeletonPanel.js +1 -11
- package/public/js/github-panel/components/StatCard.js +1 -7
- package/public/js/github-panel/vendor/react.js +1 -9
- package/public/js/runtime/react-runtime.js +1 -9
- package/public/licenca/index.html +104 -86
- package/public/login/index.html +315 -325
- package/public/melhor-bot-whatsapp-para-grupos/index.html +291 -261
- package/public/stickers/admin/index.html +14 -19
- package/public/stickers/create/index.html +39 -44
- package/public/stickers/index.html +96 -107
- package/public/termos-de-uso/index.html +142 -115
- package/public/user/index.html +347 -350
- package/scripts/cache-bust.mjs +5 -24
- package/scripts/generate-seo-satellite-pages.mjs +10 -13
- package/scripts/run-prettier-all.mjs +25 -0
- package/scripts/sticker-catalog-loadtest.mjs +13 -11
- package/scripts/sticker-worker-task.mjs +1 -4
- package/scripts/sync-readme-snapshot.mjs +3 -2
- package/server/controllers/stickerCatalogController.js +67 -5
- package/server/http/httpServer.js +2 -10
- package/server/routes/stickerCatalog/catalogHandlers/catalogAdminHttp.js +1 -8
- package/server/routes/stickerCatalog/catalogHandlers/catalogAuthHttp.js +1 -9
- package/server/routes/stickerCatalog/catalogHandlers/catalogPublicHttp.js +10 -11
- package/server/routes/stickerCatalog/catalogHandlers/catalogUploadHttp.js +1 -10
- package/server/routes/stickerCatalog/catalogRouter.js +11 -13
|
@@ -50,8 +50,7 @@ const normalizeCreatorsSort = (value, fallback = DEFAULT_CREATORS_SORT) => {
|
|
|
50
50
|
return fallback;
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
const catalogSortLabel = (sort) =>
|
|
54
|
-
CATALOG_SORT_OPTIONS.find((entry) => entry.value === normalizeCatalogSort(sort))?.label || 'Ordenar';
|
|
53
|
+
const catalogSortLabel = (sort) => CATALOG_SORT_OPTIONS.find((entry) => entry.value === normalizeCatalogSort(sort))?.label || 'Ordenar';
|
|
55
54
|
|
|
56
55
|
const getPackTrendScore = (pack) => safeNumber(pack?.signals?.trend_score);
|
|
57
56
|
const getPackRankingScore = (pack) => safeNumber(pack?.signals?.ranking_score);
|
|
@@ -277,12 +276,13 @@ const getPackEngagement = (pack) => {
|
|
|
277
276
|
};
|
|
278
277
|
};
|
|
279
278
|
|
|
280
|
-
const getAvatarUrl = (name) =>
|
|
281
|
-
`https://api.dicebear.com/8.x/thumbs/svg?seed=${encodeURIComponent(String(name || 'omnizap'))}`;
|
|
279
|
+
const getAvatarUrl = (name) => `https://api.dicebear.com/8.x/thumbs/svg?seed=${encodeURIComponent(String(name || 'omnizap'))}`;
|
|
282
280
|
|
|
283
281
|
const parseCatalogSearchState = (search = '') => {
|
|
284
282
|
const params = new URLSearchParams(String(search || ''));
|
|
285
|
-
const filter = String(params.get('filter') || '')
|
|
283
|
+
const filter = String(params.get('filter') || '')
|
|
284
|
+
.trim()
|
|
285
|
+
.toLowerCase();
|
|
286
286
|
const hasTrendingFilter = filter === 'trending';
|
|
287
287
|
const q = hasTrendingFilter ? '' : String(params.get('q') || '').trim();
|
|
288
288
|
const category = hasTrendingFilter
|
|
@@ -321,10 +321,22 @@ const parseStickersLocation = (webPath = '/stickers') => {
|
|
|
321
321
|
|
|
322
322
|
try {
|
|
323
323
|
const firstSegment = decodeURIComponent(firstSegmentRaw);
|
|
324
|
-
if (
|
|
324
|
+
if (
|
|
325
|
+
PROFILE_ROUTE_SEGMENTS.has(
|
|
326
|
+
String(firstSegment || '')
|
|
327
|
+
.trim()
|
|
328
|
+
.toLowerCase(),
|
|
329
|
+
)
|
|
330
|
+
) {
|
|
325
331
|
return { view: 'profile', packKey: '' };
|
|
326
332
|
}
|
|
327
|
-
if (
|
|
333
|
+
if (
|
|
334
|
+
CREATORS_ROUTE_SEGMENTS.has(
|
|
335
|
+
String(firstSegment || '')
|
|
336
|
+
.trim()
|
|
337
|
+
.toLowerCase(),
|
|
338
|
+
)
|
|
339
|
+
) {
|
|
328
340
|
return { view: 'creators', packKey: '' };
|
|
329
341
|
}
|
|
330
342
|
return { view: 'pack', packKey: firstSegment };
|
|
@@ -455,17 +467,13 @@ function UploadTaskWidget({ task, onClose }) {
|
|
|
455
467
|
</div>
|
|
456
468
|
<div className="mt-2 flex items-center justify-between text-[11px]">
|
|
457
469
|
<span className="text-slate-400">${task.current || 0}/${task.total || 0}</span>
|
|
458
|
-
<span className=${`${isError ? 'text-rose-300' : isDone ? 'text-emerald-300' : isPaused ? 'text-amber-300' : 'text-cyan-300'} font-semibold`}>
|
|
459
|
-
${progress}%
|
|
460
|
-
</span>
|
|
470
|
+
<span className=${`${isError ? 'text-rose-300' : isDone ? 'text-emerald-300' : isPaused ? 'text-amber-300' : 'text-cyan-300'} font-semibold`}> ${progress}% </span>
|
|
461
471
|
</div>
|
|
462
472
|
${(isDone || isPaused) && packUrl
|
|
463
473
|
? html`
|
|
464
474
|
<div className="mt-2 flex gap-2">
|
|
465
475
|
<a href=${packUrl} className="inline-flex rounded-lg border border-cyan-500/40 bg-cyan-500/10 px-2.5 py-1.5 text-[11px] font-semibold text-cyan-200">Abrir pack</a>
|
|
466
|
-
${isPaused
|
|
467
|
-
? html`<a href="/stickers/create/" className="inline-flex rounded-lg border border-amber-500/40 bg-amber-500/10 px-2.5 py-1.5 text-[11px] font-semibold text-amber-200">Retomar envio</a>`
|
|
468
|
-
: null}
|
|
476
|
+
${isPaused ? html`<a href="/stickers/create/" className="inline-flex rounded-lg border border-amber-500/40 bg-amber-500/10 px-2.5 py-1.5 text-[11px] font-semibold text-amber-200">Retomar envio</a>` : null}
|
|
469
477
|
</div>
|
|
470
478
|
`
|
|
471
479
|
: null}
|
|
@@ -488,32 +496,11 @@ function PackCard({ pack, index, onOpen, hasNsfwAccess = true, onRequireLogin })
|
|
|
488
496
|
};
|
|
489
497
|
|
|
490
498
|
return html`
|
|
491
|
-
<button
|
|
492
|
-
type="button"
|
|
493
|
-
onClick=${handleOpen}
|
|
494
|
-
className="group w-full text-left rounded-2xl border border-slate-800 bg-slate-900/90 shadow-soft overflow-hidden transition-all duration-200 active:scale-[0.985] md:hover:scale-[1.02] hover:-translate-y-0.5 hover:shadow-lg touch-manipulation"
|
|
495
|
-
>
|
|
499
|
+
<button type="button" onClick=${handleOpen} className="group w-full text-left rounded-2xl border border-slate-800 bg-slate-900/90 shadow-soft overflow-hidden transition-all duration-200 active:scale-[0.985] md:hover:scale-[1.02] hover:-translate-y-0.5 hover:shadow-lg touch-manipulation">
|
|
496
500
|
<div className="relative aspect-[5/6] sm:aspect-[4/5] bg-slate-900 overflow-hidden">
|
|
497
|
-
<img
|
|
498
|
-
src=${coverUrl}
|
|
499
|
-
alt=${`Capa de ${pack.name}`}
|
|
500
|
-
className=${`w-full h-full object-cover transition-transform duration-300 ${
|
|
501
|
-
lockedByNsfw ? 'blur-md scale-105' : 'md:group-hover:scale-[1.05] group-active:scale-[1.02]'
|
|
502
|
-
}`}
|
|
503
|
-
loading="lazy"
|
|
504
|
-
/>
|
|
501
|
+
<img src=${coverUrl} alt=${`Capa de ${pack.name}`} className=${`w-full h-full object-cover transition-transform duration-300 ${lockedByNsfw ? 'blur-md scale-105' : 'md:group-hover:scale-[1.05] group-active:scale-[1.02]'}`} loading="lazy" />
|
|
505
502
|
<div className="absolute inset-0 bg-gradient-to-t from-slate-950 via-slate-950/60 to-transparent"></div>
|
|
506
|
-
<div className="absolute top-2 left-2 flex items-center gap-1">
|
|
507
|
-
${lockedByNsfw
|
|
508
|
-
? html`<span className="rounded-full border border-amber-300/35 bg-amber-500/25 backdrop-blur px-1.5 py-0.5 text-[9px] font-bold text-amber-100">🔞 Login</span>`
|
|
509
|
-
: null}
|
|
510
|
-
${isTrending
|
|
511
|
-
? html`<span className="rounded-full border border-emerald-300/30 bg-emerald-400/80 backdrop-blur px-1.5 py-0.5 text-[9px] font-bold text-slate-900">Trending</span>`
|
|
512
|
-
: null}
|
|
513
|
-
${isNew
|
|
514
|
-
? html`<span className="rounded-full border border-white/15 bg-black/45 backdrop-blur px-1.5 py-0.5 text-[9px] font-semibold text-slate-100">Novo</span>`
|
|
515
|
-
: null}
|
|
516
|
-
</div>
|
|
503
|
+
<div className="absolute top-2 left-2 flex items-center gap-1">${lockedByNsfw ? html`<span className="rounded-full border border-amber-300/35 bg-amber-500/25 backdrop-blur px-1.5 py-0.5 text-[9px] font-bold text-amber-100">🔞 Login</span>` : null} ${isTrending ? html`<span className="rounded-full border border-emerald-300/30 bg-emerald-400/80 backdrop-blur px-1.5 py-0.5 text-[9px] font-bold text-slate-900">Trending</span>` : null} ${isNew ? html`<span className="rounded-full border border-white/15 bg-black/45 backdrop-blur px-1.5 py-0.5 text-[9px] font-semibold text-slate-100">Novo</span>` : null}</div>
|
|
517
504
|
|
|
518
505
|
<div className="absolute inset-x-0 bottom-0 p-2">
|
|
519
506
|
<h3 className="font-semibold text-sm leading-5 line-clamp-2">${pack.name || 'Pack sem nome'}</h3>
|
|
@@ -529,40 +516,25 @@ function PackCard({ pack, index, onOpen, hasNsfwAccess = true, onRequireLogin })
|
|
|
529
516
|
</div>
|
|
530
517
|
|
|
531
518
|
<div className="pointer-events-none absolute inset-x-2 bottom-2 hidden md:flex justify-center opacity-0 transition-opacity duration-200 group-hover:opacity-100">
|
|
532
|
-
<span className="inline-flex h-8 w-full items-center justify-center rounded-xl border border-emerald-400/35 bg-emerald-400/12 px-3 text-xs font-semibold text-emerald-200 backdrop-blur">
|
|
533
|
-
${lockedByNsfw ? 'Entrar para desbloquear' : 'Abrir pack'}
|
|
534
|
-
</span>
|
|
519
|
+
<span className="inline-flex h-8 w-full items-center justify-center rounded-xl border border-emerald-400/35 bg-emerald-400/12 px-3 text-xs font-semibold text-emerald-200 backdrop-blur"> ${lockedByNsfw ? 'Entrar para desbloquear' : 'Abrir pack'} </span>
|
|
535
520
|
</div>
|
|
536
521
|
${lockedByNsfw
|
|
537
522
|
? html`
|
|
538
523
|
<div className="pointer-events-none absolute inset-x-0 top-[42%] flex justify-center px-3">
|
|
539
|
-
<span className="inline-flex rounded-xl border border-amber-400/40 bg-slate-950/70 px-2.5 py-1 text-[10px] font-semibold text-amber-100 backdrop-blur">
|
|
540
|
-
Conteúdo sensível
|
|
541
|
-
</span>
|
|
524
|
+
<span className="inline-flex rounded-xl border border-amber-400/40 bg-slate-950/70 px-2.5 py-1 text-[10px] font-semibold text-amber-100 backdrop-blur"> Conteúdo sensível </span>
|
|
542
525
|
</div>
|
|
543
526
|
`
|
|
544
527
|
: null}
|
|
545
528
|
</div>
|
|
546
529
|
|
|
547
530
|
<div className="px-2 pb-2 pt-1 bg-slate-900/95 md:hidden">
|
|
548
|
-
<span className="inline-flex h-[34px] w-full items-center justify-center rounded-xl border border-emerald-400/30 bg-emerald-400/10 text-xs font-semibold text-emerald-200 transition group-active:brightness-110">
|
|
549
|
-
${lockedByNsfw ? 'Entrar para desbloquear' : 'Abrir pack'}
|
|
550
|
-
</span>
|
|
531
|
+
<span className="inline-flex h-[34px] w-full items-center justify-center rounded-xl border border-emerald-400/30 bg-emerald-400/10 text-xs font-semibold text-emerald-200 transition group-active:brightness-110"> ${lockedByNsfw ? 'Entrar para desbloquear' : 'Abrir pack'} </span>
|
|
551
532
|
</div>
|
|
552
533
|
</button>
|
|
553
534
|
`;
|
|
554
535
|
}
|
|
555
536
|
|
|
556
|
-
function CatalogMetricCard({
|
|
557
|
-
label,
|
|
558
|
-
value = '',
|
|
559
|
-
valueRaw = null,
|
|
560
|
-
numberFormat = 'compact',
|
|
561
|
-
icon = '📊',
|
|
562
|
-
hint = '',
|
|
563
|
-
bars = [],
|
|
564
|
-
tone = 'slate',
|
|
565
|
-
}) {
|
|
537
|
+
function CatalogMetricCard({ label, value = '', valueRaw = null, numberFormat = 'compact', icon = '📊', hint = '', bars = [], tone = 'slate' }) {
|
|
566
538
|
const toneMap = {
|
|
567
539
|
slate: 'border-slate-800 bg-slate-900/60',
|
|
568
540
|
emerald: 'border-emerald-500/20 bg-emerald-500/5',
|
|
@@ -606,29 +578,13 @@ function CatalogMetricCard({
|
|
|
606
578
|
};
|
|
607
579
|
}, [numericTarget]);
|
|
608
580
|
|
|
609
|
-
const resolvedValue =
|
|
610
|
-
numericTarget === null
|
|
611
|
-
? String(value || '0')
|
|
612
|
-
: numberFormat === 'full'
|
|
613
|
-
? formatFullNum(animatedValue)
|
|
614
|
-
: shortNum(animatedValue);
|
|
581
|
+
const resolvedValue = numericTarget === null ? String(value || '0') : numberFormat === 'full' ? formatFullNum(animatedValue) : shortNum(animatedValue);
|
|
615
582
|
|
|
616
583
|
return html`
|
|
617
|
-
<article
|
|
618
|
-
className=${`rounded-xl border p-2.5 ${toneMap[tone] || toneMap.slate}`}
|
|
619
|
-
title=${hint || label}
|
|
620
|
-
>
|
|
584
|
+
<article className=${`rounded-xl border p-2.5 ${toneMap[tone] || toneMap.slate}`} title=${hint || label}>
|
|
621
585
|
<div className="flex items-center justify-between gap-2">
|
|
622
586
|
<span className="text-sm">${icon}</span>
|
|
623
|
-
<div className="flex items-end gap-0.5">
|
|
624
|
-
${(Array.isArray(bars) ? bars : []).slice(0, 7).map((bar, index) => html`
|
|
625
|
-
<span
|
|
626
|
-
key=${index}
|
|
627
|
-
className="w-1 rounded-full bg-white/15"
|
|
628
|
-
style=${{ height: `${Math.max(4, Math.min(16, Number(bar || 0)))}px` }}
|
|
629
|
-
></span>
|
|
630
|
-
`)}
|
|
631
|
-
</div>
|
|
587
|
+
<div className="flex items-end gap-0.5">${(Array.isArray(bars) ? bars : []).slice(0, 7).map((bar, index) => html` <span key=${index} className="w-1 rounded-full bg-white/15" style=${{ height: `${Math.max(4, Math.min(16, Number(bar || 0)))}px` }}></span> `)}</div>
|
|
632
588
|
</div>
|
|
633
589
|
<p className="mt-1 text-base font-bold text-slate-100">${resolvedValue}</p>
|
|
634
590
|
<p className="text-[11px] text-slate-400">${label}</p>
|
|
@@ -648,22 +604,11 @@ function DiscoverPackRowItem({ pack, onOpen, rank = 0, hasNsfwAccess = true, onR
|
|
|
648
604
|
onOpen(pack.pack_key);
|
|
649
605
|
};
|
|
650
606
|
return html`
|
|
651
|
-
<button
|
|
652
|
-
|
|
653
|
-
onClick=${handleOpen}
|
|
654
|
-
className="w-full flex items-center gap-2 rounded-xl border border-slate-800 bg-slate-900/50 px-2 py-1.5 text-left hover:bg-slate-800/90"
|
|
655
|
-
>
|
|
656
|
-
<img
|
|
657
|
-
src=${lockedByNsfw ? NSFW_STICKER_PLACEHOLDER_URL : pack.cover_url || DEFAULT_STICKER_PLACEHOLDER_URL}
|
|
658
|
-
alt=""
|
|
659
|
-
className=${`h-9 w-9 rounded-lg object-cover bg-slate-800 ${lockedByNsfw ? 'blur-sm' : ''}`}
|
|
660
|
-
loading="lazy"
|
|
661
|
-
/>
|
|
607
|
+
<button type="button" onClick=${handleOpen} className="w-full flex items-center gap-2 rounded-xl border border-slate-800 bg-slate-900/50 px-2 py-1.5 text-left hover:bg-slate-800/90">
|
|
608
|
+
<img src=${lockedByNsfw ? NSFW_STICKER_PLACEHOLDER_URL : pack.cover_url || DEFAULT_STICKER_PLACEHOLDER_URL} alt="" className=${`h-9 w-9 rounded-lg object-cover bg-slate-800 ${lockedByNsfw ? 'blur-sm' : ''}`} loading="lazy" />
|
|
662
609
|
<span className="min-w-0 flex-1">
|
|
663
610
|
<span className="block truncate text-xs font-medium text-slate-100">${rank > 0 ? `${rank}. ` : ''}${pack.name || 'Pack'}</span>
|
|
664
|
-
<span className="block truncate text-[10px] text-slate-400">
|
|
665
|
-
${lockedByNsfw ? '🔒 Entrar para desbloquear' : `${pack.publisher || '-'} · ❤️ ${shortNum(getPackEngagement(pack).likeCount)}`}
|
|
666
|
-
</span>
|
|
611
|
+
<span className="block truncate text-[10px] text-slate-400"> ${lockedByNsfw ? '🔒 Entrar para desbloquear' : `${pack.publisher || '-'} · ❤️ ${shortNum(getPackEngagement(pack).likeCount)}`} </span>
|
|
667
612
|
</span>
|
|
668
613
|
<span className="text-[10px] text-slate-500">→</span>
|
|
669
614
|
</button>
|
|
@@ -682,28 +627,15 @@ function DiscoverPackMiniCard({ pack, onOpen, hasNsfwAccess = true, onRequireLog
|
|
|
682
627
|
onOpen(pack.pack_key);
|
|
683
628
|
};
|
|
684
629
|
return html`
|
|
685
|
-
<button
|
|
686
|
-
type="button"
|
|
687
|
-
onClick=${handleOpen}
|
|
688
|
-
className="group w-[170px] shrink-0 overflow-hidden rounded-xl border border-slate-800 bg-slate-900/80 text-left"
|
|
689
|
-
>
|
|
630
|
+
<button type="button" onClick=${handleOpen} className="group w-[170px] shrink-0 overflow-hidden rounded-xl border border-slate-800 bg-slate-900/80 text-left">
|
|
690
631
|
<div className="relative h-24 bg-slate-900">
|
|
691
|
-
<img
|
|
692
|
-
src=${lockedByNsfw ? NSFW_STICKER_PLACEHOLDER_URL : pack.cover_url || DEFAULT_STICKER_PLACEHOLDER_URL}
|
|
693
|
-
alt=""
|
|
694
|
-
className=${`h-full w-full object-cover transition-transform duration-200 ${
|
|
695
|
-
lockedByNsfw ? 'blur-sm scale-105' : 'group-active:scale-[1.02]'
|
|
696
|
-
}`}
|
|
697
|
-
loading="lazy"
|
|
698
|
-
/>
|
|
632
|
+
<img src=${lockedByNsfw ? NSFW_STICKER_PLACEHOLDER_URL : pack.cover_url || DEFAULT_STICKER_PLACEHOLDER_URL} alt="" className=${`h-full w-full object-cover transition-transform duration-200 ${lockedByNsfw ? 'blur-sm scale-105' : 'group-active:scale-[1.02]'}`} loading="lazy" />
|
|
699
633
|
<div className="absolute inset-0 bg-gradient-to-t from-slate-950/90 to-transparent"></div>
|
|
700
634
|
${lockedByNsfw ? html`<span className="absolute top-1.5 left-1.5 rounded-full border border-amber-300/35 bg-amber-500/25 px-1.5 py-0.5 text-[9px] font-semibold text-amber-100">🔞 Login</span>` : null}
|
|
701
635
|
</div>
|
|
702
636
|
<div className="p-2">
|
|
703
637
|
<p className="truncate text-xs font-semibold text-slate-100">${pack.name || 'Pack'}</p>
|
|
704
|
-
<p className="mt-1 truncate text-[10px] text-slate-400">
|
|
705
|
-
${lockedByNsfw ? 'Entrar para desbloquear' : `⬇ ${shortNum(engagement.openCount)} · ❤️ ${shortNum(engagement.likeCount)}`}
|
|
706
|
-
</p>
|
|
638
|
+
<p className="mt-1 truncate text-[10px] text-slate-400">${lockedByNsfw ? 'Entrar para desbloquear' : `⬇ ${shortNum(engagement.openCount)} · ❤️ ${shortNum(engagement.likeCount)}`}</p>
|
|
707
639
|
</div>
|
|
708
640
|
</button>
|
|
709
641
|
`;
|
|
@@ -712,11 +644,7 @@ function DiscoverPackMiniCard({ pack, onOpen, hasNsfwAccess = true, onRequireLog
|
|
|
712
644
|
function DiscoverCreatorMiniCard({ creator, onPick }) {
|
|
713
645
|
if (!creator?.publisher) return null;
|
|
714
646
|
return html`
|
|
715
|
-
<button
|
|
716
|
-
type="button"
|
|
717
|
-
onClick=${() => onPick(creator.publisher)}
|
|
718
|
-
className="w-[190px] shrink-0 rounded-xl border border-slate-800 bg-slate-900/70 p-2 text-left hover:bg-slate-800/90"
|
|
719
|
-
>
|
|
647
|
+
<button type="button" onClick=${() => onPick(creator.publisher)} className="w-[190px] shrink-0 rounded-xl border border-slate-800 bg-slate-900/70 p-2 text-left hover:bg-slate-800/90">
|
|
720
648
|
<div className="flex items-center gap-2">
|
|
721
649
|
<img src=${getAvatarUrl(creator.publisher)} alt="" className="h-9 w-9 rounded-full bg-slate-800" />
|
|
722
650
|
<span className="min-w-0">
|
|
@@ -780,70 +708,22 @@ function MyPackCard({ pack, onOpenPublic }) {
|
|
|
780
708
|
</div>
|
|
781
709
|
</div>
|
|
782
710
|
<div className="mt-3 flex flex-wrap gap-2">
|
|
783
|
-
${shareable
|
|
784
|
-
|
|
785
|
-
<button
|
|
786
|
-
type="button"
|
|
787
|
-
onClick=${() => onOpenPublic(pack.pack_key)}
|
|
788
|
-
className="inline-flex h-9 items-center justify-center rounded-xl border border-emerald-500/35 bg-emerald-500/10 px-3 text-xs font-semibold text-emerald-200 hover:bg-emerald-500/20"
|
|
789
|
-
>
|
|
790
|
-
Abrir no catálogo
|
|
791
|
-
</button>
|
|
792
|
-
`
|
|
793
|
-
: html`
|
|
794
|
-
<span className="inline-flex h-9 items-center justify-center rounded-xl border border-slate-700 bg-slate-900/70 px-3 text-xs text-slate-400">
|
|
795
|
-
Não visível no catálogo
|
|
796
|
-
</span>
|
|
797
|
-
`}
|
|
798
|
-
<a
|
|
799
|
-
href="/stickers/create/"
|
|
800
|
-
className="inline-flex h-9 items-center justify-center rounded-xl border border-cyan-500/35 bg-cyan-500/10 px-3 text-xs font-semibold text-cyan-200 hover:bg-cyan-500/20"
|
|
801
|
-
>
|
|
802
|
-
Criar/gerenciar packs
|
|
803
|
-
</a>
|
|
711
|
+
${shareable ? html` <button type="button" onClick=${() => onOpenPublic(pack.pack_key)} className="inline-flex h-9 items-center justify-center rounded-xl border border-emerald-500/35 bg-emerald-500/10 px-3 text-xs font-semibold text-emerald-200 hover:bg-emerald-500/20">Abrir no catálogo</button> ` : html` <span className="inline-flex h-9 items-center justify-center rounded-xl border border-slate-700 bg-slate-900/70 px-3 text-xs text-slate-400"> Não visível no catálogo </span> `}
|
|
712
|
+
<a href="/stickers/create/" className="inline-flex h-9 items-center justify-center rounded-xl border border-cyan-500/35 bg-cyan-500/10 px-3 text-xs font-semibold text-cyan-200 hover:bg-cyan-500/20"> Criar/gerenciar packs </a>
|
|
804
713
|
</div>
|
|
805
714
|
</article>
|
|
806
715
|
`;
|
|
807
716
|
}
|
|
808
717
|
|
|
809
|
-
function ProfilePage({
|
|
810
|
-
googleAuthConfig,
|
|
811
|
-
googleAuth,
|
|
812
|
-
googleAuthBusy,
|
|
813
|
-
googleAuthError,
|
|
814
|
-
googleSessionChecked,
|
|
815
|
-
googleAuthUiReady,
|
|
816
|
-
googleButtonRef,
|
|
817
|
-
myPacks,
|
|
818
|
-
myPacksLoading,
|
|
819
|
-
myPacksError,
|
|
820
|
-
myProfileStats,
|
|
821
|
-
onBack,
|
|
822
|
-
onRefresh,
|
|
823
|
-
onLogout,
|
|
824
|
-
onOpenPublicPack,
|
|
825
|
-
}) {
|
|
718
|
+
function ProfilePage({ googleAuthConfig, googleAuth, googleAuthBusy, googleAuthError, googleSessionChecked, googleAuthUiReady, googleButtonRef, myPacks, myPacksLoading, myPacksError, myProfileStats, onBack, onRefresh, onLogout, onOpenPublicPack }) {
|
|
826
719
|
const hasGoogleLogin = Boolean(googleAuth?.user?.sub);
|
|
827
720
|
const googleLoginEnabled = Boolean(googleAuthConfig?.enabled && googleAuthConfig?.clientId);
|
|
828
721
|
|
|
829
722
|
return html`
|
|
830
723
|
<section className="space-y-4 pb-20 sm:pb-4">
|
|
831
724
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
832
|
-
<button
|
|
833
|
-
|
|
834
|
-
onClick=${onBack}
|
|
835
|
-
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"
|
|
836
|
-
>
|
|
837
|
-
← Voltar para catálogo
|
|
838
|
-
</button>
|
|
839
|
-
<button
|
|
840
|
-
type="button"
|
|
841
|
-
onClick=${onRefresh}
|
|
842
|
-
disabled=${myPacksLoading || googleAuthBusy}
|
|
843
|
-
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 disabled:opacity-60"
|
|
844
|
-
>
|
|
845
|
-
${myPacksLoading ? 'Atualizando...' : 'Atualizar'}
|
|
846
|
-
</button>
|
|
725
|
+
<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">← Voltar para catálogo</button>
|
|
726
|
+
<button type="button" onClick=${onRefresh} disabled=${myPacksLoading || googleAuthBusy} 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 disabled:opacity-60">${myPacksLoading ? 'Atualizando...' : 'Atualizar'}</button>
|
|
847
727
|
</div>
|
|
848
728
|
|
|
849
729
|
<section className="rounded-2xl border border-slate-800 bg-slate-900/80 p-4">
|
|
@@ -851,39 +731,20 @@ function ProfilePage({
|
|
|
851
731
|
<div>
|
|
852
732
|
<p className="text-xs uppercase tracking-wide text-slate-400">Perfil de criador</p>
|
|
853
733
|
<h1 className="mt-1 text-xl font-bold">Meus packs</h1>
|
|
854
|
-
<p className="mt-1 text-sm text-slate-400">
|
|
855
|
-
Faça login com Google para ver e vincular os packs criados pela sua conta.
|
|
856
|
-
</p>
|
|
734
|
+
<p className="mt-1 text-sm text-slate-400">Faça login com Google para ver e vincular os packs criados pela sua conta.</p>
|
|
857
735
|
</div>
|
|
858
|
-
${hasGoogleLogin
|
|
859
|
-
? html`
|
|
860
|
-
<button
|
|
861
|
-
type="button"
|
|
862
|
-
onClick=${onLogout}
|
|
863
|
-
disabled=${googleAuthBusy}
|
|
864
|
-
className="inline-flex h-10 items-center rounded-xl border border-slate-700 px-3 text-sm text-slate-200 hover:bg-slate-800 disabled:opacity-60"
|
|
865
|
-
>
|
|
866
|
-
${googleAuthBusy ? 'Saindo...' : 'Sair'}
|
|
867
|
-
</button>
|
|
868
|
-
`
|
|
869
|
-
: null}
|
|
736
|
+
${hasGoogleLogin ? html` <button type="button" onClick=${onLogout} disabled=${googleAuthBusy} className="inline-flex h-10 items-center rounded-xl border border-slate-700 px-3 text-sm text-slate-200 hover:bg-slate-800 disabled:opacity-60">${googleAuthBusy ? 'Saindo...' : 'Sair'}</button> ` : null}
|
|
870
737
|
</div>
|
|
871
738
|
|
|
872
739
|
<div className="mt-4 rounded-2xl border border-slate-800 bg-slate-950/50 p-3">
|
|
873
740
|
${hasGoogleLogin
|
|
874
741
|
? html`
|
|
875
742
|
<div className="flex flex-wrap items-center gap-3">
|
|
876
|
-
<img
|
|
877
|
-
src=${googleAuth.user?.picture || getAvatarUrl(googleAuth.user?.name)}
|
|
878
|
-
alt="Avatar do Google"
|
|
879
|
-
className="h-12 w-12 rounded-full border border-slate-700 bg-slate-900 object-cover"
|
|
880
|
-
/>
|
|
743
|
+
<img src=${googleAuth.user?.picture || getAvatarUrl(googleAuth.user?.name)} alt="Avatar do Google" className="h-12 w-12 rounded-full border border-slate-700 bg-slate-900 object-cover" />
|
|
881
744
|
<div className="min-w-0">
|
|
882
745
|
<p className="truncate text-sm font-semibold text-slate-100">${googleAuth.user?.name || 'Conta Google'}</p>
|
|
883
746
|
<p className="truncate text-xs text-slate-400">${googleAuth.user?.email || ''}</p>
|
|
884
|
-
<p className="truncate text-[11px] text-slate-500">
|
|
885
|
-
${googleAuth.expiresAt ? `Sessão válida até ${new Date(googleAuth.expiresAt).toLocaleString('pt-BR')}` : 'Sessão ativa'}
|
|
886
|
-
</p>
|
|
747
|
+
<p className="truncate text-[11px] text-slate-500">${googleAuth.expiresAt ? `Sessão válida até ${new Date(googleAuth.expiresAt).toLocaleString('pt-BR')}` : 'Sessão ativa'}</p>
|
|
887
748
|
</div>
|
|
888
749
|
</div>
|
|
889
750
|
`
|
|
@@ -891,22 +752,12 @@ function ProfilePage({
|
|
|
891
752
|
<div className="space-y-3">
|
|
892
753
|
<div className="rounded-xl border border-cyan-500/20 bg-cyan-500/5 p-3">
|
|
893
754
|
<p className="text-sm font-semibold text-cyan-200">Entrar com Google</p>
|
|
894
|
-
<p className="mt-1 text-xs text-slate-300">
|
|
895
|
-
${googleLoginEnabled
|
|
896
|
-
? 'Use a mesma conta Google usada na criação de packs para carregar seus dados e packs automaticamente.'
|
|
897
|
-
: 'Login Google indisponível no momento.'}
|
|
898
|
-
</p>
|
|
755
|
+
<p className="mt-1 text-xs text-slate-300">${googleLoginEnabled ? 'Use a mesma conta Google usada na criação de packs para carregar seus dados e packs automaticamente.' : 'Login Google indisponível no momento.'}</p>
|
|
899
756
|
</div>
|
|
900
757
|
${googleLoginEnabled
|
|
901
758
|
? html`
|
|
902
759
|
<div ref=${googleButtonRef} className="min-h-[42px] w-full max-w-[320px] overflow-hidden"></div>
|
|
903
|
-
${!googleSessionChecked
|
|
904
|
-
? html`<p className="text-xs text-slate-400">Verificando sessão Google...</p>`
|
|
905
|
-
: googleAuthBusy
|
|
906
|
-
? html`<p className="text-xs text-slate-400">Conectando conta Google...</p>`
|
|
907
|
-
: !googleAuthUiReady && !googleAuthError
|
|
908
|
-
? html`<p className="text-xs text-slate-400">Carregando login Google...</p>`
|
|
909
|
-
: null}
|
|
760
|
+
${!googleSessionChecked ? html`<p className="text-xs text-slate-400">Verificando sessão Google...</p>` : googleAuthBusy ? html`<p className="text-xs text-slate-400">Conectando conta Google...</p>` : !googleAuthUiReady && !googleAuthError ? html`<p className="text-xs text-slate-400">Carregando login Google...</p>` : null}
|
|
910
761
|
`
|
|
911
762
|
: null}
|
|
912
763
|
</div>
|
|
@@ -916,15 +767,29 @@ function ProfilePage({
|
|
|
916
767
|
</section>
|
|
917
768
|
|
|
918
769
|
${myPacksError ? html`<div className="rounded-2xl border border-rose-500/40 bg-rose-500/10 px-4 py-3 text-sm text-rose-200">${myPacksError}</div>` : null}
|
|
919
|
-
|
|
920
770
|
${hasGoogleLogin
|
|
921
771
|
? html`
|
|
922
772
|
<section className="grid grid-cols-2 lg:grid-cols-5 gap-3">
|
|
923
|
-
<article className="rounded-xl border border-slate-800 bg-slate-900/70 p-3"
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
<article className="rounded-xl border border-slate-800 bg-slate-900/70 p-3"
|
|
773
|
+
<article className="rounded-xl border border-slate-800 bg-slate-900/70 p-3">
|
|
774
|
+
<p className="text-[11px] text-slate-400">Total</p>
|
|
775
|
+
<p className="text-lg font-semibold">${shortNum(myProfileStats?.total || 0)}</p>
|
|
776
|
+
</article>
|
|
777
|
+
<article className="rounded-xl border border-slate-800 bg-slate-900/70 p-3">
|
|
778
|
+
<p className="text-[11px] text-slate-400">Publicados</p>
|
|
779
|
+
<p className="text-lg font-semibold">${shortNum(myProfileStats?.published || 0)}</p>
|
|
780
|
+
</article>
|
|
781
|
+
<article className="rounded-xl border border-slate-800 bg-slate-900/70 p-3">
|
|
782
|
+
<p className="text-[11px] text-slate-400">Rascunhos</p>
|
|
783
|
+
<p className="text-lg font-semibold">${shortNum(myProfileStats?.drafts || 0)}</p>
|
|
784
|
+
</article>
|
|
785
|
+
<article className="rounded-xl border border-slate-800 bg-slate-900/70 p-3">
|
|
786
|
+
<p className="text-[11px] text-slate-400">Privados</p>
|
|
787
|
+
<p className="text-lg font-semibold">${shortNum(myProfileStats?.private || 0)}</p>
|
|
788
|
+
</article>
|
|
789
|
+
<article className="rounded-xl border border-slate-800 bg-slate-900/70 p-3">
|
|
790
|
+
<p className="text-[11px] text-slate-400">Não listados</p>
|
|
791
|
+
<p className="text-lg font-semibold">${shortNum(myProfileStats?.unlisted || 0)}</p>
|
|
792
|
+
</article>
|
|
928
793
|
</section>
|
|
929
794
|
|
|
930
795
|
<section className="space-y-3">
|
|
@@ -933,19 +798,9 @@ function ProfilePage({
|
|
|
933
798
|
<span className="text-xs text-slate-400">${myPacksLoading ? 'Carregando...' : `${myPacks.length} pack(s)`}</span>
|
|
934
799
|
</div>
|
|
935
800
|
${myPacksLoading
|
|
936
|
-
? html`
|
|
937
|
-
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3">
|
|
938
|
-
${Array.from({ length: 6 }).map(
|
|
939
|
-
(_, index) => html`<div key=${index} className="h-40 rounded-2xl border border-slate-800 bg-slate-900/70 animate-pulse"></div>`,
|
|
940
|
-
)}
|
|
941
|
-
</div>
|
|
942
|
-
`
|
|
801
|
+
? html` <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3">${Array.from({ length: 6 }).map((_, index) => html`<div key=${index} className="h-40 rounded-2xl border border-slate-800 bg-slate-900/70 animate-pulse"></div>`)}</div> `
|
|
943
802
|
: myPacks.length
|
|
944
|
-
? html`
|
|
945
|
-
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3">
|
|
946
|
-
${myPacks.map((pack) => html`<${MyPackCard} key=${pack.id || pack.pack_key} pack=${pack} onOpenPublic=${onOpenPublicPack} />`)}
|
|
947
|
-
</div>
|
|
948
|
-
`
|
|
803
|
+
? html` <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3">${myPacks.map((pack) => html`<${MyPackCard} key=${pack.id || pack.pack_key} pack=${pack} onOpenPublic=${onOpenPublicPack} />`)}</div> `
|
|
949
804
|
: html`
|
|
950
805
|
<div className="rounded-2xl border border-dashed border-slate-700 bg-slate-900/60 p-6 text-center">
|
|
951
806
|
<p className="text-sm font-semibold text-slate-100">Nenhum pack encontrado para esta conta.</p>
|
|
@@ -965,25 +820,10 @@ function ToastStack({ toasts = [], onDismiss }) {
|
|
|
965
820
|
<div className="fixed right-3 top-16 z-[90] flex w-[min(92vw,380px)] flex-col gap-2">
|
|
966
821
|
${toasts.map(
|
|
967
822
|
(toast) => html`
|
|
968
|
-
<div
|
|
969
|
-
key=${toast.id}
|
|
970
|
-
className=${`rounded-2xl border px-3 py-2.5 shadow-xl backdrop-blur ${
|
|
971
|
-
toast.type === 'error'
|
|
972
|
-
? 'border-rose-500/35 bg-rose-500/15 text-rose-100'
|
|
973
|
-
: toast.type === 'warning'
|
|
974
|
-
? 'border-amber-500/35 bg-amber-500/15 text-amber-100'
|
|
975
|
-
: 'border-emerald-500/35 bg-emerald-500/15 text-emerald-100'
|
|
976
|
-
}`}
|
|
977
|
-
>
|
|
823
|
+
<div key=${toast.id} className=${`rounded-2xl border px-3 py-2.5 shadow-xl backdrop-blur ${toast.type === 'error' ? 'border-rose-500/35 bg-rose-500/15 text-rose-100' : toast.type === 'warning' ? 'border-amber-500/35 bg-amber-500/15 text-amber-100' : 'border-emerald-500/35 bg-emerald-500/15 text-emerald-100'}`}>
|
|
978
824
|
<div className="flex items-start justify-between gap-2">
|
|
979
825
|
<p className="text-sm leading-5">${toast.message || ''}</p>
|
|
980
|
-
<button
|
|
981
|
-
type="button"
|
|
982
|
-
onClick=${() => onDismiss?.(toast.id)}
|
|
983
|
-
className="rounded-md border border-white/10 px-1.5 py-0.5 text-[11px] text-white/70 hover:bg-white/10"
|
|
984
|
-
>
|
|
985
|
-
fechar
|
|
986
|
-
</button>
|
|
826
|
+
<button type="button" onClick=${() => onDismiss?.(toast.id)} className="rounded-md border border-white/10 px-1.5 py-0.5 text-[11px] text-white/70 hover:bg-white/10">fechar</button>
|
|
987
827
|
</div>
|
|
988
828
|
</div>
|
|
989
829
|
`,
|
|
@@ -992,17 +832,7 @@ function ToastStack({ toasts = [], onDismiss }) {
|
|
|
992
832
|
`;
|
|
993
833
|
}
|
|
994
834
|
|
|
995
|
-
function ConfirmDialog({
|
|
996
|
-
open = false,
|
|
997
|
-
title = 'Confirmar',
|
|
998
|
-
message = '',
|
|
999
|
-
confirmLabel = 'Confirmar',
|
|
1000
|
-
cancelLabel = 'Cancelar',
|
|
1001
|
-
busy = false,
|
|
1002
|
-
danger = false,
|
|
1003
|
-
onCancel,
|
|
1004
|
-
onConfirm,
|
|
1005
|
-
}) {
|
|
835
|
+
function ConfirmDialog({ open = false, title = 'Confirmar', message = '', confirmLabel = 'Confirmar', cancelLabel = 'Cancelar', busy = false, danger = false, onCancel, onConfirm }) {
|
|
1006
836
|
if (!open) return null;
|
|
1007
837
|
return html`
|
|
1008
838
|
<div className="fixed inset-0 z-[88] flex items-end justify-center bg-black/60 p-3 sm:items-center">
|
|
@@ -1011,26 +841,8 @@ function ConfirmDialog({
|
|
|
1011
841
|
<h3 className="text-base font-bold text-slate-100">${title}</h3>
|
|
1012
842
|
<p className="mt-2 text-sm text-slate-300">${message}</p>
|
|
1013
843
|
<div className="mt-4 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
|
1014
|
-
<button
|
|
1015
|
-
|
|
1016
|
-
onClick=${onCancel}
|
|
1017
|
-
disabled=${busy}
|
|
1018
|
-
className="h-10 rounded-xl border border-slate-700 px-4 text-sm text-slate-200 hover:bg-slate-800 disabled:opacity-60"
|
|
1019
|
-
>
|
|
1020
|
-
${cancelLabel}
|
|
1021
|
-
</button>
|
|
1022
|
-
<button
|
|
1023
|
-
type="button"
|
|
1024
|
-
onClick=${onConfirm}
|
|
1025
|
-
disabled=${busy}
|
|
1026
|
-
className=${`h-10 rounded-xl border px-4 text-sm font-semibold disabled:opacity-60 ${
|
|
1027
|
-
danger
|
|
1028
|
-
? 'border-rose-500/35 bg-rose-500/15 text-rose-100 hover:bg-rose-500/20'
|
|
1029
|
-
: 'border-emerald-500/35 bg-emerald-500/15 text-emerald-100 hover:bg-emerald-500/20'
|
|
1030
|
-
}`}
|
|
1031
|
-
>
|
|
1032
|
-
${busy ? 'Processando...' : confirmLabel}
|
|
1033
|
-
</button>
|
|
844
|
+
<button type="button" onClick=${onCancel} disabled=${busy} className="h-10 rounded-xl border border-slate-700 px-4 text-sm text-slate-200 hover:bg-slate-800 disabled:opacity-60">${cancelLabel}</button>
|
|
845
|
+
<button type="button" onClick=${onConfirm} disabled=${busy} className=${`h-10 rounded-xl border px-4 text-sm font-semibold disabled:opacity-60 ${danger ? 'border-rose-500/35 bg-rose-500/15 text-rose-100 hover:bg-rose-500/20' : 'border-emerald-500/35 bg-emerald-500/15 text-emerald-100 hover:bg-emerald-500/20'}`}>${busy ? 'Processando...' : confirmLabel}</button>
|
|
1034
846
|
</div>
|
|
1035
847
|
</div>
|
|
1036
848
|
</div>
|
|
@@ -1061,25 +873,15 @@ function PackActionsSheet({ pack, open = false, busyAction = '', onClose, onActi
|
|
|
1061
873
|
</div>
|
|
1062
874
|
</div>
|
|
1063
875
|
<div className="space-y-1">
|
|
1064
|
-
${PROFILE_PACK_ACTIONS.map(
|
|
1065
|
-
|
|
1066
|
-
key=${action.key}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
action.danger
|
|
1072
|
-
? 'border-rose-500/25 bg-rose-500/10 text-rose-100 hover:bg-rose-500/15'
|
|
1073
|
-
: 'border-slate-700 bg-slate-950/60 text-slate-100 hover:bg-slate-800'
|
|
1074
|
-
}`}
|
|
1075
|
-
>
|
|
1076
|
-
<span>${busyAction === action.key ? '⏳ ' : ''}${action.label}</span>
|
|
1077
|
-
</button>
|
|
1078
|
-
`)}
|
|
876
|
+
${PROFILE_PACK_ACTIONS.map(
|
|
877
|
+
(action) => html`
|
|
878
|
+
<button key=${action.key} type="button" onClick=${() => onAction?.(action.key, pack)} disabled=${Boolean(busyAction)} className=${`w-full rounded-xl border px-3 py-3 text-left text-sm transition disabled:opacity-60 ${action.danger ? 'border-rose-500/25 bg-rose-500/10 text-rose-100 hover:bg-rose-500/15' : 'border-slate-700 bg-slate-950/60 text-slate-100 hover:bg-slate-800'}`}>
|
|
879
|
+
<span>${busyAction === action.key ? '⏳ ' : ''}${action.label}</span>
|
|
880
|
+
</button>
|
|
881
|
+
`,
|
|
882
|
+
)}
|
|
1079
883
|
</div>
|
|
1080
|
-
<button type="button" onClick=${onClose} className="mt-3 h-10 w-full rounded-xl border border-slate-700 text-sm text-slate-200 hover:bg-slate-800">
|
|
1081
|
-
Fechar
|
|
1082
|
-
</button>
|
|
884
|
+
<button type="button" onClick=${onClose} className="mt-3 h-10 w-full rounded-xl border border-slate-700 text-sm text-slate-200 hover:bg-slate-800">Fechar</button>
|
|
1083
885
|
</section>
|
|
1084
886
|
</div>
|
|
1085
887
|
`;
|
|
@@ -1107,10 +909,22 @@ function PackAnalyticsModal({ open = false, pack = null, data = null, loading =
|
|
|
1107
909
|
? html`<div className="mt-4 rounded-xl border border-rose-500/35 bg-rose-500/10 p-3 text-sm text-rose-200">${error}</div>`
|
|
1108
910
|
: html`
|
|
1109
911
|
<div className="mt-4 grid grid-cols-2 md:grid-cols-4 gap-3">
|
|
1110
|
-
<article className="rounded-xl border border-slate-800 bg-slate-950/60 p-3"
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
912
|
+
<article className="rounded-xl border border-slate-800 bg-slate-950/60 p-3">
|
|
913
|
+
<p className="text-[11px] text-slate-400">Downloads</p>
|
|
914
|
+
<p className="text-lg font-bold text-slate-100">⬇ ${shortNum(analytics?.downloads || 0)}</p>
|
|
915
|
+
</article>
|
|
916
|
+
<article className="rounded-xl border border-slate-800 bg-slate-950/60 p-3">
|
|
917
|
+
<p className="text-[11px] text-slate-400">Likes</p>
|
|
918
|
+
<p className="text-lg font-bold text-slate-100">❤️ ${shortNum(analytics?.likes || 0)}</p>
|
|
919
|
+
</article>
|
|
920
|
+
<article className="rounded-xl border border-slate-800 bg-slate-950/60 p-3">
|
|
921
|
+
<p className="text-[11px] text-slate-400">Dislikes</p>
|
|
922
|
+
<p className="text-lg font-bold text-slate-100">👎 ${shortNum(analytics?.dislikes || 0)}</p>
|
|
923
|
+
</article>
|
|
924
|
+
<article className="rounded-xl border border-slate-800 bg-slate-950/60 p-3">
|
|
925
|
+
<p className="text-[11px] text-slate-400">Score</p>
|
|
926
|
+
<p className="text-lg font-bold text-slate-100">⭐ ${shortNum(analytics?.score || 0)}</p>
|
|
927
|
+
</article>
|
|
1114
928
|
</div>
|
|
1115
929
|
<div className="mt-4 grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
1116
930
|
<article className="rounded-xl border border-slate-800 bg-slate-950/60 p-3">
|
|
@@ -1159,14 +973,7 @@ function CreatorStatCard({ icon, label, value, tone = 'slate', sublabel = '' })
|
|
|
1159
973
|
`;
|
|
1160
974
|
}
|
|
1161
975
|
|
|
1162
|
-
function CreatorPackCardPro({
|
|
1163
|
-
pack,
|
|
1164
|
-
onOpenPublic,
|
|
1165
|
-
onOpenActions,
|
|
1166
|
-
onOpenManage,
|
|
1167
|
-
onQuickDelete,
|
|
1168
|
-
actionBusy = '',
|
|
1169
|
-
}) {
|
|
976
|
+
function CreatorPackCardPro({ pack, onOpenPublic, onOpenActions, onOpenManage, onQuickDelete, actionBusy = '' }) {
|
|
1170
977
|
const visibilityPill = formatVisibilityPill(pack?.visibility);
|
|
1171
978
|
const statusPill = formatStatusPill(pack?.status);
|
|
1172
979
|
const engagement = getPackEngagement(pack);
|
|
@@ -1183,17 +990,8 @@ function CreatorPackCardPro({
|
|
|
1183
990
|
<span className=${`inline-flex rounded-full border px-2 py-0.5 text-[10px] ${statusPill.className}`}>${statusPill.label}</span>
|
|
1184
991
|
<span className=${`inline-flex rounded-full border px-2 py-0.5 text-[10px] ${visibilityPill.className}`}>${visibilityPill.label}</span>
|
|
1185
992
|
</div>
|
|
1186
|
-
<button
|
|
1187
|
-
|
|
1188
|
-
onClick=${() => onOpenActions?.(pack)}
|
|
1189
|
-
className="absolute right-2 top-2 inline-flex h-8 w-8 items-center justify-center rounded-full border border-slate-700/90 bg-slate-950/80 text-slate-100 hover:bg-slate-800"
|
|
1190
|
-
title="Ações"
|
|
1191
|
-
>
|
|
1192
|
-
⋮
|
|
1193
|
-
</button>
|
|
1194
|
-
${isCoverHidden
|
|
1195
|
-
? html`<div className="absolute bottom-2 left-2 rounded-full border border-slate-600 bg-slate-950/80 px-2 py-0.5 text-[10px] text-slate-300">🔒 capa oculta no catálogo</div>`
|
|
1196
|
-
: null}
|
|
993
|
+
<button type="button" onClick=${() => onOpenActions?.(pack)} className="absolute right-2 top-2 inline-flex h-8 w-8 items-center justify-center rounded-full border border-slate-700/90 bg-slate-950/80 text-slate-100 hover:bg-slate-800" title="Ações">⋮</button>
|
|
994
|
+
${isCoverHidden ? html`<div className="absolute bottom-2 left-2 rounded-full border border-slate-600 bg-slate-950/80 px-2 py-0.5 text-[10px] text-slate-300">🔒 capa oculta no catálogo</div>` : null}
|
|
1197
995
|
</div>
|
|
1198
996
|
|
|
1199
997
|
<div className="min-w-0 p-2 space-y-1.5 sm:p-2.5 sm:space-y-2">
|
|
@@ -1210,80 +1008,30 @@ function CreatorPackCardPro({
|
|
|
1210
1008
|
</div>
|
|
1211
1009
|
|
|
1212
1010
|
<div className="grid grid-cols-3 gap-1.5">
|
|
1213
|
-
<button
|
|
1214
|
-
type="button"
|
|
1215
|
-
onClick=${() => onOpenManage?.(pack)}
|
|
1216
|
-
disabled=${Boolean(actionBusy)}
|
|
1217
|
-
className="inline-flex h-8 items-center justify-center rounded-lg border border-slate-700 bg-slate-950/60 px-1 text-[11px] text-slate-100 hover:bg-slate-800 disabled:opacity-60"
|
|
1218
|
-
title="Adicionar sticker"
|
|
1219
|
-
>
|
|
1011
|
+
<button type="button" onClick=${() => onOpenManage?.(pack)} disabled=${Boolean(actionBusy)} className="inline-flex h-8 items-center justify-center rounded-lg border border-slate-700 bg-slate-950/60 px-1 text-[11px] text-slate-100 hover:bg-slate-800 disabled:opacity-60" title="Adicionar sticker">
|
|
1220
1012
|
<span className="sm:hidden">➕</span>
|
|
1221
1013
|
<span className="hidden sm:inline">➕ Sticker</span>
|
|
1222
1014
|
</button>
|
|
1223
|
-
<button
|
|
1224
|
-
type="button"
|
|
1225
|
-
onClick=${() => onOpenManage?.(pack)}
|
|
1226
|
-
disabled=${Boolean(actionBusy)}
|
|
1227
|
-
className="inline-flex h-8 items-center justify-center rounded-lg border border-slate-700 bg-slate-950/60 px-1 text-[11px] text-slate-100 hover:bg-slate-800 disabled:opacity-60"
|
|
1228
|
-
title="Editar pack"
|
|
1229
|
-
>
|
|
1015
|
+
<button type="button" onClick=${() => onOpenManage?.(pack)} disabled=${Boolean(actionBusy)} className="inline-flex h-8 items-center justify-center rounded-lg border border-slate-700 bg-slate-950/60 px-1 text-[11px] text-slate-100 hover:bg-slate-800 disabled:opacity-60" title="Editar pack">
|
|
1230
1016
|
<span className="sm:hidden">✏️</span>
|
|
1231
1017
|
<span className="hidden sm:inline">✏️ Editar</span>
|
|
1232
1018
|
</button>
|
|
1233
|
-
<button
|
|
1234
|
-
type="button"
|
|
1235
|
-
onClick=${() => onQuickDelete?.(pack)}
|
|
1236
|
-
disabled=${Boolean(actionBusy)}
|
|
1237
|
-
className="inline-flex h-8 items-center justify-center rounded-lg border border-rose-500/25 bg-rose-500/10 px-1 text-[11px] text-rose-100 hover:bg-rose-500/15 disabled:opacity-60"
|
|
1238
|
-
title="Excluir pack"
|
|
1239
|
-
>
|
|
1019
|
+
<button type="button" onClick=${() => onQuickDelete?.(pack)} disabled=${Boolean(actionBusy)} className="inline-flex h-8 items-center justify-center rounded-lg border border-rose-500/25 bg-rose-500/10 px-1 text-[11px] text-rose-100 hover:bg-rose-500/15 disabled:opacity-60" title="Excluir pack">
|
|
1240
1020
|
<span className="sm:hidden">🗑️</span>
|
|
1241
1021
|
<span className="hidden sm:inline">🗑️ Excluir</span>
|
|
1242
1022
|
</button>
|
|
1243
1023
|
</div>
|
|
1244
1024
|
|
|
1245
1025
|
<div className="flex gap-2">
|
|
1246
|
-
<button
|
|
1247
|
-
|
|
1248
|
-
onClick=${() => onOpenManage?.(pack)}
|
|
1249
|
-
disabled=${Boolean(actionBusy)}
|
|
1250
|
-
className="inline-flex h-8 flex-1 items-center justify-center rounded-xl border border-cyan-500/35 bg-cyan-500/10 px-2.5 text-[11px] font-semibold text-cyan-100 hover:bg-cyan-500/20 disabled:opacity-60 sm:h-9 sm:text-xs"
|
|
1251
|
-
>
|
|
1252
|
-
${actionBusy === 'manage' ? 'Abrindo...' : 'Gerenciar pack'}
|
|
1253
|
-
</button>
|
|
1254
|
-
${shareable
|
|
1255
|
-
? html`
|
|
1256
|
-
<button
|
|
1257
|
-
type="button"
|
|
1258
|
-
onClick=${() => onOpenPublic?.(pack.pack_key)}
|
|
1259
|
-
className="inline-flex h-8 items-center justify-center rounded-xl border border-slate-700 bg-slate-950/60 px-2.5 text-[11px] font-medium text-slate-100 hover:bg-slate-800 sm:h-9 sm:px-3 sm:text-xs"
|
|
1260
|
-
>
|
|
1261
|
-
Abrir
|
|
1262
|
-
</button>
|
|
1263
|
-
`
|
|
1264
|
-
: html`<span className="inline-flex h-9 items-center justify-center rounded-xl border border-slate-800 bg-slate-950/40 px-3 text-[11px] text-slate-500">Sem link público</span>`}
|
|
1026
|
+
<button type="button" onClick=${() => onOpenManage?.(pack)} disabled=${Boolean(actionBusy)} className="inline-flex h-8 flex-1 items-center justify-center rounded-xl border border-cyan-500/35 bg-cyan-500/10 px-2.5 text-[11px] font-semibold text-cyan-100 hover:bg-cyan-500/20 disabled:opacity-60 sm:h-9 sm:text-xs">${actionBusy === 'manage' ? 'Abrindo...' : 'Gerenciar pack'}</button>
|
|
1027
|
+
${shareable ? html` <button type="button" onClick=${() => onOpenPublic?.(pack.pack_key)} className="inline-flex h-8 items-center justify-center rounded-xl border border-slate-700 bg-slate-950/60 px-2.5 text-[11px] font-medium text-slate-100 hover:bg-slate-800 sm:h-9 sm:px-3 sm:text-xs">Abrir</button> ` : html`<span className="inline-flex h-9 items-center justify-center rounded-xl border border-slate-800 bg-slate-950/40 px-3 text-[11px] text-slate-500">Sem link público</span>`}
|
|
1265
1028
|
</div>
|
|
1266
1029
|
</div>
|
|
1267
1030
|
</article>
|
|
1268
1031
|
`;
|
|
1269
1032
|
}
|
|
1270
1033
|
|
|
1271
|
-
function PackManagerModal({
|
|
1272
|
-
open = false,
|
|
1273
|
-
data = null,
|
|
1274
|
-
loading = false,
|
|
1275
|
-
error = '',
|
|
1276
|
-
busyAction = '',
|
|
1277
|
-
onClose,
|
|
1278
|
-
onRefresh,
|
|
1279
|
-
onSaveMetadata,
|
|
1280
|
-
onAddSticker,
|
|
1281
|
-
onRemoveSticker,
|
|
1282
|
-
onReplaceSticker,
|
|
1283
|
-
onSetCover,
|
|
1284
|
-
onReorder,
|
|
1285
|
-
onOpenAnalytics,
|
|
1286
|
-
}) {
|
|
1034
|
+
function PackManagerModal({ open = false, data = null, loading = false, error = '', busyAction = '', onClose, onRefresh, onSaveMetadata, onAddSticker, onRemoveSticker, onReplaceSticker, onSetCover, onReorder, onOpenAnalytics }) {
|
|
1287
1035
|
const pack = data?.pack || null;
|
|
1288
1036
|
const publishState = data?.publish_state || null;
|
|
1289
1037
|
const analytics = data?.analytics || null;
|
|
@@ -1321,9 +1069,7 @@ function PackManagerModal({
|
|
|
1321
1069
|
|
|
1322
1070
|
const orderMap = new Map(items.map((item) => [item.sticker_id, item]));
|
|
1323
1071
|
const orderedItems = orderIds.map((id) => orderMap.get(id)).filter(Boolean);
|
|
1324
|
-
const orderDirty =
|
|
1325
|
-
orderedItems.length === items.length &&
|
|
1326
|
-
orderedItems.some((item, index) => String(item?.sticker_id || '') !== String(items[index]?.sticker_id || ''));
|
|
1072
|
+
const orderDirty = orderedItems.length === items.length && orderedItems.some((item, index) => String(item?.sticker_id || '') !== String(items[index]?.sticker_id || ''));
|
|
1327
1073
|
|
|
1328
1074
|
return html`
|
|
1329
1075
|
<div className="fixed inset-0 z-[85] flex items-end justify-center bg-black/65 p-2 sm:items-center sm:p-4">
|
|
@@ -1337,11 +1083,7 @@ function PackManagerModal({
|
|
|
1337
1083
|
<p className="truncate text-xs text-slate-500">${pack?.pack_key || '-'}</p>
|
|
1338
1084
|
</div>
|
|
1339
1085
|
<div className="flex w-full flex-wrap items-center gap-1.5 sm:w-auto sm:justify-end sm:gap-2">
|
|
1340
|
-
<label
|
|
1341
|
-
className=${`inline-flex h-9 cursor-pointer items-center rounded-xl border border-emerald-500/35 bg-emerald-500/10 px-2.5 text-[11px] font-semibold text-emerald-100 hover:bg-emerald-500/20 sm:h-10 sm:px-3 sm:text-xs ${
|
|
1342
|
-
busyAction ? 'pointer-events-none opacity-60' : ''
|
|
1343
|
-
}`}
|
|
1344
|
-
>
|
|
1086
|
+
<label className=${`inline-flex h-9 cursor-pointer items-center rounded-xl border border-emerald-500/35 bg-emerald-500/10 px-2.5 text-[11px] font-semibold text-emerald-100 hover:bg-emerald-500/20 sm:h-10 sm:px-3 sm:text-xs ${busyAction ? 'pointer-events-none opacity-60' : ''}`}>
|
|
1345
1087
|
<span className="sm:hidden">➕ Sticker</span>
|
|
1346
1088
|
<span className="hidden sm:inline">➕ Adicionar sticker</span>
|
|
1347
1089
|
<input
|
|
@@ -1392,10 +1134,22 @@ function PackManagerModal({
|
|
|
1392
1134
|
</div>
|
|
1393
1135
|
</div>
|
|
1394
1136
|
<div className="grid grid-cols-2 gap-2 text-xs">
|
|
1395
|
-
<div className="rounded-xl border border-slate-800 bg-slate-900/60 p-2"
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1137
|
+
<div className="rounded-xl border border-slate-800 bg-slate-900/60 p-2">
|
|
1138
|
+
<p className="text-slate-400">Stickers</p>
|
|
1139
|
+
<p className="font-semibold text-slate-100">${shortNum(pack.sticker_count || 0)}</p>
|
|
1140
|
+
</div>
|
|
1141
|
+
<div className="rounded-xl border border-slate-800 bg-slate-900/60 p-2">
|
|
1142
|
+
<p className="text-slate-400">Downloads</p>
|
|
1143
|
+
<p className="font-semibold text-slate-100">${shortNum(analytics?.downloads || 0)}</p>
|
|
1144
|
+
</div>
|
|
1145
|
+
<div className="rounded-xl border border-slate-800 bg-slate-900/60 p-2">
|
|
1146
|
+
<p className="text-slate-400">Likes</p>
|
|
1147
|
+
<p className="font-semibold text-slate-100">${shortNum(analytics?.likes || 0)}</p>
|
|
1148
|
+
</div>
|
|
1149
|
+
<div className="rounded-xl border border-slate-800 bg-slate-900/60 p-2">
|
|
1150
|
+
<p className="text-slate-400">Pronto p/ publicar</p>
|
|
1151
|
+
<p className="font-semibold text-slate-100">${publishState?.consistency?.can_publish ? 'Sim' : 'Não'}</p>
|
|
1152
|
+
</div>
|
|
1399
1153
|
</div>
|
|
1400
1154
|
</section>
|
|
1401
1155
|
|
|
@@ -1414,15 +1168,15 @@ function PackManagerModal({
|
|
|
1414
1168
|
>
|
|
1415
1169
|
<div>
|
|
1416
1170
|
<label className="mb-1 block text-[11px] uppercase tracking-wide text-slate-400">Nome</label>
|
|
1417
|
-
<input value=${name} onChange=${(e) => setName(e.target.value)}
|
|
1171
|
+
<input value=${name} onChange=${(e) => setName(e.target.value)} maxlength="120" className="h-11 w-full rounded-xl border border-slate-700 bg-slate-900 px-3 text-sm text-slate-100 outline-none focus:border-cyan-400/40" />
|
|
1418
1172
|
</div>
|
|
1419
1173
|
<div>
|
|
1420
1174
|
<label className="mb-1 block text-[11px] uppercase tracking-wide text-slate-400">Publisher</label>
|
|
1421
|
-
<input value=${publisher} onChange=${(e) => setPublisher(e.target.value)}
|
|
1175
|
+
<input value=${publisher} onChange=${(e) => setPublisher(e.target.value)} maxlength="120" className="h-11 w-full rounded-xl border border-slate-700 bg-slate-900 px-3 text-sm text-slate-100 outline-none focus:border-cyan-400/40" />
|
|
1422
1176
|
</div>
|
|
1423
1177
|
<div>
|
|
1424
1178
|
<label className="mb-1 block text-[11px] uppercase tracking-wide text-slate-400">Descrição</label>
|
|
1425
|
-
<textarea value=${description} onChange=${(e) => setDescription(e.target.value)} rows="3"
|
|
1179
|
+
<textarea value=${description} onChange=${(e) => setDescription(e.target.value)} rows="3" maxlength="1024" className="w-full rounded-xl border border-slate-700 bg-slate-900 px-3 py-2 text-sm text-slate-100 outline-none focus:border-cyan-400/40"></textarea>
|
|
1426
1180
|
</div>
|
|
1427
1181
|
<div>
|
|
1428
1182
|
<label className="mb-1 block text-[11px] uppercase tracking-wide text-slate-400">Tags (separadas por vírgula)</label>
|
|
@@ -1437,9 +1191,7 @@ function PackManagerModal({
|
|
|
1437
1191
|
<option value="private">Privado</option>
|
|
1438
1192
|
</select>
|
|
1439
1193
|
</div>
|
|
1440
|
-
<button type="submit" disabled=${Boolean(busyAction)} className="h-11 w-full rounded-xl border border-cyan-500/35 bg-cyan-500/10 text-sm font-semibold text-cyan-100 hover:bg-cyan-500/20 disabled:opacity-60">
|
|
1441
|
-
${busyAction === 'saveMetadata' ? 'Salvando...' : 'Salvar alterações'}
|
|
1442
|
-
</button>
|
|
1194
|
+
<button type="submit" disabled=${Boolean(busyAction)} className="h-11 w-full rounded-xl border border-cyan-500/35 bg-cyan-500/10 text-sm font-semibold text-cyan-100 hover:bg-cyan-500/20 disabled:opacity-60">${busyAction === 'saveMetadata' ? 'Salvando...' : 'Salvar alterações'}</button>
|
|
1443
1195
|
</form>
|
|
1444
1196
|
</aside>
|
|
1445
1197
|
|
|
@@ -1449,18 +1201,7 @@ function PackManagerModal({
|
|
|
1449
1201
|
<h4 className="text-base font-bold text-slate-100">Stickers do pack</h4>
|
|
1450
1202
|
<p className="text-[11px] text-slate-400">Arraste para reordenar. Use ⭐, 🔁 e ❌.</p>
|
|
1451
1203
|
</div>
|
|
1452
|
-
${orderDirty
|
|
1453
|
-
? html`
|
|
1454
|
-
<button
|
|
1455
|
-
type="button"
|
|
1456
|
-
onClick=${() => onReorder?.(orderIds)}
|
|
1457
|
-
disabled=${Boolean(busyAction)}
|
|
1458
|
-
className="h-9 rounded-xl border border-amber-500/35 bg-amber-500/10 px-2.5 text-[11px] font-semibold text-amber-100 hover:bg-amber-500/20 disabled:opacity-60 sm:h-10 sm:px-3 sm:text-xs"
|
|
1459
|
-
>
|
|
1460
|
-
${busyAction === 'reorder' ? 'Salvando ordem...' : 'Salvar ordem'}
|
|
1461
|
-
</button>
|
|
1462
|
-
`
|
|
1463
|
-
: null}
|
|
1204
|
+
${orderDirty ? html` <button type="button" onClick=${() => onReorder?.(orderIds)} disabled=${Boolean(busyAction)} className="h-9 rounded-xl border border-amber-500/35 bg-amber-500/10 px-2.5 text-[11px] font-semibold text-amber-100 hover:bg-amber-500/20 disabled:opacity-60 sm:h-10 sm:px-3 sm:text-xs">${busyAction === 'reorder' ? 'Salvando ordem...' : 'Salvar ordem'}</button> ` : null}
|
|
1464
1205
|
</div>
|
|
1465
1206
|
|
|
1466
1207
|
${orderedItems.length
|
|
@@ -1493,21 +1234,8 @@ function PackManagerModal({
|
|
|
1493
1234
|
<div className="p-1.5 space-y-1.5 sm:p-2 sm:space-y-2">
|
|
1494
1235
|
<p className="truncate text-[10px] text-slate-500">${item.sticker_id}</p>
|
|
1495
1236
|
<div className="grid grid-cols-3 gap-1.5">
|
|
1496
|
-
<button
|
|
1497
|
-
|
|
1498
|
-
title="Definir como capa"
|
|
1499
|
-
onClick=${() => onSetCover?.(item.sticker_id)}
|
|
1500
|
-
disabled=${Boolean(busyAction)}
|
|
1501
|
-
className="h-8 rounded-lg border border-amber-500/30 bg-amber-500/10 text-[11px] text-amber-100 hover:bg-amber-500/15 disabled:opacity-60"
|
|
1502
|
-
>
|
|
1503
|
-
⭐
|
|
1504
|
-
</button>
|
|
1505
|
-
<label
|
|
1506
|
-
className=${`inline-flex h-8 cursor-pointer items-center justify-center rounded-lg border border-cyan-500/30 bg-cyan-500/10 px-2 text-[11px] text-cyan-100 hover:bg-cyan-500/15 ${
|
|
1507
|
-
busyAction ? 'pointer-events-none opacity-60' : ''
|
|
1508
|
-
}`}
|
|
1509
|
-
title="Substituir sticker"
|
|
1510
|
-
>
|
|
1237
|
+
<button type="button" title="Definir como capa" onClick=${() => onSetCover?.(item.sticker_id)} disabled=${Boolean(busyAction)} className="h-8 rounded-lg border border-amber-500/30 bg-amber-500/10 text-[11px] text-amber-100 hover:bg-amber-500/15 disabled:opacity-60">⭐</button>
|
|
1238
|
+
<label className=${`inline-flex h-8 cursor-pointer items-center justify-center rounded-lg border border-cyan-500/30 bg-cyan-500/10 px-2 text-[11px] text-cyan-100 hover:bg-cyan-500/15 ${busyAction ? 'pointer-events-none opacity-60' : ''}`} title="Substituir sticker">
|
|
1511
1239
|
🔁
|
|
1512
1240
|
<input
|
|
1513
1241
|
type="file"
|
|
@@ -1522,15 +1250,7 @@ function PackManagerModal({
|
|
|
1522
1250
|
}}
|
|
1523
1251
|
/>
|
|
1524
1252
|
</label>
|
|
1525
|
-
<button
|
|
1526
|
-
type="button"
|
|
1527
|
-
title="Remover sticker"
|
|
1528
|
-
onClick=${() => onRemoveSticker?.(item.sticker_id)}
|
|
1529
|
-
disabled=${Boolean(busyAction)}
|
|
1530
|
-
className="h-8 rounded-lg border border-rose-500/30 bg-rose-500/10 text-[11px] text-rose-100 hover:bg-rose-500/15 disabled:opacity-60"
|
|
1531
|
-
>
|
|
1532
|
-
❌
|
|
1533
|
-
</button>
|
|
1253
|
+
<button type="button" title="Remover sticker" onClick=${() => onRemoveSticker?.(item.sticker_id)} disabled=${Boolean(busyAction)} className="h-8 rounded-lg border border-rose-500/30 bg-rose-500/10 text-[11px] text-rose-100 hover:bg-rose-500/15 disabled:opacity-60">❌</button>
|
|
1534
1254
|
</div>
|
|
1535
1255
|
</div>
|
|
1536
1256
|
</article>
|
|
@@ -1555,25 +1275,7 @@ function PackManagerModal({
|
|
|
1555
1275
|
`;
|
|
1556
1276
|
}
|
|
1557
1277
|
|
|
1558
|
-
function CreatorProfileDashboard({
|
|
1559
|
-
googleAuthConfig,
|
|
1560
|
-
googleAuth,
|
|
1561
|
-
googleAuthBusy,
|
|
1562
|
-
googleAuthError,
|
|
1563
|
-
googleSessionChecked,
|
|
1564
|
-
myPacks,
|
|
1565
|
-
myPacksLoading,
|
|
1566
|
-
myPacksError,
|
|
1567
|
-
myProfileStats,
|
|
1568
|
-
onBack,
|
|
1569
|
-
onRefresh,
|
|
1570
|
-
onLogout,
|
|
1571
|
-
onOpenPublicPack,
|
|
1572
|
-
onOpenPackActions,
|
|
1573
|
-
onOpenManagePack,
|
|
1574
|
-
onRequestDeletePack,
|
|
1575
|
-
packActionBusyByKey = {},
|
|
1576
|
-
}) {
|
|
1278
|
+
function CreatorProfileDashboard({ googleAuthConfig, googleAuth, googleAuthBusy, googleAuthError, googleSessionChecked, myPacks, myPacksLoading, myPacksError, myProfileStats, onBack, onRefresh, onLogout, onOpenPublicPack, onOpenPackActions, onOpenManagePack, onRequestDeletePack, packActionBusyByKey = {} }) {
|
|
1577
1279
|
const [packSearch, setPackSearch] = useState('');
|
|
1578
1280
|
const [packSort, setPackSort] = useState('recent');
|
|
1579
1281
|
const [packFilter, setPackFilter] = useState('all');
|
|
@@ -1605,15 +1307,7 @@ function CreatorProfileDashboard({
|
|
|
1605
1307
|
if (packFilter === 'unlisted' && visibility !== 'unlisted') return false;
|
|
1606
1308
|
}
|
|
1607
1309
|
if (!q) return true;
|
|
1608
|
-
const searchable = [
|
|
1609
|
-
pack?.name,
|
|
1610
|
-
pack?.publisher,
|
|
1611
|
-
pack?.pack_key,
|
|
1612
|
-
pack?.description,
|
|
1613
|
-
...(Array.isArray(pack?.manual_tags) ? pack.manual_tags : []),
|
|
1614
|
-
]
|
|
1615
|
-
.map((value) => normalizeToken(value))
|
|
1616
|
-
.join(' ');
|
|
1310
|
+
const searchable = [pack?.name, pack?.publisher, pack?.pack_key, pack?.description, ...(Array.isArray(pack?.manual_tags) ? pack.manual_tags : [])].map((value) => normalizeToken(value)).join(' ');
|
|
1617
1311
|
return searchable.includes(q);
|
|
1618
1312
|
});
|
|
1619
1313
|
|
|
@@ -1657,11 +1351,7 @@ function CreatorProfileDashboard({
|
|
|
1657
1351
|
<div className="relative">
|
|
1658
1352
|
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
1659
1353
|
<div className="flex min-w-0 items-center gap-3">
|
|
1660
|
-
<img
|
|
1661
|
-
src=${googleAuth?.user?.picture || getAvatarUrl(googleAuth?.user?.name || 'creator')}
|
|
1662
|
-
alt="Avatar"
|
|
1663
|
-
className="h-20 w-20 rounded-2xl border border-slate-700 bg-slate-900 object-cover sm:h-24 sm:w-24"
|
|
1664
|
-
/>
|
|
1354
|
+
<img src=${googleAuth?.user?.picture || getAvatarUrl(googleAuth?.user?.name || 'creator')} alt="Avatar" className="h-20 w-20 rounded-2xl border border-slate-700 bg-slate-900 object-cover sm:h-24 sm:w-24" />
|
|
1665
1355
|
<div className="min-w-0">
|
|
1666
1356
|
<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
1357
|
<h1 className="truncate text-2xl font-extrabold tracking-tight text-slate-100 sm:text-3xl">Gerenciamento de Packs</h1>
|
|
@@ -1669,36 +1359,39 @@ function CreatorProfileDashboard({
|
|
|
1669
1359
|
<p className="mt-0.5 text-[11px] text-slate-500">Área exclusiva para gerenciamento dos seus packs.</p>
|
|
1670
1360
|
</div>
|
|
1671
1361
|
</div>
|
|
1672
|
-
${hasGoogleLogin
|
|
1673
|
-
? html`<button type="button" onClick=${onLogout} disabled=${googleAuthBusy} className="inline-flex h-10 items-center rounded-xl border border-slate-700 px-3 text-xs text-slate-200 hover:bg-slate-800 disabled:opacity-60">${googleAuthBusy ? 'Saindo...' : 'Sair'}</button>`
|
|
1674
|
-
: null}
|
|
1362
|
+
${hasGoogleLogin ? html`<button type="button" onClick=${onLogout} disabled=${googleAuthBusy} className="inline-flex h-10 items-center rounded-xl border border-slate-700 px-3 text-xs text-slate-200 hover:bg-slate-800 disabled:opacity-60">${googleAuthBusy ? 'Saindo...' : 'Sair'}</button>` : null}
|
|
1675
1363
|
</div>
|
|
1676
1364
|
|
|
1677
1365
|
${hasGoogleLogin
|
|
1678
1366
|
? html`
|
|
1679
1367
|
<div className="mt-3 rounded-2xl border border-slate-800 bg-slate-950/35 p-2.5">
|
|
1680
1368
|
<div className="grid grid-cols-2 gap-2 sm:grid-cols-4">
|
|
1681
|
-
<div className="rounded-xl border border-slate-800 bg-slate-900/50 px-2.5 py-2"
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1369
|
+
<div className="rounded-xl border border-slate-800 bg-slate-900/50 px-2.5 py-2">
|
|
1370
|
+
<p className="text-[10px] text-slate-400">Packs</p>
|
|
1371
|
+
<p className="text-base font-bold text-slate-100">${shortNum(myProfileStats?.total || 0)}</p>
|
|
1372
|
+
</div>
|
|
1373
|
+
<div className="rounded-xl border border-slate-800 bg-slate-900/50 px-2.5 py-2">
|
|
1374
|
+
<p className="text-[10px] text-slate-400">Downloads</p>
|
|
1375
|
+
<p className="text-base font-bold text-slate-100">${shortNum(totals.downloads)}</p>
|
|
1376
|
+
</div>
|
|
1377
|
+
<div className="rounded-xl border border-slate-800 bg-slate-900/50 px-2.5 py-2">
|
|
1378
|
+
<p className="text-[10px] text-slate-400">Likes</p>
|
|
1379
|
+
<p className="text-base font-bold text-slate-100">${shortNum(totals.likes)}</p>
|
|
1380
|
+
</div>
|
|
1381
|
+
<div className="rounded-xl border border-slate-800 bg-slate-900/50 px-2.5 py-2">
|
|
1382
|
+
<p className="text-[10px] text-slate-400">Stickers publicados</p>
|
|
1383
|
+
<p className="text-base font-bold text-slate-100">${shortNum(totals.publishedStickers)}</p>
|
|
1384
|
+
</div>
|
|
1685
1385
|
</div>
|
|
1686
1386
|
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
1687
|
-
${profileStatusChips.map(
|
|
1688
|
-
|
|
1689
|
-
key=${chip.key}
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
: 'border-slate-700 bg-slate-900/50 text-slate-300 hover:bg-slate-800'
|
|
1696
|
-
}`}
|
|
1697
|
-
>
|
|
1698
|
-
<span>${chip.label}</span>
|
|
1699
|
-
<span className="font-semibold">${shortNum(chip.value)}</span>
|
|
1700
|
-
</button>
|
|
1701
|
-
`)}
|
|
1387
|
+
${profileStatusChips.map(
|
|
1388
|
+
(chip) => html`
|
|
1389
|
+
<button key=${chip.key} type="button" onClick=${() => setPackFilter((prev) => (prev === chip.key ? 'all' : chip.key))} className=${`inline-flex items-center gap-1 rounded-full border px-2 py-1 text-[11px] ${packFilter === chip.key ? 'border-cyan-400/35 bg-cyan-500/10 text-cyan-100' : 'border-slate-700 bg-slate-900/50 text-slate-300 hover:bg-slate-800'}`}>
|
|
1390
|
+
<span>${chip.label}</span>
|
|
1391
|
+
<span className="font-semibold">${shortNum(chip.value)}</span>
|
|
1392
|
+
</button>
|
|
1393
|
+
`,
|
|
1394
|
+
)}
|
|
1702
1395
|
</div>
|
|
1703
1396
|
</div>
|
|
1704
1397
|
`
|
|
@@ -1712,16 +1405,9 @@ function CreatorProfileDashboard({
|
|
|
1712
1405
|
<div className="space-y-2.5">
|
|
1713
1406
|
<div className="rounded-xl border border-cyan-500/20 bg-cyan-500/5 p-3">
|
|
1714
1407
|
<p className="text-sm font-semibold text-cyan-200">Sessão necessária para gerenciamento</p>
|
|
1715
|
-
<p className="mt-1 text-xs text-slate-300">
|
|
1716
|
-
${googleLoginEnabled
|
|
1717
|
-
? 'Esta área é exclusiva para gerenciar seus packs e stickers. Redirecionando para o login...'
|
|
1718
|
-
: 'Login Google indisponível no momento.'}
|
|
1719
|
-
</p>
|
|
1408
|
+
<p className="mt-1 text-xs text-slate-300">${googleLoginEnabled ? 'Esta área é exclusiva para gerenciar seus packs e stickers. Redirecionando para o login...' : 'Login Google indisponível no momento.'}</p>
|
|
1720
1409
|
</div>
|
|
1721
|
-
${!googleSessionChecked
|
|
1722
|
-
? html`<p className="text-xs text-slate-400">Verificando sessão...</p>`
|
|
1723
|
-
: null}
|
|
1724
|
-
${googleAuthError ? html`<p className="text-xs text-rose-300">${googleAuthError}</p>` : null}
|
|
1410
|
+
${!googleSessionChecked ? html`<p className="text-xs text-slate-400">Verificando sessão...</p>` : null} ${googleAuthError ? html`<p className="text-xs text-rose-300">${googleAuthError}</p>` : null}
|
|
1725
1411
|
</div>
|
|
1726
1412
|
</section>
|
|
1727
1413
|
`
|
|
@@ -1741,75 +1427,37 @@ function CreatorProfileDashboard({
|
|
|
1741
1427
|
<div className="mt-2 grid gap-2 md:grid-cols-[minmax(0,1fr)_180px]">
|
|
1742
1428
|
<div className="relative">
|
|
1743
1429
|
<span className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-xs text-slate-400">🔎</span>
|
|
1744
|
-
<input
|
|
1745
|
-
type="search"
|
|
1746
|
-
value=${packSearch}
|
|
1747
|
-
onChange=${(e) => setPackSearch(e.target.value)}
|
|
1748
|
-
placeholder="Buscar packs..."
|
|
1749
|
-
className="h-10 w-full rounded-xl border border-slate-700 bg-slate-950/60 pl-9 pr-3 text-sm text-slate-100 placeholder:text-slate-500 outline-none focus:border-cyan-400/40"
|
|
1750
|
-
/>
|
|
1430
|
+
<input type="search" value=${packSearch} onChange=${(e) => setPackSearch(e.target.value)} placeholder="Buscar packs..." className="h-10 w-full rounded-xl border border-slate-700 bg-slate-950/60 pl-9 pr-3 text-sm text-slate-100 placeholder:text-slate-500 outline-none focus:border-cyan-400/40" />
|
|
1751
1431
|
</div>
|
|
1752
|
-
<select
|
|
1753
|
-
value=${packSort}
|
|
1754
|
-
onChange=${(e) => setPackSort(e.target.value)}
|
|
1755
|
-
className="h-10 rounded-xl border border-slate-700 bg-slate-950/60 px-3 text-sm text-slate-100 outline-none focus:border-cyan-400/40"
|
|
1756
|
-
>
|
|
1432
|
+
<select value=${packSort} onChange=${(e) => setPackSort(e.target.value)} className="h-10 rounded-xl border border-slate-700 bg-slate-950/60 px-3 text-sm text-slate-100 outline-none focus:border-cyan-400/40">
|
|
1757
1433
|
<option value="recent">Mais recente</option>
|
|
1758
1434
|
<option value="downloads">Mais downloads</option>
|
|
1759
1435
|
<option value="likes">Mais likes</option>
|
|
1760
1436
|
</select>
|
|
1761
1437
|
</div>
|
|
1762
1438
|
|
|
1763
|
-
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
1764
|
-
${packFilterOptions.map((option) => html`
|
|
1765
|
-
<button
|
|
1766
|
-
key=${option.key}
|
|
1767
|
-
type="button"
|
|
1768
|
-
onClick=${() => setPackFilter(option.key)}
|
|
1769
|
-
className=${`h-8 rounded-full border px-2.5 text-[11px] ${
|
|
1770
|
-
packFilter === option.key
|
|
1771
|
-
? 'border-cyan-400/35 bg-cyan-500/10 text-cyan-100'
|
|
1772
|
-
: 'border-slate-700 bg-slate-950/50 text-slate-300 hover:bg-slate-800'
|
|
1773
|
-
}`}
|
|
1774
|
-
>
|
|
1775
|
-
${option.label}
|
|
1776
|
-
</button>
|
|
1777
|
-
`)}
|
|
1778
|
-
</div>
|
|
1439
|
+
<div className="mt-2 flex flex-wrap gap-1.5">${packFilterOptions.map((option) => html` <button key=${option.key} type="button" onClick=${() => setPackFilter(option.key)} className=${`h-8 rounded-full border px-2.5 text-[11px] ${packFilter === option.key ? 'border-cyan-400/35 bg-cyan-500/10 text-cyan-100' : 'border-slate-700 bg-slate-950/50 text-slate-300 hover:bg-slate-800'}`}>${option.label}</button> `)}</div>
|
|
1779
1440
|
</div>
|
|
1780
1441
|
|
|
1781
1442
|
${myPacksLoading
|
|
1782
|
-
? html`
|
|
1783
|
-
<div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
|
|
1784
|
-
${Array.from({ length: 6 }).map((_, index) => html`<div key=${index} className="h-56 animate-pulse rounded-2xl border border-slate-800 bg-slate-900/70"></div>`)}
|
|
1785
|
-
</div>
|
|
1786
|
-
`
|
|
1443
|
+
? html` <div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">${Array.from({ length: 6 }).map((_, index) => html`<div key=${index} className="h-56 animate-pulse rounded-2xl border border-slate-800 bg-slate-900/70"></div>`)}</div> `
|
|
1787
1444
|
: filteredSortedPacks.length
|
|
1788
|
-
? html`
|
|
1789
|
-
<div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
|
|
1790
|
-
${filteredSortedPacks.map((pack) => html`
|
|
1791
|
-
<${CreatorPackCardPro}
|
|
1792
|
-
key=${pack.id || pack.pack_key}
|
|
1793
|
-
pack=${pack}
|
|
1794
|
-
onOpenPublic=${onOpenPublicPack}
|
|
1795
|
-
onOpenActions=${onOpenPackActions}
|
|
1796
|
-
onOpenManage=${onOpenManagePack}
|
|
1797
|
-
onQuickDelete=${onRequestDeletePack}
|
|
1798
|
-
actionBusy=${packActionBusyByKey?.[pack.pack_key] || ''}
|
|
1799
|
-
/>
|
|
1800
|
-
`)}
|
|
1801
|
-
</div>
|
|
1802
|
-
`
|
|
1445
|
+
? html` <div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">${filteredSortedPacks.map((pack) => html` <${CreatorPackCardPro} key=${pack.id || pack.pack_key} pack=${pack} onOpenPublic=${onOpenPublicPack} onOpenActions=${onOpenPackActions} onOpenManage=${onOpenManagePack} onQuickDelete=${onRequestDeletePack} actionBusy=${packActionBusyByKey?.[pack.pack_key] || ''} /> `)}</div> `
|
|
1803
1446
|
: html`
|
|
1804
1447
|
<div className="rounded-2xl border border-dashed border-slate-700 bg-slate-900/60 p-5 text-center">
|
|
1805
1448
|
<p className="text-sm font-semibold text-slate-100">${packs.length ? 'Nenhum pack corresponde aos filtros.' : 'Nenhum pack encontrado para esta conta.'}</p>
|
|
1806
|
-
<p className="mt-1 text-xs text-slate-400">
|
|
1807
|
-
${packs.length
|
|
1808
|
-
? 'Tente limpar busca/filtros para ver seus packs.'
|
|
1809
|
-
: html`Crie um pack com essa conta Google em <a href="/stickers/create/" className="text-cyan-300 underline">/stickers/create</a> e volte aqui.`}
|
|
1810
|
-
</p>
|
|
1449
|
+
<p className="mt-1 text-xs text-slate-400">${packs.length ? 'Tente limpar busca/filtros para ver seus packs.' : html`Crie um pack com essa conta Google em <a href="/stickers/create/" className="text-cyan-300 underline">/stickers/create</a> e volte aqui.`}</p>
|
|
1811
1450
|
${packs.length
|
|
1812
|
-
? html`<button
|
|
1451
|
+
? html`<button
|
|
1452
|
+
type="button"
|
|
1453
|
+
onClick=${() => {
|
|
1454
|
+
setPackSearch('');
|
|
1455
|
+
setPackFilter('all');
|
|
1456
|
+
}}
|
|
1457
|
+
className="mt-3 h-9 rounded-xl border border-slate-700 px-3 text-xs text-slate-100 hover:bg-slate-800"
|
|
1458
|
+
>
|
|
1459
|
+
Limpar filtros
|
|
1460
|
+
</button>`
|
|
1813
1461
|
: null}
|
|
1814
1462
|
</div>
|
|
1815
1463
|
`}
|
|
@@ -1824,35 +1472,16 @@ function OrphanCard({ sticker, hasNsfwAccess = true, onRequireLogin }) {
|
|
|
1824
1472
|
return html`
|
|
1825
1473
|
<article className="group rounded-2xl border border-slate-700/80 bg-slate-800/70 shadow-soft overflow-hidden transition-all duration-200 hover:-translate-y-0.5">
|
|
1826
1474
|
<div className="relative aspect-square bg-slate-900 overflow-hidden">
|
|
1827
|
-
<img
|
|
1828
|
-
src=${lockedByNsfw ? NSFW_STICKER_PLACEHOLDER_URL : sticker.url || DEFAULT_STICKER_PLACEHOLDER_URL}
|
|
1829
|
-
alt="Sticker sem pack"
|
|
1830
|
-
className=${`w-full h-full object-contain transition-transform duration-300 ${
|
|
1831
|
-
lockedByNsfw ? 'blur-md scale-105' : 'group-hover:scale-110'
|
|
1832
|
-
}`}
|
|
1833
|
-
loading="lazy"
|
|
1834
|
-
/>
|
|
1475
|
+
<img src=${lockedByNsfw ? NSFW_STICKER_PLACEHOLDER_URL : sticker.url || DEFAULT_STICKER_PLACEHOLDER_URL} alt="Sticker sem pack" className=${`w-full h-full object-contain transition-transform duration-300 ${lockedByNsfw ? 'blur-md scale-105' : 'group-hover:scale-110'}`} loading="lazy" />
|
|
1835
1476
|
${lockedByNsfw
|
|
1836
1477
|
? html`
|
|
1837
1478
|
<div className="absolute inset-0 flex items-center justify-center bg-slate-950/35 p-2">
|
|
1838
|
-
<button
|
|
1839
|
-
type="button"
|
|
1840
|
-
onClick=${() => onRequireLogin?.()}
|
|
1841
|
-
className="inline-flex h-8 items-center rounded-lg border border-amber-400/35 bg-amber-500/15 px-2.5 text-[10px] font-semibold text-amber-100"
|
|
1842
|
-
>
|
|
1843
|
-
Entrar para desbloquear
|
|
1844
|
-
</button>
|
|
1479
|
+
<button type="button" onClick=${() => onRequireLogin?.()} className="inline-flex h-8 items-center rounded-lg border border-amber-400/35 bg-amber-500/15 px-2.5 text-[10px] font-semibold text-amber-100">Entrar para desbloquear</button>
|
|
1845
1480
|
</div>
|
|
1846
1481
|
`
|
|
1847
1482
|
: null}
|
|
1848
1483
|
</div>
|
|
1849
|
-
<div className="p-2">
|
|
1850
|
-
${primaryTag(sticker)
|
|
1851
|
-
? html`<span className="inline-flex rounded-full border border-slate-600 bg-slate-900/80 px-2 py-0.5 text-[10px] text-slate-300">${primaryTag(
|
|
1852
|
-
sticker,
|
|
1853
|
-
)}</span>`
|
|
1854
|
-
: html`<span className="inline-flex rounded-full border border-slate-600 bg-slate-900/80 px-2 py-0.5 text-[10px] text-slate-400">sticker</span>`}
|
|
1855
|
-
</div>
|
|
1484
|
+
<div className="p-2">${primaryTag(sticker) ? html`<span className="inline-flex rounded-full border border-slate-600 bg-slate-900/80 px-2 py-0.5 text-[10px] text-slate-300">${primaryTag(sticker)}</span>` : html`<span className="inline-flex rounded-full border border-slate-600 bg-slate-900/80 px-2 py-0.5 text-[10px] text-slate-400">sticker</span>`}</div>
|
|
1856
1485
|
</article>
|
|
1857
1486
|
`;
|
|
1858
1487
|
}
|
|
@@ -1882,13 +1511,7 @@ function EmptyState({ onClear }) {
|
|
|
1882
1511
|
<div className="text-5xl mb-2">🧩</div>
|
|
1883
1512
|
<p className="text-slate-100 font-semibold">Nenhum pack encontrado</p>
|
|
1884
1513
|
<p className="text-slate-400 text-sm mt-1">Tente outra busca ou remova os filtros ativos.</p>
|
|
1885
|
-
<button
|
|
1886
|
-
type="button"
|
|
1887
|
-
onClick=${onClear}
|
|
1888
|
-
className="mt-4 inline-flex items-center justify-center rounded-xl border border-slate-600 px-4 py-2 text-sm text-slate-200 hover:bg-slate-700 transition"
|
|
1889
|
-
>
|
|
1890
|
-
Limpar filtros
|
|
1891
|
-
</button>
|
|
1514
|
+
<button type="button" onClick=${onClear} className="mt-4 inline-flex items-center justify-center rounded-xl border border-slate-600 px-4 py-2 text-sm text-slate-200 hover:bg-slate-700 transition">Limpar filtros</button>
|
|
1892
1515
|
</div>
|
|
1893
1516
|
`;
|
|
1894
1517
|
}
|
|
@@ -1907,30 +1530,13 @@ function CatalogSortPicker({ open = false, currentSort = DEFAULT_CATALOG_SORT, b
|
|
|
1907
1530
|
<p className="text-[11px] uppercase tracking-wide text-slate-400">Ordenar catálogo</p>
|
|
1908
1531
|
<p className="text-sm font-semibold text-slate-100">Escolha o tipo de ranking</p>
|
|
1909
1532
|
</div>
|
|
1910
|
-
<button
|
|
1911
|
-
type="button"
|
|
1912
|
-
onClick=${onClose}
|
|
1913
|
-
className="h-8 rounded-lg border border-slate-700 px-2.5 text-xs text-slate-300 hover:bg-slate-800 disabled:opacity-60"
|
|
1914
|
-
disabled=${busy}
|
|
1915
|
-
>
|
|
1916
|
-
Fechar
|
|
1917
|
-
</button>
|
|
1533
|
+
<button type="button" onClick=${onClose} className="h-8 rounded-lg border border-slate-700 px-2.5 text-xs text-slate-300 hover:bg-slate-800 disabled:opacity-60" disabled=${busy}>Fechar</button>
|
|
1918
1534
|
</div>
|
|
1919
1535
|
<div className="space-y-1.5">
|
|
1920
1536
|
${CATALOG_SORT_OPTIONS.map((option) => {
|
|
1921
1537
|
const active = selectedSort === option.value;
|
|
1922
1538
|
return html`
|
|
1923
|
-
<button
|
|
1924
|
-
key=${option.value}
|
|
1925
|
-
type="button"
|
|
1926
|
-
onClick=${() => onSelect?.(option.value)}
|
|
1927
|
-
disabled=${busy}
|
|
1928
|
-
className=${`w-full rounded-xl border px-3 py-2.5 text-left transition disabled:opacity-60 ${
|
|
1929
|
-
active
|
|
1930
|
-
? 'border-emerald-400/40 bg-emerald-500/10 text-emerald-100'
|
|
1931
|
-
: 'border-slate-700 bg-slate-900/70 text-slate-200 hover:bg-slate-800'
|
|
1932
|
-
}`}
|
|
1933
|
-
>
|
|
1539
|
+
<button key=${option.value} type="button" onClick=${() => onSelect?.(option.value)} disabled=${busy} className=${`w-full rounded-xl border px-3 py-2.5 text-left transition disabled:opacity-60 ${active ? 'border-emerald-400/40 bg-emerald-500/10 text-emerald-100' : 'border-slate-700 bg-slate-900/70 text-slate-200 hover:bg-slate-800'}`}>
|
|
1934
1540
|
<span className="flex items-center justify-between gap-2">
|
|
1935
1541
|
<span className="inline-flex items-center gap-2">
|
|
1936
1542
|
<span>${option.icon}</span>
|
|
@@ -1962,9 +1568,7 @@ function PackPageSkeleton() {
|
|
|
1962
1568
|
<div className="h-20 rounded-xl bg-slate-800"></div>
|
|
1963
1569
|
</div>
|
|
1964
1570
|
</div>
|
|
1965
|
-
<div className="grid grid-cols-3 gap-2 sm:gap-3">
|
|
1966
|
-
${Array.from({ length: 9 }).map((_, index) => html`<div key=${index} className="aspect-square rounded-xl border border-slate-700 bg-slate-800"></div>`)}
|
|
1967
|
-
</div>
|
|
1571
|
+
<div className="grid grid-cols-3 gap-2 sm:gap-3">${Array.from({ length: 9 }).map((_, index) => html`<div key=${index} className="aspect-square rounded-xl border border-slate-700 bg-slate-800"></div>`)}</div>
|
|
1968
1572
|
</section>
|
|
1969
1573
|
`;
|
|
1970
1574
|
}
|
|
@@ -1990,36 +1594,16 @@ function CreatorRankingSkeleton({ count = 8 }) {
|
|
|
1990
1594
|
`;
|
|
1991
1595
|
}
|
|
1992
1596
|
|
|
1993
|
-
function CreatorsRankingPage({
|
|
1994
|
-
creators = [],
|
|
1995
|
-
loading = false,
|
|
1996
|
-
error = '',
|
|
1997
|
-
sort = DEFAULT_CREATORS_SORT,
|
|
1998
|
-
onSortChange,
|
|
1999
|
-
onBack,
|
|
2000
|
-
onRetry,
|
|
2001
|
-
onOpenCreator,
|
|
2002
|
-
onOpenPack,
|
|
2003
|
-
}) {
|
|
1597
|
+
function CreatorsRankingPage({ creators = [], loading = false, error = '', sort = DEFAULT_CREATORS_SORT, onSortChange, onBack, onRetry, onOpenCreator, onOpenPack }) {
|
|
2004
1598
|
const selectedSort = normalizeCreatorsSort(sort);
|
|
2005
1599
|
|
|
2006
1600
|
return html`
|
|
2007
1601
|
<section className="space-y-4">
|
|
2008
1602
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
2009
|
-
<button
|
|
2010
|
-
type="button"
|
|
2011
|
-
onClick=${onBack}
|
|
2012
|
-
className="inline-flex h-10 items-center gap-2 rounded-xl border border-slate-700 px-3 text-sm text-slate-200 hover:bg-slate-800"
|
|
2013
|
-
>
|
|
2014
|
-
← Voltar para catálogo
|
|
2015
|
-
</button>
|
|
1603
|
+
<button type="button" onClick=${onBack} className="inline-flex h-10 items-center gap-2 rounded-xl border border-slate-700 px-3 text-sm text-slate-200 hover:bg-slate-800">← Voltar para catálogo</button>
|
|
2016
1604
|
<div className="flex items-center gap-2">
|
|
2017
1605
|
<span className="text-xs text-slate-400">Ordenar</span>
|
|
2018
|
-
<select
|
|
2019
|
-
value=${selectedSort}
|
|
2020
|
-
onChange=${(event) => onSortChange?.(event.target.value)}
|
|
2021
|
-
className="h-9 rounded-xl border border-slate-700 bg-slate-900 px-3 text-xs text-slate-200 outline-none"
|
|
2022
|
-
>
|
|
1606
|
+
<select value=${selectedSort} onChange=${(event) => onSortChange?.(event.target.value)} className="h-9 rounded-xl border border-slate-700 bg-slate-900 px-3 text-xs text-slate-200 outline-none">
|
|
2023
1607
|
<option value="popular">Popular</option>
|
|
2024
1608
|
<option value="likes">Likes</option>
|
|
2025
1609
|
<option value="downloads">Downloads</option>
|
|
@@ -2040,19 +1624,11 @@ function CreatorsRankingPage({
|
|
|
2040
1624
|
<div className="rounded-2xl border border-rose-500/30 bg-rose-500/10 p-4">
|
|
2041
1625
|
<p className="text-sm font-semibold text-rose-100">Falha ao carregar criadores</p>
|
|
2042
1626
|
<p className="mt-1 text-xs text-rose-200/90">${error}</p>
|
|
2043
|
-
<button
|
|
2044
|
-
type="button"
|
|
2045
|
-
onClick=${onRetry}
|
|
2046
|
-
className="mt-3 inline-flex h-9 items-center rounded-xl border border-rose-400/40 px-3 text-xs text-rose-100 hover:bg-rose-500/10"
|
|
2047
|
-
>
|
|
2048
|
-
Tentar novamente
|
|
2049
|
-
</button>
|
|
1627
|
+
<button type="button" onClick=${onRetry} className="mt-3 inline-flex h-9 items-center rounded-xl border border-rose-400/40 px-3 text-xs text-rose-100 hover:bg-rose-500/10">Tentar novamente</button>
|
|
2050
1628
|
</div>
|
|
2051
1629
|
`
|
|
2052
1630
|
: null}
|
|
2053
|
-
|
|
2054
1631
|
${loading ? html`<${CreatorRankingSkeleton} />` : null}
|
|
2055
|
-
|
|
2056
1632
|
${!loading && !error && !creators.length
|
|
2057
1633
|
? html`
|
|
2058
1634
|
<div className="rounded-2xl border border-dashed border-slate-700 bg-slate-900/50 p-8 text-center">
|
|
@@ -2061,46 +1637,31 @@ function CreatorsRankingPage({
|
|
|
2061
1637
|
</div>
|
|
2062
1638
|
`
|
|
2063
1639
|
: null}
|
|
2064
|
-
|
|
2065
1640
|
${!loading && creators.length
|
|
2066
1641
|
? html`
|
|
2067
1642
|
<div className="space-y-2">
|
|
2068
|
-
${creators.map(
|
|
2069
|
-
|
|
2070
|
-
<
|
|
2071
|
-
<
|
|
2072
|
-
|
|
2073
|
-
<div className="
|
|
2074
|
-
<
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
<
|
|
2079
|
-
|
|
2080
|
-
|
|
1643
|
+
${creators.map(
|
|
1644
|
+
(creator, index) => html`
|
|
1645
|
+
<article key=${creator.key || creator.publisher || index} className="fade-card rounded-2xl border border-slate-800 bg-slate-900/65 p-3">
|
|
1646
|
+
<div className="flex items-center gap-3">
|
|
1647
|
+
<img src=${creator.avatarUrl || getAvatarUrl(creator.publisher)} alt="" className="h-12 w-12 rounded-full bg-slate-800 border border-slate-700" loading="lazy" />
|
|
1648
|
+
<div className="min-w-0 flex-1">
|
|
1649
|
+
<div className="flex items-center gap-2">
|
|
1650
|
+
<p className="truncate text-sm font-semibold text-slate-100">${creator.publisher || 'Criador'}</p>
|
|
1651
|
+
${creator.verified ? html`<span className="rounded-full border border-cyan-400/30 bg-cyan-500/10 px-2 py-0.5 text-[10px] text-cyan-200">Verificado</span>` : null}
|
|
1652
|
+
</div>
|
|
1653
|
+
<div className="mt-1 flex flex-wrap gap-x-3 gap-y-1 text-[11px] text-slate-400">
|
|
1654
|
+
<span>📦 ${shortNum(creator.packCount || 0)} packs</span>
|
|
1655
|
+
<span>⬇ ${shortNum(creator.downloads || 0)} downloads</span>
|
|
1656
|
+
<span>❤️ ${shortNum(creator.likes || 0)} likes</span>
|
|
1657
|
+
</div>
|
|
1658
|
+
${creator.topPack?.name ? html` <button type="button" onClick=${() => onOpenPack?.(creator.topPack?.pack_key)} className="mt-1 text-[11px] text-cyan-300 hover:text-cyan-200">Top pack: ${creator.topPack.name}</button> ` : null}
|
|
2081
1659
|
</div>
|
|
2082
|
-
|
|
2083
|
-
? html`
|
|
2084
|
-
<button
|
|
2085
|
-
type="button"
|
|
2086
|
-
onClick=${() => onOpenPack?.(creator.topPack?.pack_key)}
|
|
2087
|
-
className="mt-1 text-[11px] text-cyan-300 hover:text-cyan-200"
|
|
2088
|
-
>
|
|
2089
|
-
Top pack: ${creator.topPack.name}
|
|
2090
|
-
</button>
|
|
2091
|
-
`
|
|
2092
|
-
: null}
|
|
1660
|
+
<button type="button" onClick=${() => onOpenCreator?.(creator)} className="shrink-0 h-9 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">Ver perfil</button>
|
|
2093
1661
|
</div>
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
className="shrink-0 h-9 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"
|
|
2098
|
-
>
|
|
2099
|
-
Ver perfil
|
|
2100
|
-
</button>
|
|
2101
|
-
</div>
|
|
2102
|
-
</article>
|
|
2103
|
-
`)}
|
|
1662
|
+
</article>
|
|
1663
|
+
`,
|
|
1664
|
+
)}
|
|
2104
1665
|
</div>
|
|
2105
1666
|
`
|
|
2106
1667
|
: null}
|
|
@@ -2124,24 +1685,14 @@ function StickerPreview({ item, onClose, onPrev, onNext, hasNsfwAccess = true, o
|
|
|
2124
1685
|
<button type="button" className="absolute inset-0" aria-label="Fechar preview" onClick=${onClose}></button>
|
|
2125
1686
|
|
|
2126
1687
|
<div className="relative w-full max-w-xl rounded-2xl border border-slate-700 bg-slate-900 p-3">
|
|
2127
|
-
<img
|
|
2128
|
-
src=${lockedByNsfw ? NSFW_STICKER_PLACEHOLDER_URL : item.asset_url || DEFAULT_STICKER_PLACEHOLDER_URL}
|
|
2129
|
-
alt=${item.accessibility_label || 'Sticker'}
|
|
2130
|
-
className=${`w-full max-h-[70vh] object-contain rounded-xl bg-slate-950 ${lockedByNsfw ? 'blur-md' : ''}`}
|
|
2131
|
-
/>
|
|
1688
|
+
<img src=${lockedByNsfw ? NSFW_STICKER_PLACEHOLDER_URL : item.asset_url || DEFAULT_STICKER_PLACEHOLDER_URL} alt=${item.accessibility_label || 'Sticker'} className=${`w-full max-h-[70vh] object-contain rounded-xl bg-slate-950 ${lockedByNsfw ? 'blur-md' : ''}`} />
|
|
2132
1689
|
${lockedByNsfw
|
|
2133
1690
|
? html`
|
|
2134
1691
|
<div className="absolute inset-x-3 top-3 bottom-[64px] flex items-center justify-center rounded-xl bg-slate-950/40 p-4">
|
|
2135
1692
|
<div className="max-w-sm rounded-xl border border-amber-500/35 bg-slate-950/80 px-4 py-3 text-center">
|
|
2136
1693
|
<p className="text-sm font-semibold text-amber-100">Conteúdo sensível</p>
|
|
2137
1694
|
<p className="mt-1 text-xs text-slate-300">Faça login com Google para desbloquear este sticker.</p>
|
|
2138
|
-
<button
|
|
2139
|
-
type="button"
|
|
2140
|
-
onClick=${() => onRequireLogin?.()}
|
|
2141
|
-
className="mt-3 inline-flex h-9 items-center rounded-xl border border-amber-400/40 bg-amber-500/15 px-3 text-xs font-semibold text-amber-100"
|
|
2142
|
-
>
|
|
2143
|
-
Entrar e desbloquear
|
|
2144
|
-
</button>
|
|
1695
|
+
<button type="button" onClick=${() => onRequireLogin?.()} className="mt-3 inline-flex h-9 items-center rounded-xl border border-amber-400/40 bg-amber-500/15 px-3 text-xs font-semibold text-amber-100">Entrar e desbloquear</button>
|
|
2145
1696
|
</div>
|
|
2146
1697
|
</div>
|
|
2147
1698
|
`
|
|
@@ -2150,9 +1701,7 @@ function StickerPreview({ item, onClose, onPrev, onNext, hasNsfwAccess = true, o
|
|
|
2150
1701
|
<div className="mt-3 flex items-center justify-between gap-2">
|
|
2151
1702
|
<button type="button" onClick=${onPrev} className="rounded-lg border border-slate-600 px-3 py-2 text-sm text-slate-200 hover:bg-slate-800">← Anterior</button>
|
|
2152
1703
|
<div className="flex items-center gap-2">
|
|
2153
|
-
${lockedByNsfw
|
|
2154
|
-
? null
|
|
2155
|
-
: html`<button type="button" onClick=${handleCopy} className="rounded-lg border border-slate-600 px-3 py-2 text-sm text-slate-200 hover:bg-slate-800">Copiar link</button>`}
|
|
1704
|
+
${lockedByNsfw ? null : html`<button type="button" onClick=${handleCopy} className="rounded-lg border border-slate-600 px-3 py-2 text-sm text-slate-200 hover:bg-slate-800">Copiar link</button>`}
|
|
2156
1705
|
<button type="button" onClick=${onClose} className="rounded-lg border border-slate-600 px-3 py-2 text-sm text-slate-200 hover:bg-slate-800">Fechar</button>
|
|
2157
1706
|
</div>
|
|
2158
1707
|
<button type="button" onClick=${onNext} className="rounded-lg border border-slate-600 px-3 py-2 text-sm text-slate-200 hover:bg-slate-800">Próximo →</button>
|
|
@@ -2162,29 +1711,11 @@ function StickerPreview({ item, onClose, onPrev, onNext, hasNsfwAccess = true, o
|
|
|
2162
1711
|
`;
|
|
2163
1712
|
}
|
|
2164
1713
|
|
|
2165
|
-
function PackPage({
|
|
2166
|
-
pack,
|
|
2167
|
-
relatedPacks,
|
|
2168
|
-
onBack,
|
|
2169
|
-
onOpenRelated,
|
|
2170
|
-
onLike,
|
|
2171
|
-
onDislike,
|
|
2172
|
-
onTagClick,
|
|
2173
|
-
reactionLoading = '',
|
|
2174
|
-
reactionNotice = null,
|
|
2175
|
-
hasNsfwAccess = true,
|
|
2176
|
-
onRequireLogin,
|
|
2177
|
-
}) {
|
|
1714
|
+
function PackPage({ pack, relatedPacks, onBack, onOpenRelated, onLike, onDislike, onTagClick, reactionLoading = '', reactionNotice = null, hasNsfwAccess = true, onRequireLogin }) {
|
|
2178
1715
|
if (!pack) {
|
|
2179
1716
|
return html`
|
|
2180
1717
|
<section className="space-y-4">
|
|
2181
|
-
<button
|
|
2182
|
-
type="button"
|
|
2183
|
-
onClick=${onBack}
|
|
2184
|
-
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"
|
|
2185
|
-
>
|
|
2186
|
-
← Voltar para catálogo
|
|
2187
|
-
</button>
|
|
1718
|
+
<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">← Voltar para catálogo</button>
|
|
2188
1719
|
<div className="rounded-2xl border border-dashed border-slate-700 bg-slate-900/50 p-8 text-center">
|
|
2189
1720
|
<p className="text-sm font-semibold text-slate-100">Pack não encontrado</p>
|
|
2190
1721
|
<p className="mt-1 text-xs text-slate-400">Tente voltar ao catálogo e abrir novamente.</p>
|
|
@@ -2196,9 +1727,7 @@ function PackPage({
|
|
|
2196
1727
|
const items = Array.isArray(pack?.items) ? pack.items : [];
|
|
2197
1728
|
const tags = Array.isArray(pack?.tags) ? pack.tags : [];
|
|
2198
1729
|
const packLockedByNsfw = isPackMarkedNsfw(pack) && !hasNsfwAccess;
|
|
2199
|
-
const cover = packLockedByNsfw
|
|
2200
|
-
? NSFW_STICKER_PLACEHOLDER_URL
|
|
2201
|
-
: pack?.cover_url || items?.[0]?.asset_url || DEFAULT_STICKER_PLACEHOLDER_URL;
|
|
1730
|
+
const cover = packLockedByNsfw ? NSFW_STICKER_PLACEHOLDER_URL : pack?.cover_url || items?.[0]?.asset_url || DEFAULT_STICKER_PLACEHOLDER_URL;
|
|
2202
1731
|
const whatsappUrl = String(pack?.whatsapp?.url || '').trim();
|
|
2203
1732
|
const packApiPath = `/api/sticker-packs/${encodeURIComponent(String(pack?.pack_key || '').trim())}`;
|
|
2204
1733
|
const engagement = getPackEngagement(pack);
|
|
@@ -2208,35 +1737,18 @@ function PackPage({
|
|
|
2208
1737
|
|
|
2209
1738
|
return html`
|
|
2210
1739
|
<section className="space-y-4 pb-4">
|
|
2211
|
-
<button
|
|
2212
|
-
type="button"
|
|
2213
|
-
onClick=${onBack}
|
|
2214
|
-
className="inline-flex h-10 items-center gap-2 rounded-xl border border-slate-700 bg-slate-900/70 px-3 text-sm text-slate-200 hover:bg-slate-800"
|
|
2215
|
-
>
|
|
2216
|
-
← Voltar para catálogo
|
|
2217
|
-
</button>
|
|
1740
|
+
<button type="button" onClick=${onBack} className="inline-flex h-10 items-center gap-2 rounded-xl border border-slate-700 bg-slate-900/70 px-3 text-sm text-slate-200 hover:bg-slate-800">← Voltar para catálogo</button>
|
|
2218
1741
|
|
|
2219
1742
|
<article className="overflow-hidden rounded-2xl border border-slate-700 bg-slate-900/80 shadow-[0_18px_40px_rgba(2,6,23,0.25)]">
|
|
2220
1743
|
<div className="relative h-52 sm:h-64 md:h-72 bg-slate-900">
|
|
2221
|
-
<img
|
|
2222
|
-
src=${cover}
|
|
2223
|
-
alt=${`Capa ${pack?.name || 'Pack'}`}
|
|
2224
|
-
className=${`h-full w-full object-cover ${packLockedByNsfw ? 'blur-md scale-105' : ''}`}
|
|
2225
|
-
loading="lazy"
|
|
2226
|
-
/>
|
|
1744
|
+
<img src=${cover} alt=${`Capa ${pack?.name || 'Pack'}`} className=${`h-full w-full object-cover ${packLockedByNsfw ? 'blur-md scale-105' : ''}`} loading="lazy" />
|
|
2227
1745
|
<div className="absolute inset-0 bg-gradient-to-t from-slate-950 via-slate-950/55 to-transparent"></div>
|
|
2228
|
-
${packLockedByNsfw
|
|
2229
|
-
? html`<div className="absolute top-3 left-3 rounded-full border border-amber-300/40 bg-amber-500/25 px-2 py-1 text-[10px] font-semibold text-amber-100">🔞 Conteúdo sensível</div>`
|
|
2230
|
-
: null}
|
|
1746
|
+
${packLockedByNsfw ? html`<div className="absolute top-3 left-3 rounded-full border border-amber-300/40 bg-amber-500/25 px-2 py-1 text-[10px] font-semibold text-amber-100">🔞 Conteúdo sensível</div>` : null}
|
|
2231
1747
|
<div className="absolute inset-x-0 bottom-0 p-4 sm:p-5">
|
|
2232
1748
|
<div className="max-w-4xl">
|
|
2233
1749
|
<p className="text-[11px] uppercase tracking-wide text-slate-300/90">Pack público</p>
|
|
2234
|
-
<h1 className="mt-1 text-xl sm:text-2xl font-extrabold tracking-tight text-white drop-shadow-[0_2px_12px_rgba(0,0,0,0.35)]">
|
|
2235
|
-
|
|
2236
|
-
</h1>
|
|
2237
|
-
<p className="mt-1 text-xs sm:text-sm text-slate-200/90">
|
|
2238
|
-
${pack?.publisher || '-'} · ${pack?.created_at ? new Date(pack.created_at).toLocaleDateString('pt-BR') : 'sem data'}
|
|
2239
|
-
</p>
|
|
1750
|
+
<h1 className="mt-1 text-xl sm:text-2xl font-extrabold tracking-tight text-white drop-shadow-[0_2px_12px_rgba(0,0,0,0.35)]">${pack?.name || 'Pack'}</h1>
|
|
1751
|
+
<p className="mt-1 text-xs sm:text-sm text-slate-200/90">${pack?.publisher || '-'} · ${pack?.created_at ? new Date(pack.created_at).toLocaleDateString('pt-BR') : 'sem data'}</p>
|
|
2240
1752
|
</div>
|
|
2241
1753
|
</div>
|
|
2242
1754
|
</div>
|
|
@@ -2247,13 +1759,7 @@ function PackPage({
|
|
|
2247
1759
|
<div className="rounded-xl border border-amber-500/35 bg-amber-500/10 p-3">
|
|
2248
1760
|
<p className="text-sm font-semibold text-amber-100">Este pack foi marcado como sensível (+18).</p>
|
|
2249
1761
|
<p className="mt-1 text-xs text-slate-300">Faça login com Google para visualizar os stickers.</p>
|
|
2250
|
-
<button
|
|
2251
|
-
type="button"
|
|
2252
|
-
onClick=${() => onRequireLogin?.()}
|
|
2253
|
-
className="mt-3 inline-flex h-9 items-center rounded-xl border border-amber-400/35 bg-amber-500/15 px-3 text-xs font-semibold text-amber-100"
|
|
2254
|
-
>
|
|
2255
|
-
Entrar para desbloquear
|
|
2256
|
-
</button>
|
|
1762
|
+
<button type="button" onClick=${() => onRequireLogin?.()} className="mt-3 inline-flex h-9 items-center rounded-xl border border-amber-400/35 bg-amber-500/15 px-3 text-xs font-semibold text-amber-100">Entrar para desbloquear</button>
|
|
2257
1763
|
</div>
|
|
2258
1764
|
`
|
|
2259
1765
|
: null}
|
|
@@ -2264,69 +1770,29 @@ function PackPage({
|
|
|
2264
1770
|
<span className="text-slate-200">👁 <strong className="font-semibold">${shortNum(engagement.openCount)}</strong></span>
|
|
2265
1771
|
</div>
|
|
2266
1772
|
|
|
2267
|
-
${whatsappUrl && !packLockedByNsfw
|
|
2268
|
-
? html`
|
|
2269
|
-
<a
|
|
2270
|
-
href=${whatsappUrl}
|
|
2271
|
-
target="_blank"
|
|
2272
|
-
rel="noreferrer noopener"
|
|
2273
|
-
className="inline-flex h-11 w-full items-center justify-center rounded-xl bg-[#25D366] px-5 text-sm font-bold text-[#042d17] shadow-[0_8px_24px_rgba(37,211,102,0.30)] transition hover:brightness-95"
|
|
2274
|
-
>
|
|
2275
|
-
🟢 Adicionar no WhatsApp
|
|
2276
|
-
</a>
|
|
2277
|
-
`
|
|
2278
|
-
: null}
|
|
1773
|
+
${whatsappUrl && !packLockedByNsfw ? html` <a href=${whatsappUrl} target="_blank" rel="noreferrer noopener" className="inline-flex h-11 w-full items-center justify-center rounded-xl bg-[#25D366] px-5 text-sm font-bold text-[#042d17] shadow-[0_8px_24px_rgba(37,211,102,0.30)] transition hover:brightness-95"> 🟢 Adicionar no WhatsApp </a> ` : null}
|
|
2279
1774
|
|
|
2280
1775
|
<div className="flex flex-wrap items-center gap-2">
|
|
2281
|
-
<button
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
disabled=${hasReactionRequest || packLockedByNsfw}
|
|
2285
|
-
className="inline-flex h-9 items-center justify-center rounded-xl border border-emerald-500/35 bg-emerald-500/10 px-3 text-xs font-semibold text-emerald-100 transition hover:bg-emerald-500/20 disabled:cursor-not-allowed disabled:opacity-60"
|
|
2286
|
-
>
|
|
2287
|
-
${reactionLoading === 'like' ? 'Enviando...' : 'Curtir'}
|
|
2288
|
-
</button>
|
|
2289
|
-
<button
|
|
2290
|
-
type="button"
|
|
2291
|
-
onClick=${() => onDislike?.(pack?.pack_key)}
|
|
2292
|
-
disabled=${hasReactionRequest || packLockedByNsfw}
|
|
2293
|
-
className="inline-flex h-9 items-center justify-center rounded-xl border border-rose-500/35 bg-rose-500/10 px-3 text-xs font-semibold text-rose-100 transition hover:bg-rose-500/20 disabled:cursor-not-allowed disabled:opacity-60"
|
|
2294
|
-
>
|
|
2295
|
-
${reactionLoading === 'dislike' ? 'Enviando...' : 'Não curtir'}
|
|
2296
|
-
</button>
|
|
2297
|
-
${reactionNotice?.message
|
|
2298
|
-
? html`
|
|
2299
|
-
<span className=${`text-xs ${reactionNotice.type === 'error' ? 'text-rose-300' : 'text-emerald-300'}`}>
|
|
2300
|
-
${reactionNotice.message}
|
|
2301
|
-
</span>
|
|
2302
|
-
`
|
|
2303
|
-
: null}
|
|
1776
|
+
<button type="button" onClick=${() => onLike?.(pack?.pack_key)} disabled=${hasReactionRequest || packLockedByNsfw} className="inline-flex h-9 items-center justify-center rounded-xl border border-emerald-500/35 bg-emerald-500/10 px-3 text-xs font-semibold text-emerald-100 transition hover:bg-emerald-500/20 disabled:cursor-not-allowed disabled:opacity-60">${reactionLoading === 'like' ? 'Enviando...' : 'Curtir'}</button>
|
|
1777
|
+
<button type="button" onClick=${() => onDislike?.(pack?.pack_key)} disabled=${hasReactionRequest || packLockedByNsfw} className="inline-flex h-9 items-center justify-center rounded-xl border border-rose-500/35 bg-rose-500/10 px-3 text-xs font-semibold text-rose-100 transition hover:bg-rose-500/20 disabled:cursor-not-allowed disabled:opacity-60">${reactionLoading === 'dislike' ? 'Enviando...' : 'Não curtir'}</button>
|
|
1778
|
+
${reactionNotice?.message ? html` <span className=${`text-xs ${reactionNotice.type === 'error' ? 'text-rose-300' : 'text-emerald-300'}`}> ${reactionNotice.message} </span> ` : null}
|
|
2304
1779
|
</div>
|
|
2305
1780
|
|
|
2306
|
-
${pack?.description
|
|
2307
|
-
? html`<p className="text-sm leading-6 text-slate-300">${pack.description}</p>`
|
|
2308
|
-
: null}
|
|
1781
|
+
${pack?.description ? html`<p className="text-sm leading-6 text-slate-300">${pack.description}</p>` : null}
|
|
2309
1782
|
|
|
2310
1783
|
<section className="rounded-xl border border-cyan-500/25 bg-cyan-500/5 p-3">
|
|
2311
1784
|
<p className="text-[11px] uppercase tracking-wide text-cyan-200">Use este pack no seu bot</p>
|
|
2312
|
-
<p className="mt-1 text-sm text-slate-200">
|
|
2313
|
-
Este pack faz parte do módulo de stickers da plataforma OmniZap. Você pode consumir por API e conectar ao seu fluxo de automação WhatsApp.
|
|
2314
|
-
</p>
|
|
1785
|
+
<p className="mt-1 text-sm text-slate-200">Este pack faz parte do módulo de stickers da plataforma OmniZap. Você pode consumir por API e conectar ao seu fluxo de automação WhatsApp.</p>
|
|
2315
1786
|
<pre className="mt-2 overflow-auto rounded-lg border border-slate-800 bg-slate-950/60 p-2 text-[11px] text-slate-300">
|
|
2316
1787
|
GET ${packApiPath}
|
|
2317
1788
|
POST ${packApiPath}/open
|
|
2318
1789
|
POST ${packApiPath}/like
|
|
2319
|
-
</pre
|
|
1790
|
+
</pre
|
|
1791
|
+
>
|
|
2320
1792
|
<div className="mt-2 flex flex-wrap gap-2">
|
|
2321
|
-
<a href="/api-docs/" className="inline-flex h-8 items-center rounded-lg border border-cyan-500/35 bg-cyan-500/10 px-3 text-[11px] font-semibold text-cyan-100 hover:bg-cyan-500/20">
|
|
2322
|
-
|
|
2323
|
-
</a>
|
|
2324
|
-
<a href="/" className="inline-flex h-8 items-center rounded-lg border border-slate-700 bg-slate-900/70 px-3 text-[11px] text-slate-200 hover:bg-slate-800">
|
|
2325
|
-
Plataforma OmniZap
|
|
2326
|
-
</a>
|
|
2327
|
-
<a href="/stickers/" className="inline-flex h-8 items-center rounded-lg border border-slate-700 bg-slate-900/70 px-3 text-[11px] text-slate-200 hover:bg-slate-800">
|
|
2328
|
-
Voltar ao catálogo
|
|
2329
|
-
</a>
|
|
1793
|
+
<a href="/api-docs/" className="inline-flex h-8 items-center rounded-lg border border-cyan-500/35 bg-cyan-500/10 px-3 text-[11px] font-semibold text-cyan-100 hover:bg-cyan-500/20"> Ver API e exemplos </a>
|
|
1794
|
+
<a href="/" className="inline-flex h-8 items-center rounded-lg border border-slate-700 bg-slate-900/70 px-3 text-[11px] text-slate-200 hover:bg-slate-800"> Plataforma OmniZap </a>
|
|
1795
|
+
<a href="/stickers/" className="inline-flex h-8 items-center rounded-lg border border-slate-700 bg-slate-900/70 px-3 text-[11px] text-slate-200 hover:bg-slate-800"> Voltar ao catálogo </a>
|
|
2330
1796
|
</div>
|
|
2331
1797
|
</section>
|
|
2332
1798
|
|
|
@@ -2334,18 +1800,7 @@ POST ${packApiPath}/like
|
|
|
2334
1800
|
? html`
|
|
2335
1801
|
<div className="space-y-1">
|
|
2336
1802
|
<p className="text-[11px] uppercase tracking-wide text-slate-500">Tags</p>
|
|
2337
|
-
<div className="chips-scroll -mx-1 flex gap-2 overflow-x-auto px-1 pb-1">
|
|
2338
|
-
${tags.slice(0, 12).map((tag) => html`
|
|
2339
|
-
<button
|
|
2340
|
-
key=${tag}
|
|
2341
|
-
type="button"
|
|
2342
|
-
onClick=${() => onTagClick?.(tag)}
|
|
2343
|
-
className="chip-item shrink-0 rounded-full border border-slate-700 bg-slate-900/90 px-2.5 py-1 text-[11px] text-slate-200 hover:border-cyan-400/30 hover:bg-cyan-500/10"
|
|
2344
|
-
>
|
|
2345
|
-
${tagLabel(tag)}
|
|
2346
|
-
</button>
|
|
2347
|
-
`)}
|
|
2348
|
-
</div>
|
|
1803
|
+
<div className="chips-scroll -mx-1 flex gap-2 overflow-x-auto px-1 pb-1">${tags.slice(0, 12).map((tag) => html` <button key=${tag} type="button" onClick=${() => onTagClick?.(tag)} className="chip-item shrink-0 rounded-full border border-slate-700 bg-slate-900/90 px-2.5 py-1 text-[11px] text-slate-200 hover:border-cyan-400/30 hover:bg-cyan-500/10">${tagLabel(tag)}</button> `)}</div>
|
|
2349
1804
|
</div>
|
|
2350
1805
|
`
|
|
2351
1806
|
: null}
|
|
@@ -2363,57 +1818,35 @@ POST ${packApiPath}/like
|
|
|
2363
1818
|
<div className="rounded-2xl border border-dashed border-amber-500/35 bg-slate-900/55 p-8 text-center">
|
|
2364
1819
|
<p className="text-sm font-semibold text-amber-100">Stickers bloqueados por conteúdo sensível.</p>
|
|
2365
1820
|
<p className="mt-1 text-xs text-slate-300">Entre com Google para liberar a visualização deste pack.</p>
|
|
2366
|
-
<button
|
|
2367
|
-
type="button"
|
|
2368
|
-
onClick=${() => onRequireLogin?.()}
|
|
2369
|
-
className="mt-3 inline-flex h-9 items-center rounded-xl border border-amber-400/35 bg-amber-500/15 px-3 text-xs font-semibold text-amber-100"
|
|
2370
|
-
>
|
|
2371
|
-
Entrar e desbloquear
|
|
2372
|
-
</button>
|
|
1821
|
+
<button type="button" onClick=${() => onRequireLogin?.()} className="mt-3 inline-flex h-9 items-center rounded-xl border border-amber-400/35 bg-amber-500/15 px-3 text-xs font-semibold text-amber-100">Entrar e desbloquear</button>
|
|
2373
1822
|
</div>
|
|
2374
1823
|
`
|
|
2375
1824
|
: items.length
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
(item, index) => {
|
|
1825
|
+
? html`
|
|
1826
|
+
<div className="pack-stickers-grid gap-2 sm:gap-3">
|
|
1827
|
+
${items.map((item, index) => {
|
|
2380
1828
|
const stickerLockedByNsfw = isStickerMarkedNsfw(item) && !hasNsfwAccess;
|
|
2381
1829
|
return html`
|
|
2382
|
-
<button
|
|
2383
|
-
|
|
2384
|
-
type="button"
|
|
2385
|
-
onClick=${() => (stickerLockedByNsfw ? onRequireLogin?.() : setPreviewIndex(index))}
|
|
2386
|
-
className="pack-sticker-card group relative overflow-hidden rounded-xl border border-slate-800 bg-slate-900/80 text-left transition hover:-translate-y-0.5 hover:border-slate-600"
|
|
2387
|
-
>
|
|
2388
|
-
<img
|
|
2389
|
-
src=${stickerLockedByNsfw ? NSFW_STICKER_PLACEHOLDER_URL : item.asset_url || DEFAULT_STICKER_PLACEHOLDER_URL}
|
|
2390
|
-
alt=${item.accessibility_label || 'Sticker'}
|
|
2391
|
-
loading="lazy"
|
|
2392
|
-
className=${`w-full aspect-square object-contain bg-slate-950 transition-transform duration-300 ${
|
|
2393
|
-
stickerLockedByNsfw ? 'blur-md scale-105' : 'group-hover:scale-105'
|
|
2394
|
-
}`}
|
|
2395
|
-
/>
|
|
1830
|
+
<button key=${item.sticker_id || item.position || index} type="button" onClick=${() => (stickerLockedByNsfw ? onRequireLogin?.() : setPreviewIndex(index))} className="pack-sticker-card group relative overflow-hidden rounded-xl border border-slate-800 bg-slate-900/80 text-left transition hover:-translate-y-0.5 hover:border-slate-600">
|
|
1831
|
+
<img src=${stickerLockedByNsfw ? NSFW_STICKER_PLACEHOLDER_URL : item.asset_url || DEFAULT_STICKER_PLACEHOLDER_URL} alt=${item.accessibility_label || 'Sticker'} loading="lazy" className=${`w-full aspect-square object-contain bg-slate-950 transition-transform duration-300 ${stickerLockedByNsfw ? 'blur-md scale-105' : 'group-hover:scale-105'}`} />
|
|
2396
1832
|
${stickerLockedByNsfw
|
|
2397
1833
|
? html`
|
|
2398
1834
|
<div className="absolute inset-0 flex items-center justify-center bg-slate-950/35 p-2">
|
|
2399
|
-
<span className="rounded-lg border border-amber-400/35 bg-amber-500/15 px-2 py-1 text-[10px] font-semibold text-amber-100">
|
|
2400
|
-
Entrar para desbloquear
|
|
2401
|
-
</span>
|
|
1835
|
+
<span className="rounded-lg border border-amber-400/35 bg-amber-500/15 px-2 py-1 text-[10px] font-semibold text-amber-100"> Entrar para desbloquear </span>
|
|
2402
1836
|
</div>
|
|
2403
1837
|
`
|
|
2404
1838
|
: null}
|
|
2405
1839
|
</button>
|
|
2406
1840
|
`;
|
|
2407
|
-
}
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
`
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
`}
|
|
1841
|
+
})}
|
|
1842
|
+
</div>
|
|
1843
|
+
`
|
|
1844
|
+
: html`
|
|
1845
|
+
<div className="rounded-2xl border border-dashed border-slate-700 bg-slate-900/40 p-8 text-center">
|
|
1846
|
+
<p className="text-sm font-semibold text-slate-100">Este pack ainda não tem stickers visíveis.</p>
|
|
1847
|
+
<p className="mt-1 text-xs text-slate-400">Volte mais tarde ou abra outro pack do catálogo.</p>
|
|
1848
|
+
</div>
|
|
1849
|
+
`}
|
|
2417
1850
|
</section>
|
|
2418
1851
|
|
|
2419
1852
|
${relatedPacks.length
|
|
@@ -2423,27 +1856,11 @@ POST ${packApiPath}/like
|
|
|
2423
1856
|
<h2 className="text-lg font-bold">Packs relacionados</h2>
|
|
2424
1857
|
<span className="text-xs text-slate-500">${relatedPacks.length} sugestões</span>
|
|
2425
1858
|
</div>
|
|
2426
|
-
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-2.5 sm:gap-3">
|
|
2427
|
-
${relatedPacks.map(
|
|
2428
|
-
(entry, index) => html`<div key=${entry.pack_key || entry.id} className="fade-card"><${PackCard} pack=${entry} index=${index} onOpen=${onOpenRelated} hasNsfwAccess=${hasNsfwAccess} onRequireLogin=${onRequireLogin} /></div>`,
|
|
2429
|
-
)}
|
|
2430
|
-
</div>
|
|
1859
|
+
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-2.5 sm:gap-3">${relatedPacks.map((entry, index) => html`<div key=${entry.pack_key || entry.id} className="fade-card"><${PackCard} pack=${entry} index=${index} onOpen=${onOpenRelated} hasNsfwAccess=${hasNsfwAccess} onRequireLogin=${onRequireLogin} /></div>`)}</div>
|
|
2431
1860
|
</section>
|
|
2432
1861
|
`
|
|
2433
1862
|
: null}
|
|
2434
|
-
|
|
2435
|
-
${previewIndex >= 0
|
|
2436
|
-
? html`
|
|
2437
|
-
<${StickerPreview}
|
|
2438
|
-
item=${currentPreviewItem}
|
|
2439
|
-
onClose=${() => setPreviewIndex(-1)}
|
|
2440
|
-
onPrev=${() => setPreviewIndex((value) => (value <= 0 ? items.length - 1 : value - 1))}
|
|
2441
|
-
onNext=${() => setPreviewIndex((value) => (value >= items.length - 1 ? 0 : value + 1))}
|
|
2442
|
-
hasNsfwAccess=${hasNsfwAccess}
|
|
2443
|
-
onRequireLogin=${onRequireLogin}
|
|
2444
|
-
/>
|
|
2445
|
-
`
|
|
2446
|
-
: null}
|
|
1863
|
+
${previewIndex >= 0 ? html` <${StickerPreview} item=${currentPreviewItem} onClose=${() => setPreviewIndex(-1)} onPrev=${() => setPreviewIndex((value) => (value <= 0 ? items.length - 1 : value - 1))} onNext=${() => setPreviewIndex((value) => (value >= items.length - 1 ? 0 : value + 1))} hasNsfwAccess=${hasNsfwAccess} onRequireLogin=${onRequireLogin} /> ` : null}
|
|
2447
1864
|
</section>
|
|
2448
1865
|
`;
|
|
2449
1866
|
}
|
|
@@ -2571,11 +1988,7 @@ function StickersApp() {
|
|
|
2571
1988
|
|
|
2572
1989
|
if (activeCategory && !sortedTags.some((entry) => entry.value === activeCategory)) {
|
|
2573
1990
|
const meta = CATEGORY_META.get(activeCategory) || null;
|
|
2574
|
-
sortedTags.unshift(
|
|
2575
|
-
meta
|
|
2576
|
-
? { value: activeCategory, label: `${meta.icon} ${meta.label}`, icon: meta.icon }
|
|
2577
|
-
: { value: activeCategory, label: `🏷️ ${activeCategory.replace(/-/g, ' ')}`, icon: '🏷️' },
|
|
2578
|
-
);
|
|
1991
|
+
sortedTags.unshift(meta ? { value: activeCategory, label: `${meta.icon} ${meta.label}`, icon: meta.icon } : { value: activeCategory, label: `🏷️ ${activeCategory.replace(/-/g, ' ')}`, icon: '🏷️' });
|
|
2579
1992
|
}
|
|
2580
1993
|
|
|
2581
1994
|
return [{ value: '', label: '🔥 Em alta', icon: '🔥' }, ...sortedTags];
|
|
@@ -2624,9 +2037,7 @@ function StickersApp() {
|
|
|
2624
2037
|
icon: '🕘',
|
|
2625
2038
|
}));
|
|
2626
2039
|
}
|
|
2627
|
-
return tagSuggestions
|
|
2628
|
-
.filter((item) => normalizeToken(item.value).includes(q) || normalizeToken(item.label).includes(q))
|
|
2629
|
-
.slice(0, 8);
|
|
2040
|
+
return tagSuggestions.filter((item) => normalizeToken(item.value).includes(q) || normalizeToken(item.label).includes(q)).slice(0, 8);
|
|
2630
2041
|
}, [query, tagSuggestions, recentSearches]);
|
|
2631
2042
|
|
|
2632
2043
|
const sortedPacks = useMemo(() => {
|
|
@@ -2662,10 +2073,7 @@ function StickersApp() {
|
|
|
2662
2073
|
return list;
|
|
2663
2074
|
}, [packs, sortBy]);
|
|
2664
2075
|
|
|
2665
|
-
const categoryActiveLabel =
|
|
2666
|
-
catalogFilter === 'trending'
|
|
2667
|
-
? 'Em alta agora'
|
|
2668
|
-
: dynamicCategoryOptions.find((entry) => entry.value === activeCategory)?.label?.replace(/^.+?\s/, '') || 'Todas';
|
|
2076
|
+
const categoryActiveLabel = catalogFilter === 'trending' ? 'Em alta agora' : dynamicCategoryOptions.find((entry) => entry.value === activeCategory)?.label?.replace(/^.+?\s/, '') || 'Todas';
|
|
2669
2077
|
const growingNowPacks = useMemo(() => {
|
|
2670
2078
|
return [...packs]
|
|
2671
2079
|
.map((pack) => {
|
|
@@ -2779,13 +2187,11 @@ function StickersApp() {
|
|
|
2779
2187
|
const recentPublishedPacks = useMemo(
|
|
2780
2188
|
() =>
|
|
2781
2189
|
[...packs]
|
|
2782
|
-
.sort(
|
|
2783
|
-
(a, b)
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
},
|
|
2788
|
-
)
|
|
2190
|
+
.sort((a, b) => {
|
|
2191
|
+
const completenessDelta = comparePacksByCompleteness(a, b);
|
|
2192
|
+
if (completenessDelta !== 0) return completenessDelta;
|
|
2193
|
+
return new Date(b?.created_at || b?.updated_at || 0).getTime() - new Date(a?.created_at || a?.updated_at || 0).getTime();
|
|
2194
|
+
})
|
|
2789
2195
|
.slice(0, 10),
|
|
2790
2196
|
[packs],
|
|
2791
2197
|
);
|
|
@@ -2893,8 +2299,7 @@ function StickersApp() {
|
|
|
2893
2299
|
const hasGoogleLogin = Boolean(googleAuth.user?.sub);
|
|
2894
2300
|
const hasNsfwAccess = hasGoogleLogin;
|
|
2895
2301
|
const googleLoginEnabled = Boolean(googleAuthConfig.enabled && googleAuthConfig.clientId);
|
|
2896
|
-
const shouldRenderGoogleButton =
|
|
2897
|
-
isProfileView && googleLoginEnabled && !hasGoogleLogin && googleSessionChecked && !googleAuthBusy;
|
|
2302
|
+
const shouldRenderGoogleButton = isProfileView && googleLoginEnabled && !hasGoogleLogin && googleSessionChecked && !googleAuthBusy;
|
|
2898
2303
|
|
|
2899
2304
|
const fetchJson = async (url, options = undefined) => {
|
|
2900
2305
|
const opts = options && typeof options === 'object' ? { ...options } : {};
|
|
@@ -3034,10 +2439,12 @@ function StickersApp() {
|
|
|
3034
2439
|
});
|
|
3035
2440
|
};
|
|
3036
2441
|
|
|
3037
|
-
const buildManagePackApiPath = (packKey, suffix = '') =>
|
|
3038
|
-
`${config.apiBasePath}/${encodeURIComponent(String(packKey || ''))}/manage${suffix}`;
|
|
2442
|
+
const buildManagePackApiPath = (packKey, suffix = '') => `${config.apiBasePath}/${encodeURIComponent(String(packKey || ''))}/manage${suffix}`;
|
|
3039
2443
|
|
|
3040
|
-
const getManagedMutationStatus = (payload) =>
|
|
2444
|
+
const getManagedMutationStatus = (payload) =>
|
|
2445
|
+
String(payload?.data?.status || payload?.status || '')
|
|
2446
|
+
.trim()
|
|
2447
|
+
.toLowerCase();
|
|
3041
2448
|
|
|
3042
2449
|
const applyManagedPackToMyList = (managedData) => {
|
|
3043
2450
|
const pack = managedData?.pack || null;
|
|
@@ -3130,8 +2537,7 @@ function StickersApp() {
|
|
|
3130
2537
|
applyManagedPackToMyList(managed);
|
|
3131
2538
|
}
|
|
3132
2539
|
|
|
3133
|
-
const targetKey =
|
|
3134
|
-
String(managed?.pack?.pack_key || envelope?.data?.pack_key || managePackTargetKey || '').trim() || '';
|
|
2540
|
+
const targetKey = String(managed?.pack?.pack_key || envelope?.data?.pack_key || managePackTargetKey || '').trim() || '';
|
|
3135
2541
|
if (targetKey && managePackOpen && String(managePackTargetKey || '') === targetKey) {
|
|
3136
2542
|
try {
|
|
3137
2543
|
await loadManagePackData(targetKey, { openModal: true, silent: true });
|
|
@@ -3192,9 +2598,7 @@ function StickersApp() {
|
|
|
3192
2598
|
const applyPackEngagement = (packKey, engagement) => {
|
|
3193
2599
|
if (!packKey || !engagement) return;
|
|
3194
2600
|
setPacks((prev) => prev.map((entry) => (entry?.pack_key === packKey ? mergeEngagementInPack(entry, engagement) : entry)));
|
|
3195
|
-
setRelatedPacks((prev) =>
|
|
3196
|
-
prev.map((entry) => (entry?.pack_key === packKey ? mergeEngagementInPack(entry, engagement) : entry)),
|
|
3197
|
-
);
|
|
2601
|
+
setRelatedPacks((prev) => prev.map((entry) => (entry?.pack_key === packKey ? mergeEngagementInPack(entry, engagement) : entry)));
|
|
3198
2602
|
setCurrentPack((prev) => (prev?.pack_key === packKey ? mergeEngagementInPack(prev, engagement) : prev));
|
|
3199
2603
|
};
|
|
3200
2604
|
|
|
@@ -3252,7 +2656,7 @@ function StickersApp() {
|
|
|
3252
2656
|
|
|
3253
2657
|
setPacks((prev) => (reset ? data : prev.concat(data)));
|
|
3254
2658
|
setPackHasMore(hasMore);
|
|
3255
|
-
setPackOffset(Number.isFinite(next) ? next :
|
|
2659
|
+
setPackOffset(Number.isFinite(next) ? next : reset ? data.length : nextOffset + data.length);
|
|
3256
2660
|
} catch (err) {
|
|
3257
2661
|
setError(err?.message || 'Falha ao carregar packs');
|
|
3258
2662
|
if (reset) setPacks([]);
|
|
@@ -3363,9 +2767,7 @@ function StickersApp() {
|
|
|
3363
2767
|
else if (Array.isArray(pack?.tags) && pack.tags[0]) relatedParams.set('categories', pack.tags[0]);
|
|
3364
2768
|
|
|
3365
2769
|
const relatedPayload = await fetchJson(`${config.apiBasePath}?${relatedParams.toString()}`);
|
|
3366
|
-
const relatedList = (Array.isArray(relatedPayload?.data) ? relatedPayload.data : [])
|
|
3367
|
-
.filter((entry) => entry.pack_key && entry.pack_key !== pack?.pack_key)
|
|
3368
|
-
.slice(0, 8);
|
|
2770
|
+
const relatedList = (Array.isArray(relatedPayload?.data) ? relatedPayload.data : []).filter((entry) => entry.pack_key && entry.pack_key !== pack?.pack_key).slice(0, 8);
|
|
3369
2771
|
setRelatedPacks(relatedList);
|
|
3370
2772
|
};
|
|
3371
2773
|
|
|
@@ -3414,14 +2816,14 @@ function StickersApp() {
|
|
|
3414
2816
|
}
|
|
3415
2817
|
};
|
|
3416
2818
|
|
|
3417
|
-
const buildCatalogWebUrl = ({
|
|
3418
|
-
q = appliedQuery,
|
|
3419
|
-
category = activeCategory,
|
|
3420
|
-
sort = sortBy,
|
|
3421
|
-
filter = catalogFilter,
|
|
3422
|
-
} = {}) => {
|
|
2819
|
+
const buildCatalogWebUrl = ({ q = appliedQuery, category = activeCategory, sort = sortBy, filter = catalogFilter } = {}) => {
|
|
3423
2820
|
const params = new URLSearchParams();
|
|
3424
|
-
const normalizedFilter =
|
|
2821
|
+
const normalizedFilter =
|
|
2822
|
+
String(filter || '')
|
|
2823
|
+
.trim()
|
|
2824
|
+
.toLowerCase() === 'trending'
|
|
2825
|
+
? 'trending'
|
|
2826
|
+
: '';
|
|
3425
2827
|
const normalizedSort = normalizeCatalogSort(sort || DEFAULT_CATALOG_SORT);
|
|
3426
2828
|
if (normalizedFilter === 'trending') {
|
|
3427
2829
|
params.set('filter', 'trending');
|
|
@@ -3441,10 +2843,19 @@ function StickersApp() {
|
|
|
3441
2843
|
};
|
|
3442
2844
|
|
|
3443
2845
|
const applyCatalogViewState = ({ q = '', category = '', sort = DEFAULT_CATALOG_SORT, filter = '' } = {}) => {
|
|
3444
|
-
const normalizedFilter =
|
|
2846
|
+
const normalizedFilter =
|
|
2847
|
+
String(filter || '')
|
|
2848
|
+
.trim()
|
|
2849
|
+
.toLowerCase() === 'trending'
|
|
2850
|
+
? 'trending'
|
|
2851
|
+
: '';
|
|
3445
2852
|
const normalizedSort = normalizeCatalogSort(normalizedFilter ? 'trending' : sort || DEFAULT_CATALOG_SORT);
|
|
3446
2853
|
const nextQ = normalizedFilter ? '' : String(q || '').trim();
|
|
3447
|
-
const nextCategory = normalizedFilter
|
|
2854
|
+
const nextCategory = normalizedFilter
|
|
2855
|
+
? ''
|
|
2856
|
+
: String(category || '')
|
|
2857
|
+
.trim()
|
|
2858
|
+
.toLowerCase();
|
|
3448
2859
|
|
|
3449
2860
|
setCatalogFilter(normalizedFilter);
|
|
3450
2861
|
setSortBy(normalizedSort);
|
|
@@ -3475,7 +2886,12 @@ function StickersApp() {
|
|
|
3475
2886
|
q,
|
|
3476
2887
|
category,
|
|
3477
2888
|
sort: normalizeCatalogSort(sort || DEFAULT_CATALOG_SORT),
|
|
3478
|
-
filter:
|
|
2889
|
+
filter:
|
|
2890
|
+
String(filter || '')
|
|
2891
|
+
.trim()
|
|
2892
|
+
.toLowerCase() === 'trending'
|
|
2893
|
+
? 'trending'
|
|
2894
|
+
: '',
|
|
3479
2895
|
};
|
|
3480
2896
|
if (push) window.history.pushState({}, '', buildCatalogWebUrl(nextState));
|
|
3481
2897
|
applyCatalogViewState(nextState);
|
|
@@ -3518,7 +2934,9 @@ function StickersApp() {
|
|
|
3518
2934
|
};
|
|
3519
2935
|
|
|
3520
2936
|
const openCatalogTagFilter = (tag) => {
|
|
3521
|
-
const nextTag = String(tag || '')
|
|
2937
|
+
const nextTag = String(tag || '')
|
|
2938
|
+
.trim()
|
|
2939
|
+
.toLowerCase();
|
|
3522
2940
|
if (!nextTag) return;
|
|
3523
2941
|
openCatalogWithState({
|
|
3524
2942
|
q: '',
|
|
@@ -3834,7 +3252,9 @@ function StickersApp() {
|
|
|
3834
3252
|
};
|
|
3835
3253
|
|
|
3836
3254
|
const handleCategoryChipPress = (value) => {
|
|
3837
|
-
const nextValue = String(value || '')
|
|
3255
|
+
const nextValue = String(value || '')
|
|
3256
|
+
.trim()
|
|
3257
|
+
.toLowerCase();
|
|
3838
3258
|
const previousScrollY = window.scrollY || 0;
|
|
3839
3259
|
|
|
3840
3260
|
if (!nextValue) {
|
|
@@ -3871,9 +3291,7 @@ function StickersApp() {
|
|
|
3871
3291
|
like_count: safeNumber(sourcePack?.engagement?.like_count),
|
|
3872
3292
|
dislike_count: safeNumber(sourcePack?.engagement?.dislike_count),
|
|
3873
3293
|
comment_count: safeNumber(sourcePack?.engagement?.comment_count),
|
|
3874
|
-
score:
|
|
3875
|
-
safeNumber(sourcePack?.engagement?.score) ||
|
|
3876
|
-
safeNumber(sourcePack?.engagement?.like_count) - safeNumber(sourcePack?.engagement?.dislike_count),
|
|
3294
|
+
score: safeNumber(sourcePack?.engagement?.score) || safeNumber(sourcePack?.engagement?.like_count) - safeNumber(sourcePack?.engagement?.dislike_count),
|
|
3877
3295
|
updated_at: sourcePack?.engagement?.updated_at || null,
|
|
3878
3296
|
};
|
|
3879
3297
|
const optimistic = {
|
|
@@ -3973,7 +3391,12 @@ function StickersApp() {
|
|
|
3973
3391
|
const raw = localStorage.getItem(SEARCH_HISTORY_KEY);
|
|
3974
3392
|
const parsed = JSON.parse(raw || '[]');
|
|
3975
3393
|
if (Array.isArray(parsed)) {
|
|
3976
|
-
setRecentSearches(
|
|
3394
|
+
setRecentSearches(
|
|
3395
|
+
parsed
|
|
3396
|
+
.map((entry) => String(entry || '').trim())
|
|
3397
|
+
.filter(Boolean)
|
|
3398
|
+
.slice(0, 8),
|
|
3399
|
+
);
|
|
3977
3400
|
}
|
|
3978
3401
|
} catch {}
|
|
3979
3402
|
}, []);
|
|
@@ -4262,9 +3685,7 @@ function StickersApp() {
|
|
|
4262
3685
|
}`}
|
|
4263
3686
|
</style>
|
|
4264
3687
|
|
|
4265
|
-
<header className=${`sticky top-0 z-30 border-b border-slate-800 bg-slate-950/95 backdrop-blur transition-shadow ${
|
|
4266
|
-
isScrolled ? 'shadow-[0_8px_24px_rgba(2,6,23,0.45)]' : ''
|
|
4267
|
-
}`}>
|
|
3688
|
+
<header className=${`sticky top-0 z-30 border-b border-slate-800 bg-slate-950/95 backdrop-blur transition-shadow ${isScrolled ? 'shadow-[0_8px_24px_rgba(2,6,23,0.45)]' : ''}`}>
|
|
4268
3689
|
<div className="max-w-7xl mx-auto h-14 px-3 flex items-center gap-2.5">
|
|
4269
3690
|
<a href="/" className="shrink-0 flex items-center gap-2">
|
|
4270
3691
|
<img src="https://iili.io/FC3FABe.jpg" alt="OmniZap" className="w-7 h-7 rounded-full border border-slate-700" />
|
|
@@ -4274,10 +3695,10 @@ function StickersApp() {
|
|
|
4274
3695
|
${currentView === 'catalog'
|
|
4275
3696
|
? html`
|
|
4276
3697
|
<form onSubmit=${onSubmit} className="flex-1 relative">
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
3698
|
+
<span className="absolute left-2.5 top-1/2 -translate-y-1/2 text-xs text-slate-400">🔎</span>
|
|
3699
|
+
<input
|
|
3700
|
+
type="search"
|
|
3701
|
+
value=${query}
|
|
4281
3702
|
onChange=${(e) => setQuery(e.target.value)}
|
|
4282
3703
|
onFocus=${() => setShowAutocomplete(true)}
|
|
4283
3704
|
onBlur=${() => setTimeout(() => setShowAutocomplete(false), 120)}
|
|
@@ -4286,20 +3707,15 @@ function StickersApp() {
|
|
|
4286
3707
|
setShowAutocomplete(false);
|
|
4287
3708
|
}
|
|
4288
3709
|
}}
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
3710
|
+
placeholder="Buscar packs..."
|
|
3711
|
+
className="w-full h-9 sm:h-10 rounded-2xl border border-slate-800 bg-slate-900 pl-[34px] sm:pl-9 pr-3 text-sm text-slate-100 placeholder:text-slate-500 outline-none transition focus:border-emerald-400/50 focus:ring-2 focus:ring-emerald-400/15"
|
|
3712
|
+
/>
|
|
4292
3713
|
${showAutocomplete && filteredSuggestions.length
|
|
4293
3714
|
? html`
|
|
4294
3715
|
<div className="absolute z-40 mt-2 w-full rounded-2xl border border-slate-800 bg-slate-900 shadow-xl overflow-hidden">
|
|
4295
3716
|
${filteredSuggestions.map(
|
|
4296
3717
|
(item) => html`
|
|
4297
|
-
<button
|
|
4298
|
-
key=${item.value}
|
|
4299
|
-
type="button"
|
|
4300
|
-
onClick=${() => applySuggestion(item)}
|
|
4301
|
-
className="w-full px-3 py-2.5 text-left text-sm text-slate-200 hover:bg-slate-800 flex items-center justify-between gap-2 border-b border-slate-800 last:border-b-0"
|
|
4302
|
-
>
|
|
3718
|
+
<button key=${item.value} type="button" onClick=${() => applySuggestion(item)} className="w-full px-3 py-2.5 text-left text-sm text-slate-200 hover:bg-slate-800 flex items-center justify-between gap-2 border-b border-slate-800 last:border-b-0">
|
|
4303
3719
|
<span className="inline-flex items-center gap-2">
|
|
4304
3720
|
<span>${item.icon || '🏷'}</span>
|
|
4305
3721
|
<span className="truncate">${item.label}</span>
|
|
@@ -4316,489 +3732,216 @@ function StickersApp() {
|
|
|
4316
3732
|
: html`<div className="flex-1"></div>`}
|
|
4317
3733
|
|
|
4318
3734
|
<div className="flex items-center gap-2">
|
|
4319
|
-
<button
|
|
4320
|
-
type="button"
|
|
4321
|
-
className=${`text-xs rounded-lg border px-3 py-2 ${
|
|
4322
|
-
isProfileView
|
|
4323
|
-
? 'border-amber-400/40 bg-amber-400/10 text-amber-200'
|
|
4324
|
-
: 'border-amber-500/30 bg-amber-500/5 text-amber-200 hover:bg-amber-500/10'
|
|
4325
|
-
}`}
|
|
4326
|
-
onClick=${() => openProfile(true)}
|
|
4327
|
-
title="Meu perfil e packs"
|
|
4328
|
-
>
|
|
3735
|
+
<button type="button" className=${`text-xs rounded-lg border px-3 py-2 ${isProfileView ? 'border-amber-400/40 bg-amber-400/10 text-amber-200' : 'border-amber-500/30 bg-amber-500/5 text-amber-200 hover:bg-amber-500/10'}`} onClick=${() => openProfile(true)} title="Meu perfil e packs">
|
|
4329
3736
|
<span className="sm:hidden">👤</span>
|
|
4330
3737
|
<span className="hidden sm:inline">Meus Packs</span>
|
|
4331
3738
|
</button>
|
|
4332
|
-
<a
|
|
4333
|
-
className="text-xs rounded-lg border border-cyan-500/40 bg-cyan-500/10 px-3 py-2 text-cyan-200 hover:bg-cyan-500/20"
|
|
4334
|
-
href="/stickers/create/"
|
|
4335
|
-
title="Criar pack"
|
|
4336
|
-
>
|
|
3739
|
+
<a className="text-xs rounded-lg border border-cyan-500/40 bg-cyan-500/10 px-3 py-2 text-cyan-200 hover:bg-cyan-500/20" href="/stickers/create/" title="Criar pack">
|
|
4337
3740
|
<span className="sm:hidden">➕</span>
|
|
4338
3741
|
<span className="hidden sm:inline">✨ Criar pack agora</span>
|
|
4339
3742
|
</a>
|
|
4340
3743
|
${supportInfo?.url
|
|
4341
3744
|
? html`
|
|
4342
|
-
<a
|
|
4343
|
-
className="text-xs rounded-lg border border-emerald-500/40 bg-emerald-500/10 px-3 py-2 text-emerald-200 hover:bg-emerald-500/20"
|
|
4344
|
-
href=${supportInfo.url}
|
|
4345
|
-
target="_blank"
|
|
4346
|
-
rel="noreferrer noopener"
|
|
4347
|
-
title="Suporte no WhatsApp"
|
|
4348
|
-
>
|
|
3745
|
+
<a className="text-xs rounded-lg border border-emerald-500/40 bg-emerald-500/10 px-3 py-2 text-emerald-200 hover:bg-emerald-500/20" href=${supportInfo.url} target="_blank" rel="noreferrer noopener" title="Suporte no WhatsApp">
|
|
4349
3746
|
<span className="sm:hidden">💬</span>
|
|
4350
3747
|
<span className="hidden sm:inline">Suporte</span>
|
|
4351
3748
|
</a>
|
|
4352
3749
|
`
|
|
4353
3750
|
: null}
|
|
4354
3751
|
<div className="hidden sm:flex items-center gap-2">
|
|
4355
|
-
|
|
4356
|
-
|
|
3752
|
+
<a className="text-xs rounded-lg border border-slate-700 px-3 py-2 text-slate-300 hover:bg-slate-800" href="/api-docs/">API</a>
|
|
3753
|
+
<a className="text-xs rounded-lg border border-slate-700 px-3 py-2 text-slate-300 hover:bg-slate-800" href="https://github.com/Kaikygr/omnizap-system" target="_blank" rel="noreferrer noopener">GitHub</a>
|
|
4357
3754
|
</div>
|
|
4358
3755
|
</div>
|
|
4359
3756
|
</div>
|
|
4360
3757
|
</header>
|
|
4361
3758
|
|
|
4362
3759
|
<main className="max-w-7xl mx-auto px-3 py-2.5 sm:py-3 space-y-3 pb-[calc(1rem+env(safe-area-inset-bottom))]">
|
|
4363
|
-
${error
|
|
4364
|
-
? html`<div className="rounded-2xl border border-red-500/40 bg-red-500/10 px-4 py-3 text-sm text-red-200">${error}</div>`
|
|
4365
|
-
: null}
|
|
4366
|
-
|
|
3760
|
+
${error ? html`<div className="rounded-2xl border border-red-500/40 bg-red-500/10 px-4 py-3 text-sm text-red-200">${error}</div>` : null}
|
|
4367
3761
|
${isProfileView
|
|
4368
|
-
? html`
|
|
4369
|
-
<${CreatorProfileDashboard}
|
|
4370
|
-
googleAuthConfig=${googleAuthConfig}
|
|
4371
|
-
googleAuth=${googleAuth}
|
|
4372
|
-
googleAuthBusy=${googleAuthBusy}
|
|
4373
|
-
googleAuthError=${googleAuthError}
|
|
4374
|
-
googleSessionChecked=${googleSessionChecked}
|
|
4375
|
-
myPacks=${myPacks}
|
|
4376
|
-
myPacksLoading=${myPacksLoading}
|
|
4377
|
-
myPacksError=${myPacksError}
|
|
4378
|
-
myProfileStats=${myProfileStats}
|
|
4379
|
-
onBack=${goCatalog}
|
|
4380
|
-
onRefresh=${() => refreshMyProfile()}
|
|
4381
|
-
onLogout=${handleGoogleLogout}
|
|
4382
|
-
onOpenPublicPack=${openPack}
|
|
4383
|
-
onOpenPackActions=${openPackActionsSheet}
|
|
4384
|
-
onOpenManagePack=${(pack) => openManagePackByKey(pack?.pack_key || '')}
|
|
4385
|
-
onRequestDeletePack=${requestDeletePack}
|
|
4386
|
-
packActionBusyByKey=${packActionBusyByKey}
|
|
4387
|
-
/>
|
|
4388
|
-
`
|
|
3762
|
+
? html` <${CreatorProfileDashboard} googleAuthConfig=${googleAuthConfig} googleAuth=${googleAuth} googleAuthBusy=${googleAuthBusy} googleAuthError=${googleAuthError} googleSessionChecked=${googleSessionChecked} myPacks=${myPacks} myPacksLoading=${myPacksLoading} myPacksError=${myPacksError} myProfileStats=${myProfileStats} onBack=${goCatalog} onRefresh=${() => refreshMyProfile()} onLogout=${handleGoogleLogout} onOpenPublicPack=${openPack} onOpenPackActions=${openPackActionsSheet} onOpenManagePack=${(pack) => openManagePackByKey(pack?.pack_key || '')} onRequestDeletePack=${requestDeletePack} packActionBusyByKey=${packActionBusyByKey} /> `
|
|
4389
3763
|
: isCreatorsView
|
|
4390
|
-
? html`
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
/>
|
|
4402
|
-
`
|
|
4403
|
-
: currentPackKey
|
|
4404
|
-
? html`
|
|
4405
|
-
${packLoading
|
|
4406
|
-
? html`<${PackPageSkeleton} />`
|
|
4407
|
-
: html`<${PackPage}
|
|
4408
|
-
pack=${currentPack}
|
|
4409
|
-
relatedPacks=${relatedPacks}
|
|
4410
|
-
onBack=${goCatalog}
|
|
4411
|
-
onOpenRelated=${openPack}
|
|
4412
|
-
onLike=${handleLike}
|
|
4413
|
-
onDislike=${handleDislike}
|
|
4414
|
-
onTagClick=${openCatalogTagFilter}
|
|
4415
|
-
reactionLoading=${reactionLoading}
|
|
4416
|
-
reactionNotice=${reactionNotice}
|
|
4417
|
-
hasNsfwAccess=${hasNsfwAccess}
|
|
4418
|
-
onRequireLogin=${requestNsfwUnlock}
|
|
4419
|
-
/>`}
|
|
4420
|
-
`
|
|
4421
|
-
: html`
|
|
4422
|
-
<section className="rounded-2xl border border-slate-800 bg-slate-900/80 p-3 sm:p-4">
|
|
4423
|
-
<p className="text-[11px] uppercase tracking-wide text-slate-400">Módulo de stickers da plataforma OmniZap</p>
|
|
4424
|
-
<h1 className="mt-1 text-lg sm:text-xl font-extrabold tracking-tight text-slate-100">
|
|
4425
|
-
Catálogo de stickers integrável via API
|
|
4426
|
-
</h1>
|
|
4427
|
-
<p className="mt-1 text-sm text-slate-300">
|
|
4428
|
-
Os stickers são uma feature do OmniZap para bots e automação WhatsApp. Explore packs públicos e integre no seu sistema com endpoints documentados.
|
|
4429
|
-
</p>
|
|
4430
|
-
<div className="mt-2 flex flex-wrap gap-2">
|
|
4431
|
-
<a href="/api-docs/" className="inline-flex h-8 items-center rounded-lg border border-cyan-500/35 bg-cyan-500/10 px-3 text-[11px] font-semibold text-cyan-100 hover:bg-cyan-500/20">
|
|
4432
|
-
Área de Desenvolvedor
|
|
4433
|
-
</a>
|
|
4434
|
-
<a href="/" className="inline-flex h-8 items-center rounded-lg border border-slate-700 bg-slate-900/70 px-3 text-[11px] text-slate-200 hover:bg-slate-800">
|
|
4435
|
-
Página principal OmniZap
|
|
4436
|
-
</a>
|
|
4437
|
-
</div>
|
|
4438
|
-
</section>
|
|
4439
|
-
|
|
4440
|
-
<div className="lg:grid lg:grid-cols-[220px_minmax(0,1fr)] lg:gap-4">
|
|
4441
|
-
<aside className="hidden lg:block">
|
|
4442
|
-
<div className="sticky top-[72px] space-y-2.5 rounded-2xl border border-slate-800 bg-slate-900/80 p-2.5">
|
|
4443
|
-
<div className="rounded-xl border border-slate-800 bg-slate-950/40 p-2">
|
|
4444
|
-
<div className="flex items-center justify-between gap-2">
|
|
4445
|
-
<h3 className="text-xs font-semibold uppercase tracking-wide text-slate-300">Filtros</h3>
|
|
4446
|
-
<button
|
|
4447
|
-
type="button"
|
|
4448
|
-
onClick=${clearFilters}
|
|
4449
|
-
className="h-8 rounded-lg border border-slate-700 px-2 text-[11px] text-slate-200 hover:bg-slate-800"
|
|
4450
|
-
>
|
|
4451
|
-
Limpar
|
|
4452
|
-
</button>
|
|
4453
|
-
</div>
|
|
4454
|
-
<p className="mt-1 text-[11px] text-slate-500">${packs.length}${packHasMore ? '+' : ''} packs · ${orphans.length} sem pack</p>
|
|
4455
|
-
</div>
|
|
4456
|
-
|
|
4457
|
-
<details open className="rounded-xl border border-slate-800 bg-slate-950/40 p-2">
|
|
4458
|
-
<summary className="cursor-pointer list-none text-xs font-semibold text-slate-200">
|
|
4459
|
-
Ordenar catálogo
|
|
4460
|
-
</summary>
|
|
4461
|
-
<div className="mt-2 space-y-1.5">
|
|
4462
|
-
${CATALOG_SORT_OPTIONS.map((option) => html`
|
|
4463
|
-
<button
|
|
4464
|
-
key=${option.value}
|
|
4465
|
-
type="button"
|
|
4466
|
-
onClick=${() => handleCatalogSortSelection(option.value)}
|
|
4467
|
-
disabled=${sortPickerBusy}
|
|
4468
|
-
className=${`w-full h-9 rounded-xl border text-xs disabled:opacity-60 ${
|
|
4469
|
-
normalizeCatalogSort(sortBy) === option.value
|
|
4470
|
-
? 'border-emerald-400 bg-emerald-400/10 text-emerald-200'
|
|
4471
|
-
: 'border-slate-700 text-slate-300 hover:bg-slate-800'
|
|
4472
|
-
}`}
|
|
4473
|
-
>
|
|
4474
|
-
${option.icon} ${option.label}
|
|
4475
|
-
</button>
|
|
4476
|
-
`)}
|
|
4477
|
-
</div>
|
|
4478
|
-
</details>
|
|
4479
|
-
|
|
4480
|
-
${supportInfo?.url
|
|
4481
|
-
? html`
|
|
4482
|
-
<a
|
|
4483
|
-
href=${supportInfo.url}
|
|
4484
|
-
target="_blank"
|
|
4485
|
-
rel="noreferrer noopener"
|
|
4486
|
-
className="w-full h-9 inline-flex items-center justify-center rounded-xl border border-emerald-500/35 bg-emerald-500/10 text-xs text-emerald-200 hover:bg-emerald-500/20"
|
|
4487
|
-
>
|
|
4488
|
-
💬 Suporte no WhatsApp
|
|
4489
|
-
</a>
|
|
4490
|
-
`
|
|
4491
|
-
: null}
|
|
4492
|
-
</div>
|
|
4493
|
-
</aside>
|
|
4494
|
-
|
|
4495
|
-
<div className="space-y-3 min-w-0">
|
|
4496
|
-
<section className="space-y-2 min-w-0">
|
|
4497
|
-
<div className="relative min-w-0">
|
|
4498
|
-
<div className="absolute left-0 top-0 bottom-0 w-5 bg-gradient-to-r from-slate-950 to-transparent pointer-events-none z-10"></div>
|
|
4499
|
-
<div className="absolute right-0 top-0 bottom-0 w-5 bg-gradient-to-l from-slate-950 to-transparent pointer-events-none z-10"></div>
|
|
4500
|
-
<div className="chips-scroll flex max-w-full gap-1.5 overflow-x-auto pb-1 pr-1">
|
|
4501
|
-
${dynamicCategoryOptions.map(
|
|
4502
|
-
(item) => html`
|
|
4503
|
-
<button
|
|
4504
|
-
key=${item.value || 'all'}
|
|
4505
|
-
type="button"
|
|
4506
|
-
onClick=${() => handleCategoryChipPress(item.value)}
|
|
4507
|
-
className=${`chip-item h-8 whitespace-nowrap rounded-full px-3 text-[11px] border transition ${
|
|
4508
|
-
activeCategory === item.value
|
|
4509
|
-
? 'bg-emerald-400 text-slate-900 border-emerald-300 font-semibold shadow-[0_0_0_2px_rgba(16,185,129,0.18)]'
|
|
4510
|
-
: 'bg-slate-900 text-slate-300 border-slate-800 hover:bg-slate-800'
|
|
4511
|
-
}`}
|
|
4512
|
-
>
|
|
4513
|
-
${item.label}
|
|
4514
|
-
</button>
|
|
4515
|
-
`,
|
|
4516
|
-
)}
|
|
4517
|
-
</div>
|
|
3764
|
+
? html` <${CreatorsRankingPage} creators=${sortedCreatorRanking} loading=${creatorRankingLoading} error=${creatorRankingError} sort=${creatorSort} onSortChange=${handleCreatorsSortChange} onBack=${goCatalog} onRetry=${loadCreatorRanking} onOpenCreator=${openCreatorProfileFromRanking} onOpenPack=${openPack} /> `
|
|
3765
|
+
: currentPackKey
|
|
3766
|
+
? html` ${packLoading ? html`<${PackPageSkeleton} />` : html`<${PackPage} pack=${currentPack} relatedPacks=${relatedPacks} onBack=${goCatalog} onOpenRelated=${openPack} onLike=${handleLike} onDislike=${handleDislike} onTagClick=${openCatalogTagFilter} reactionLoading=${reactionLoading} reactionNotice=${reactionNotice} hasNsfwAccess=${hasNsfwAccess} onRequireLogin=${requestNsfwUnlock} />`} `
|
|
3767
|
+
: html`
|
|
3768
|
+
<section className="rounded-2xl border border-slate-800 bg-slate-900/80 p-3 sm:p-4">
|
|
3769
|
+
<p className="text-[11px] uppercase tracking-wide text-slate-400">Módulo de stickers da plataforma OmniZap</p>
|
|
3770
|
+
<h1 className="mt-1 text-lg sm:text-xl font-extrabold tracking-tight text-slate-100">Catálogo de stickers integrável via API</h1>
|
|
3771
|
+
<p className="mt-1 text-sm text-slate-300">Os stickers são uma feature do OmniZap para bots e automação WhatsApp. Explore packs públicos e integre no seu sistema com endpoints documentados.</p>
|
|
3772
|
+
<div className="mt-2 flex flex-wrap gap-2">
|
|
3773
|
+
<a href="/api-docs/" className="inline-flex h-8 items-center rounded-lg border border-cyan-500/35 bg-cyan-500/10 px-3 text-[11px] font-semibold text-cyan-100 hover:bg-cyan-500/20"> Área de Desenvolvedor </a>
|
|
3774
|
+
<a href="/" className="inline-flex h-8 items-center rounded-lg border border-slate-700 bg-slate-900/70 px-3 text-[11px] text-slate-200 hover:bg-slate-800"> Página principal OmniZap </a>
|
|
4518
3775
|
</div>
|
|
4519
3776
|
</section>
|
|
4520
3777
|
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
</p>
|
|
4532
|
-
</div>
|
|
4533
|
-
<div className="flex items-center gap-2">
|
|
4534
|
-
${globalMarketplaceStatsLoading && !globalMarketplaceStats
|
|
4535
|
-
? html`<span className="inline-flex h-8 items-center rounded-lg border border-slate-700 bg-slate-900/60 px-3 text-[11px] text-slate-300">Carregando métricas...</span>`
|
|
4536
|
-
: null}
|
|
4537
|
-
${globalMarketplaceStatsError
|
|
4538
|
-
? html`<span className="inline-flex h-8 items-center rounded-lg border border-amber-500/30 bg-amber-500/10 px-3 text-[11px] text-amber-100">Fallback local</span>`
|
|
4539
|
-
: null}
|
|
4540
|
-
<a href="/stickers/create/" className="inline-flex h-8 items-center rounded-lg border border-emerald-500/35 bg-emerald-500/10 px-3 text-[11px] font-semibold text-emerald-200 hover:bg-emerald-500/20">
|
|
4541
|
-
✨ Criar pack agora
|
|
4542
|
-
</a>
|
|
4543
|
-
</div>
|
|
4544
|
-
</div>
|
|
3778
|
+
<div className="lg:grid lg:grid-cols-[220px_minmax(0,1fr)] lg:gap-4">
|
|
3779
|
+
<aside className="hidden lg:block">
|
|
3780
|
+
<div className="sticky top-[72px] space-y-2.5 rounded-2xl border border-slate-800 bg-slate-900/80 p-2.5">
|
|
3781
|
+
<div className="rounded-xl border border-slate-800 bg-slate-950/40 p-2">
|
|
3782
|
+
<div className="flex items-center justify-between gap-2">
|
|
3783
|
+
<h3 className="text-xs font-semibold uppercase tracking-wide text-slate-300">Filtros</h3>
|
|
3784
|
+
<button type="button" onClick=${clearFilters} className="h-8 rounded-lg border border-slate-700 px-2 text-[11px] text-slate-200 hover:bg-slate-800">Limpar</button>
|
|
3785
|
+
</div>
|
|
3786
|
+
<p className="mt-1 text-[11px] text-slate-500">${packs.length}${packHasMore ? '+' : ''} packs · ${orphans.length} sem pack</p>
|
|
3787
|
+
</div>
|
|
4545
3788
|
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
{ key: 'creators', label: '⭐ Criadores' },
|
|
4551
|
-
].map((tab) => html`
|
|
4552
|
-
<button
|
|
4553
|
-
key=${tab.key}
|
|
4554
|
-
type="button"
|
|
4555
|
-
onClick=${() => setDiscoverTab(tab.key)}
|
|
4556
|
-
className=${`h-8 rounded-full border px-2.5 text-[11px] touch-manipulation ${
|
|
4557
|
-
discoverTab === tab.key
|
|
4558
|
-
? 'border-cyan-400/35 bg-cyan-500/10 text-cyan-100'
|
|
4559
|
-
: 'border-slate-700 bg-slate-950/40 text-slate-300 hover:bg-slate-800'
|
|
4560
|
-
}`}
|
|
4561
|
-
>
|
|
4562
|
-
${tab.label}
|
|
4563
|
-
</button>
|
|
4564
|
-
`)}
|
|
4565
|
-
</div>
|
|
3789
|
+
<details open className="rounded-xl border border-slate-800 bg-slate-950/40 p-2">
|
|
3790
|
+
<summary className="cursor-pointer list-none text-xs font-semibold text-slate-200">Ordenar catálogo</summary>
|
|
3791
|
+
<div className="mt-2 space-y-1.5">${CATALOG_SORT_OPTIONS.map((option) => html` <button key=${option.value} type="button" onClick=${() => handleCatalogSortSelection(option.value)} disabled=${sortPickerBusy} className=${`w-full h-9 rounded-xl border text-xs disabled:opacity-60 ${normalizeCatalogSort(sortBy) === option.value ? 'border-emerald-400 bg-emerald-400/10 text-emerald-200' : 'border-slate-700 text-slate-300 hover:bg-slate-800'}`}>${option.icon} ${option.label}</button> `)}</div>
|
|
3792
|
+
</details>
|
|
4566
3793
|
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
)}
|
|
4580
|
-
</div>
|
|
4581
|
-
`
|
|
4582
|
-
: discoverTab === 'top'
|
|
4583
|
-
? html`
|
|
4584
|
-
<div className="grid grid-cols-1 xl:grid-cols-2 gap-2">
|
|
4585
|
-
${topWeekPacks.slice(0, 8).map(
|
|
4586
|
-
(entry, idx) => html`<${DiscoverPackRowItem}
|
|
4587
|
-
key=${`top-${entry.pack_key}`}
|
|
4588
|
-
pack=${entry}
|
|
4589
|
-
onOpen=${openPack}
|
|
4590
|
-
rank=${idx + 1}
|
|
4591
|
-
hasNsfwAccess=${hasNsfwAccess}
|
|
4592
|
-
onRequireLogin=${requestNsfwUnlock}
|
|
4593
|
-
/>`,
|
|
4594
|
-
)}
|
|
4595
|
-
</div>
|
|
4596
|
-
`
|
|
4597
|
-
: html`
|
|
4598
|
-
<div className="grid grid-cols-1 xl:grid-cols-2 gap-2">
|
|
4599
|
-
${featuredCreators.map((creator) => html`
|
|
4600
|
-
<button
|
|
4601
|
-
key=${creator.publisher}
|
|
4602
|
-
onClick=${() => openCatalogWithState({ q: creator.publisher, category: '', sort: DEFAULT_CATALOG_SORT, filter: '', push: true })}
|
|
4603
|
-
className="w-full flex items-center gap-2 rounded-xl border border-slate-800 bg-slate-900/50 px-2 py-1.5 text-left hover:bg-slate-800/90"
|
|
4604
|
-
>
|
|
4605
|
-
<img src=${getAvatarUrl(creator.publisher)} alt="" className="w-9 h-9 rounded-full bg-slate-800" />
|
|
4606
|
-
<span className="min-w-0 flex-1">
|
|
4607
|
-
<span className="block truncate text-xs font-medium text-slate-100">${creator.publisher}</span>
|
|
4608
|
-
<span className="block truncate text-[10px] text-slate-400">${creator.packCount} packs · ❤️ ${shortNum(creator.likes)} · ⬇ ${shortNum(creator.opens)}</span>
|
|
4609
|
-
</span>
|
|
4610
|
-
<span className="text-[10px] text-slate-500">filtrar</span>
|
|
4611
|
-
</button>
|
|
4612
|
-
`)}
|
|
4613
|
-
</div>
|
|
4614
|
-
`}
|
|
4615
|
-
</div>
|
|
3794
|
+
${supportInfo?.url ? html` <a href=${supportInfo.url} target="_blank" rel="noreferrer noopener" className="w-full h-9 inline-flex items-center justify-center rounded-xl border border-emerald-500/35 bg-emerald-500/10 text-xs text-emerald-200 hover:bg-emerald-500/20"> 💬 Suporte no WhatsApp </a> ` : null}
|
|
3795
|
+
</div>
|
|
3796
|
+
</aside>
|
|
3797
|
+
|
|
3798
|
+
<div className="space-y-3 min-w-0">
|
|
3799
|
+
<section className="space-y-2 min-w-0">
|
|
3800
|
+
<div className="relative min-w-0">
|
|
3801
|
+
<div className="absolute left-0 top-0 bottom-0 w-5 bg-gradient-to-r from-slate-950 to-transparent pointer-events-none z-10"></div>
|
|
3802
|
+
<div className="absolute right-0 top-0 bottom-0 w-5 bg-gradient-to-l from-slate-950 to-transparent pointer-events-none z-10"></div>
|
|
3803
|
+
<div className="chips-scroll flex max-w-full gap-1.5 overflow-x-auto pb-1 pr-1">${dynamicCategoryOptions.map((item) => html` <button key=${item.value || 'all'} type="button" onClick=${() => handleCategoryChipPress(item.value)} className=${`chip-item h-8 whitespace-nowrap rounded-full px-3 text-[11px] border transition ${activeCategory === item.value ? 'bg-emerald-400 text-slate-900 border-emerald-300 font-semibold shadow-[0_0_0_2px_rgba(16,185,129,0.18)]' : 'bg-slate-900 text-slate-300 border-slate-800 hover:bg-slate-800'}`}>${item.label}</button> `)}</div>
|
|
3804
|
+
</div>
|
|
3805
|
+
</section>
|
|
4616
3806
|
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
>
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
<
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
3807
|
+
${packs.length
|
|
3808
|
+
? html`
|
|
3809
|
+
<section className="space-y-2">
|
|
3810
|
+
<div className="rounded-2xl border border-slate-800 bg-slate-900/70 p-2.5">
|
|
3811
|
+
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
3812
|
+
<div>
|
|
3813
|
+
<p className="text-[11px] uppercase tracking-wide text-slate-400">Descobrir</p>
|
|
3814
|
+
<h3 className="text-sm font-semibold text-slate-100">Painel oficial do marketplace</h3>
|
|
3815
|
+
<p className="text-[11px] text-slate-500">Métricas globais reais da plataforma (cache ~${globalMarketplaceStats?.cacheSeconds || 45}s, atualização automática).</p>
|
|
3816
|
+
</div>
|
|
3817
|
+
<div className="flex items-center gap-2">
|
|
3818
|
+
${globalMarketplaceStatsLoading && !globalMarketplaceStats ? html`<span className="inline-flex h-8 items-center rounded-lg border border-slate-700 bg-slate-900/60 px-3 text-[11px] text-slate-300">Carregando métricas...</span>` : null} ${globalMarketplaceStatsError ? html`<span className="inline-flex h-8 items-center rounded-lg border border-amber-500/30 bg-amber-500/10 px-3 text-[11px] text-amber-100">Fallback local</span>` : null}
|
|
3819
|
+
<a href="/stickers/create/" className="inline-flex h-8 items-center rounded-lg border border-emerald-500/35 bg-emerald-500/10 px-3 text-[11px] font-semibold text-emerald-200 hover:bg-emerald-500/20"> ✨ Criar pack agora </a>
|
|
3820
|
+
</div>
|
|
3821
|
+
</div>
|
|
3822
|
+
|
|
3823
|
+
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
3824
|
+
${[
|
|
3825
|
+
{ key: 'growing', label: '🔥 Crescendo' },
|
|
3826
|
+
{ key: 'top', label: '🏆 Top 10' },
|
|
3827
|
+
{ key: 'creators', label: '⭐ Criadores' },
|
|
3828
|
+
].map((tab) => html` <button key=${tab.key} type="button" onClick=${() => setDiscoverTab(tab.key)} className=${`h-8 rounded-full border px-2.5 text-[11px] touch-manipulation ${discoverTab === tab.key ? 'border-cyan-400/35 bg-cyan-500/10 text-cyan-100' : 'border-slate-700 bg-slate-950/40 text-slate-300 hover:bg-slate-800'}`}>${tab.label}</button> `)}
|
|
3829
|
+
</div>
|
|
3830
|
+
|
|
3831
|
+
<div className="mt-2 hidden lg:block">
|
|
3832
|
+
${discoverTab === 'growing'
|
|
3833
|
+
? html` <div className="grid grid-cols-1 xl:grid-cols-2 gap-2">${growingNowPacks.slice(0, 6).map((entry) => html`<${DiscoverPackRowItem} key=${`grow-${entry.pack_key}`} pack=${entry} onOpen=${openPack} hasNsfwAccess=${hasNsfwAccess} onRequireLogin=${requestNsfwUnlock} />`)}</div> `
|
|
3834
|
+
: discoverTab === 'top'
|
|
3835
|
+
? html` <div className="grid grid-cols-1 xl:grid-cols-2 gap-2">${topWeekPacks.slice(0, 8).map((entry, idx) => html`<${DiscoverPackRowItem} key=${`top-${entry.pack_key}`} pack=${entry} onOpen=${openPack} rank=${idx + 1} hasNsfwAccess=${hasNsfwAccess} onRequireLogin=${requestNsfwUnlock} />`)}</div> `
|
|
3836
|
+
: html`
|
|
3837
|
+
<div className="grid grid-cols-1 xl:grid-cols-2 gap-2">
|
|
3838
|
+
${featuredCreators.map(
|
|
3839
|
+
(creator) => html`
|
|
3840
|
+
<button key=${creator.publisher} onClick=${() => openCatalogWithState({ q: creator.publisher, category: '', sort: DEFAULT_CATALOG_SORT, filter: '', push: true })} className="w-full flex items-center gap-2 rounded-xl border border-slate-800 bg-slate-900/50 px-2 py-1.5 text-left hover:bg-slate-800/90">
|
|
3841
|
+
<img src=${getAvatarUrl(creator.publisher)} alt="" className="w-9 h-9 rounded-full bg-slate-800" />
|
|
3842
|
+
<span className="min-w-0 flex-1">
|
|
3843
|
+
<span className="block truncate text-xs font-medium text-slate-100">${creator.publisher}</span>
|
|
3844
|
+
<span className="block truncate text-[10px] text-slate-400">${creator.packCount} packs · ❤️ ${shortNum(creator.likes)} · ⬇ ${shortNum(creator.opens)}</span>
|
|
3845
|
+
</span>
|
|
3846
|
+
<span className="text-[10px] text-slate-500">filtrar</span>
|
|
3847
|
+
</button>
|
|
3848
|
+
`,
|
|
3849
|
+
)}
|
|
3850
|
+
</div>
|
|
3851
|
+
`}
|
|
3852
|
+
</div>
|
|
3853
|
+
|
|
3854
|
+
<div className="mt-2 lg:hidden">
|
|
3855
|
+
${discoverTab === 'growing'
|
|
3856
|
+
? html`
|
|
3857
|
+
<section className="space-y-1.5">
|
|
3858
|
+
<div className="flex items-center justify-between">
|
|
3859
|
+
<h4 className="text-xs font-semibold text-slate-200">🔥 Em alta agora</h4>
|
|
3860
|
+
<button type="button" onClick=${openTrendingCatalog} className="text-[10px] text-cyan-300">ver lista</button>
|
|
3861
|
+
</div>
|
|
3862
|
+
<div className="flex gap-2 overflow-x-auto pb-1">${growingNowPacks.slice(0, 8).map((entry) => html`<${DiscoverPackMiniCard} key=${`mobile-grow-${entry.pack_key}`} pack=${entry} onOpen=${openPack} hasNsfwAccess=${hasNsfwAccess} onRequireLogin=${requestNsfwUnlock} />`)}</div>
|
|
3863
|
+
</section>
|
|
3864
|
+
`
|
|
3865
|
+
: discoverTab === 'top'
|
|
3866
|
+
? html`
|
|
3867
|
+
<section className="space-y-1.5">
|
|
3868
|
+
<div className="flex items-center justify-between">
|
|
3869
|
+
<h4 className="text-xs font-semibold text-slate-200">🏆 Top 10 da semana</h4>
|
|
3870
|
+
<button type="button" onClick=${() => openCatalogWithState({ q: '', category: '', sort: 'trending', filter: '', push: true })} className="text-[10px] text-cyan-300">ver lista</button>
|
|
3871
|
+
</div>
|
|
3872
|
+
<div className="flex gap-2 overflow-x-auto pb-1">${topWeekPacks.slice(0, 8).map((entry) => html`<${DiscoverPackMiniCard} key=${`mobile-top-${entry.pack_key}`} pack=${entry} onOpen=${openPack} hasNsfwAccess=${hasNsfwAccess} onRequireLogin=${requestNsfwUnlock} />`)}</div>
|
|
3873
|
+
</section>
|
|
3874
|
+
`
|
|
3875
|
+
: html`
|
|
3876
|
+
<section className="space-y-1.5">
|
|
3877
|
+
<div className="flex items-center justify-between">
|
|
3878
|
+
<h4 className="text-xs font-semibold text-slate-200">👑 Criadores populares</h4>
|
|
3879
|
+
<button type="button" onClick=${() => openCreatorsRanking('popular', true)} className="text-[10px] text-cyan-300">ver lista</button>
|
|
3880
|
+
</div>
|
|
3881
|
+
<div className="flex gap-2 overflow-x-auto pb-1">${featuredCreators.map((creator) => html` <${DiscoverCreatorMiniCard} key=${`mobile-tab-creator-${creator.publisher}`} creator=${creator} onPick=${(publisher) => openCatalogWithState({ q: publisher, category: '', sort: DEFAULT_CATALOG_SORT, filter: '', push: true })} /> `)}</div>
|
|
3882
|
+
</section>
|
|
3883
|
+
`}
|
|
3884
|
+
</div>
|
|
3885
|
+
</div>
|
|
4683
3886
|
|
|
4684
|
-
|
|
4685
|
-
${catalogMetricCards.map(
|
|
4686
|
-
(card) => html`<${CatalogMetricCard}
|
|
4687
|
-
key=${card.key}
|
|
4688
|
-
label=${card.label}
|
|
4689
|
-
value=${card.value}
|
|
4690
|
-
valueRaw=${card.valueRaw}
|
|
4691
|
-
numberFormat=${card.numberFormat}
|
|
4692
|
-
icon=${card.icon}
|
|
4693
|
-
hint=${card.hint}
|
|
4694
|
-
bars=${card.bars}
|
|
4695
|
-
tone=${card.tone}
|
|
4696
|
-
/>`,
|
|
4697
|
-
)}
|
|
4698
|
-
</div>
|
|
3887
|
+
<div className="grid grid-cols-2 gap-2 sm:grid-cols-4">${catalogMetricCards.map((card) => html`<${CatalogMetricCard} key=${card.key} label=${card.label} value=${card.value} valueRaw=${card.valueRaw} numberFormat=${card.numberFormat} icon=${card.icon} hint=${card.hint} bars=${card.bars} tone=${card.tone} />`)}</div>
|
|
4699
3888
|
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
3889
|
+
<div className="lg:hidden space-y-2">
|
|
3890
|
+
<section className="space-y-1.5">
|
|
3891
|
+
<div className="flex items-center justify-between">
|
|
3892
|
+
<h4 className="text-xs font-semibold text-slate-200">🆕 Recém publicados</h4>
|
|
3893
|
+
<button type="button" onClick=${openSortPicker} disabled=${sortPickerBusy} className="text-[10px] text-cyan-300 disabled:opacity-50">ordenar</button>
|
|
3894
|
+
</div>
|
|
3895
|
+
<div className="flex gap-2 overflow-x-auto pb-1">${recentPublishedPacks.slice(0, 8).map((entry) => html`<${DiscoverPackMiniCard} key=${`mobile-new-${entry.pack_key}`} pack=${entry} onOpen=${openPack} hasNsfwAccess=${hasNsfwAccess} onRequireLogin=${requestNsfwUnlock} />`)}</div>
|
|
3896
|
+
</section>
|
|
4705
3897
|
</div>
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
)}
|
|
3898
|
+
|
|
3899
|
+
<div className="hidden lg:block rounded-2xl border border-emerald-500/20 bg-gradient-to-r from-emerald-500/10 to-cyan-500/5 p-2.5">
|
|
3900
|
+
<div className="flex items-center justify-between gap-3">
|
|
3901
|
+
<div>
|
|
3902
|
+
<p className="text-xs font-semibold text-emerald-100">Quer aparecer em destaque?</p>
|
|
3903
|
+
<p className="text-[11px] text-slate-300">Publique seu pack e melhore capa/tags para ganhar mais cliques.</p>
|
|
3904
|
+
</div>
|
|
3905
|
+
<a href="/stickers/create/" className="inline-flex h-8 items-center rounded-lg border border-emerald-400/35 bg-emerald-500/10 px-3 text-[11px] font-semibold text-emerald-100 hover:bg-emerald-500/20"> Publicar pack </a>
|
|
3906
|
+
</div>
|
|
4716
3907
|
</div>
|
|
4717
3908
|
</section>
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
<
|
|
3909
|
+
`
|
|
3910
|
+
: null}
|
|
3911
|
+
${packs.length
|
|
3912
|
+
? html`
|
|
3913
|
+
<section className="space-y-3 min-w-0">
|
|
3914
|
+
<div className="flex items-end justify-between gap-3">
|
|
3915
|
+
<div>
|
|
3916
|
+
<h2 className="text-lg sm:text-xl font-bold">Packs</h2>
|
|
3917
|
+
<p className="text-xs text-slate-400">${sortedPacks.length}${packHasMore ? '+' : ''} resultados · ${categoryActiveLabel}</p>
|
|
3918
|
+
</div>
|
|
3919
|
+
<div className="hidden md:flex items-center gap-2">
|
|
3920
|
+
<span className="text-xs text-slate-400">Ordenar por</span>
|
|
3921
|
+
<button type="button" onClick=${openSortPicker} disabled=${sortPickerBusy} className="inline-flex h-8 items-center gap-2 rounded-xl border border-slate-700 bg-slate-900 px-3 text-xs text-slate-200 hover:bg-slate-800 disabled:opacity-60">
|
|
3922
|
+
<span>${catalogSortLabel(sortBy)}</span>
|
|
3923
|
+
<span className="text-[10px] text-slate-400">▾</span>
|
|
3924
|
+
</button>
|
|
3925
|
+
</div>
|
|
4725
3926
|
</div>
|
|
4726
|
-
<
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
`
|
|
4733
|
-
: null}
|
|
4734
|
-
|
|
4735
|
-
${packs.length
|
|
4736
|
-
? html`
|
|
4737
|
-
<section className="space-y-3 min-w-0">
|
|
4738
|
-
<div className="flex items-end justify-between gap-3">
|
|
4739
|
-
<div>
|
|
4740
|
-
<h2 className="text-lg sm:text-xl font-bold">Packs</h2>
|
|
4741
|
-
<p className="text-xs text-slate-400">${sortedPacks.length}${packHasMore ? '+' : ''} resultados · ${categoryActiveLabel}</p>
|
|
4742
|
-
</div>
|
|
4743
|
-
<div className="hidden md:flex items-center gap-2">
|
|
4744
|
-
<span className="text-xs text-slate-400">Ordenar por</span>
|
|
4745
|
-
<button
|
|
4746
|
-
type="button"
|
|
4747
|
-
onClick=${openSortPicker}
|
|
4748
|
-
disabled=${sortPickerBusy}
|
|
4749
|
-
className="inline-flex h-8 items-center gap-2 rounded-xl border border-slate-700 bg-slate-900 px-3 text-xs text-slate-200 hover:bg-slate-800 disabled:opacity-60"
|
|
4750
|
-
>
|
|
4751
|
-
<span>${catalogSortLabel(sortBy)}</span>
|
|
4752
|
-
<span className="text-[10px] text-slate-400">▾</span>
|
|
4753
|
-
</button>
|
|
4754
|
-
</div>
|
|
4755
|
-
</div>
|
|
4756
|
-
<div className="grid min-w-0 grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6 gap-2.5 sm:gap-3">
|
|
4757
|
-
${sortedPacks.map(
|
|
4758
|
-
(pack, index) => html`<div key=${pack.pack_key || pack.id} className="fade-card"><${PackCard}
|
|
4759
|
-
pack=${pack}
|
|
4760
|
-
index=${index}
|
|
4761
|
-
onOpen=${openPack}
|
|
4762
|
-
hasNsfwAccess=${hasNsfwAccess}
|
|
4763
|
-
onRequireLogin=${requestNsfwUnlock}
|
|
4764
|
-
/></div>`,
|
|
4765
|
-
)}
|
|
4766
|
-
</div>
|
|
4767
|
-
<div ref=${setSentinel} className="h-8 flex items-center justify-center text-xs text-slate-500">
|
|
4768
|
-
${packsLoadingMore ? 'Carregando mais packs...' : packHasMore ? 'Role para carregar mais' : 'Fim da lista'}
|
|
4769
|
-
</div>
|
|
4770
|
-
</section>
|
|
4771
|
-
`
|
|
4772
|
-
: null}
|
|
3927
|
+
<div className="grid min-w-0 grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6 gap-2.5 sm:gap-3">${sortedPacks.map((pack, index) => html`<div key=${pack.pack_key || pack.id} className="fade-card"><${PackCard} pack=${pack} index=${index} onOpen=${openPack} hasNsfwAccess=${hasNsfwAccess} onRequireLogin=${requestNsfwUnlock} /></div>`)}</div>
|
|
3928
|
+
<div ref=${setSentinel} className="h-8 flex items-center justify-center text-xs text-slate-500">${packsLoadingMore ? 'Carregando mais packs...' : packHasMore ? 'Role para carregar mais' : 'Fim da lista'}</div>
|
|
3929
|
+
</section>
|
|
3930
|
+
`
|
|
3931
|
+
: null}
|
|
3932
|
+
${packsLoading ? html`<${SkeletonGrid} count=${10} />` : null} ${!packsLoading && !hasAnyResult ? html`<${EmptyState} onClear=${clearFilters} />` : null}
|
|
4773
3933
|
|
|
4774
|
-
|
|
4775
|
-
|
|
3934
|
+
<section className="space-y-2.5">
|
|
3935
|
+
<div className="flex items-center justify-between">
|
|
3936
|
+
<h2 className="text-base sm:text-lg font-bold">Stickers sem pack</h2>
|
|
3937
|
+
<span className="text-xs text-slate-400">${orphans.length} resultados</span>
|
|
3938
|
+
</div>
|
|
4776
3939
|
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
<h2 className="text-base sm:text-lg font-bold">Stickers sem pack</h2>
|
|
4780
|
-
<span className="text-xs text-slate-400">${orphans.length} resultados</span>
|
|
3940
|
+
${orphansLoading ? html`<div className="grid grid-cols-3 md:grid-cols-5 lg:grid-cols-8 gap-2.5 sm:gap-3">${Array.from({ length: 16 }).map((_, i) => html`<div key=${i} className="rounded-2xl border border-slate-700 bg-slate-800 animate-pulse aspect-square"></div>`)}</div>` : html` <div className="grid grid-cols-3 md:grid-cols-5 lg:grid-cols-8 gap-2.5 sm:gap-3">${orphans.map((item) => html`<div key=${item.id} className="fade-card"><${OrphanCard} sticker=${item} hasNsfwAccess=${hasNsfwAccess} onRequireLogin=${requestNsfwUnlock} /></div>`)}</div> `}
|
|
3941
|
+
</section>
|
|
4781
3942
|
</div>
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
? html`<div className="grid grid-cols-3 md:grid-cols-5 lg:grid-cols-8 gap-2.5 sm:gap-3">${Array.from({ length: 16 }).map(
|
|
4785
|
-
(_, i) => html`<div key=${i} className="rounded-2xl border border-slate-700 bg-slate-800 animate-pulse aspect-square"></div>`,
|
|
4786
|
-
)}</div>`
|
|
4787
|
-
: html`
|
|
4788
|
-
<div className="grid grid-cols-3 md:grid-cols-5 lg:grid-cols-8 gap-2.5 sm:gap-3">
|
|
4789
|
-
${orphans.map(
|
|
4790
|
-
(item) => html`<div key=${item.id} className="fade-card"><${OrphanCard}
|
|
4791
|
-
sticker=${item}
|
|
4792
|
-
hasNsfwAccess=${hasNsfwAccess}
|
|
4793
|
-
onRequireLogin=${requestNsfwUnlock}
|
|
4794
|
-
/></div>`,
|
|
4795
|
-
)}
|
|
4796
|
-
</div>
|
|
4797
|
-
`}
|
|
4798
|
-
</section>
|
|
4799
|
-
</div>
|
|
4800
|
-
</div>
|
|
4801
|
-
`}
|
|
3943
|
+
</div>
|
|
3944
|
+
`}
|
|
4802
3945
|
</main>
|
|
4803
3946
|
<${UploadTaskWidget}
|
|
4804
3947
|
task=${uploadTask}
|
|
@@ -4809,55 +3952,11 @@ function StickersApp() {
|
|
|
4809
3952
|
setUploadTask(null);
|
|
4810
3953
|
}}
|
|
4811
3954
|
/>
|
|
4812
|
-
<${CatalogSortPicker}
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
onSelect=${handleCatalogSortSelection}
|
|
4818
|
-
/>
|
|
4819
|
-
<${PackActionsSheet}
|
|
4820
|
-
open=${Boolean(packActionsSheetPack)}
|
|
4821
|
-
pack=${packActionsSheetPack}
|
|
4822
|
-
busyAction=${packActionsSheetPack ? packActionBusyByKey?.[packActionsSheetPack.pack_key] || '' : ''}
|
|
4823
|
-
onClose=${closePackActionsSheet}
|
|
4824
|
-
onAction=${handlePackActionsSheetAction}
|
|
4825
|
-
/>
|
|
4826
|
-
<${PackManagerModal}
|
|
4827
|
-
open=${managePackOpen}
|
|
4828
|
-
data=${managePackData}
|
|
4829
|
-
loading=${managePackLoading}
|
|
4830
|
-
error=${managePackError}
|
|
4831
|
-
busyAction=${managePackBusyAction}
|
|
4832
|
-
onClose=${closeManagePackModal}
|
|
4833
|
-
onRefresh=${refreshManagePackData}
|
|
4834
|
-
onSaveMetadata=${handleManageSaveMetadata}
|
|
4835
|
-
onAddSticker=${handleManageAddSticker}
|
|
4836
|
-
onRemoveSticker=${handleManageRemoveSticker}
|
|
4837
|
-
onReplaceSticker=${handleManageReplaceSticker}
|
|
4838
|
-
onSetCover=${handleManageSetCover}
|
|
4839
|
-
onReorder=${handleManageReorder}
|
|
4840
|
-
onOpenAnalytics=${() => openAnalyticsModalForPack(managePackData?.pack || null)}
|
|
4841
|
-
/>
|
|
4842
|
-
<${PackAnalyticsModal}
|
|
4843
|
-
open=${analyticsModalOpen}
|
|
4844
|
-
pack=${analyticsModalPack}
|
|
4845
|
-
data=${analyticsModalData}
|
|
4846
|
-
loading=${analyticsModalLoading}
|
|
4847
|
-
error=${analyticsModalError}
|
|
4848
|
-
onClose=${closeAnalyticsModal}
|
|
4849
|
-
/>
|
|
4850
|
-
<${ConfirmDialog}
|
|
4851
|
-
open=${Boolean(confirmDeletePack)}
|
|
4852
|
-
title="Apagar pack"
|
|
4853
|
-
message=${confirmDeletePack ? `Tem certeza que deseja apagar o pack "${confirmDeletePack.name || confirmDeletePack.pack_key}"? Essa ação remove o pack do seu painel.` : ''}
|
|
4854
|
-
confirmLabel="Apagar pack"
|
|
4855
|
-
cancelLabel="Cancelar"
|
|
4856
|
-
danger=${true}
|
|
4857
|
-
busy=${confirmDeleteBusy}
|
|
4858
|
-
onCancel=${() => (confirmDeleteBusy ? null : setConfirmDeletePack(null))}
|
|
4859
|
-
onConfirm=${handleDeletePackConfirmed}
|
|
4860
|
-
/>
|
|
3955
|
+
<${CatalogSortPicker} open=${sortPickerOpen} currentSort=${sortBy} busy=${sortPickerBusy} onClose=${closeSortPicker} onSelect=${handleCatalogSortSelection} />
|
|
3956
|
+
<${PackActionsSheet} open=${Boolean(packActionsSheetPack)} pack=${packActionsSheetPack} busyAction=${packActionsSheetPack ? packActionBusyByKey?.[packActionsSheetPack.pack_key] || '' : ''} onClose=${closePackActionsSheet} onAction=${handlePackActionsSheetAction} />
|
|
3957
|
+
<${PackManagerModal} open=${managePackOpen} data=${managePackData} loading=${managePackLoading} error=${managePackError} busyAction=${managePackBusyAction} onClose=${closeManagePackModal} onRefresh=${refreshManagePackData} onSaveMetadata=${handleManageSaveMetadata} onAddSticker=${handleManageAddSticker} onRemoveSticker=${handleManageRemoveSticker} onReplaceSticker=${handleManageReplaceSticker} onSetCover=${handleManageSetCover} onReorder=${handleManageReorder} onOpenAnalytics=${() => openAnalyticsModalForPack(managePackData?.pack || null)} />
|
|
3958
|
+
<${PackAnalyticsModal} open=${analyticsModalOpen} pack=${analyticsModalPack} data=${analyticsModalData} loading=${analyticsModalLoading} error=${analyticsModalError} onClose=${closeAnalyticsModal} />
|
|
3959
|
+
<${ConfirmDialog} open=${Boolean(confirmDeletePack)} title="Apagar pack" message=${confirmDeletePack ? `Tem certeza que deseja apagar o pack "${confirmDeletePack.name || confirmDeletePack.pack_key}"? Essa ação remove o pack do seu painel.` : ''} confirmLabel="Apagar pack" cancelLabel="Cancelar" danger=${true} busy=${confirmDeleteBusy} onCancel=${() => (confirmDeleteBusy ? null : setConfirmDeletePack(null))} onConfirm=${handleDeletePackConfirmed} />
|
|
4861
3960
|
<${ToastStack} toasts=${profileToasts} onDismiss=${dismissProfileToast} />
|
|
4862
3961
|
</div>
|
|
4863
3962
|
`;
|