@kaikybrofc/omnizap-system 2.2.10 → 2.3.1
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 +13 -13
- 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 +40 -39
- 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/index.js +1 -0
- package/database/init.js +1 -8
- package/database/migrations/20260228_0027_web_visit_event.sql +15 -0
- 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 +1106 -993
- package/public/js/apps/apiDocsApp.js +17 -167
- package/public/js/apps/createPackApp.js +69 -332
- package/public/js/apps/homeApp.js +274 -101
- package/public/js/apps/loginApp.js +3 -12
- package/public/js/apps/stickersAdminApp.js +190 -181
- package/public/js/apps/stickersApp.js +482 -1411
- package/public/js/apps/userApp.js +217 -1
- 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 +200 -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 +369 -122
- package/public/user/index.html +527 -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/auth/googleWebAuth/googleWebAuthService.js +614 -0
- package/server/controllers/stickerCatalogController.js +297 -632
- 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,11 +1727,8 @@ 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
|
-
const packApiPath = `/api/sticker-packs/${encodeURIComponent(String(pack?.pack_key || '').trim())}`;
|
|
2204
1732
|
const engagement = getPackEngagement(pack);
|
|
2205
1733
|
const hasReactionRequest = Boolean(reactionLoading);
|
|
2206
1734
|
const [previewIndex, setPreviewIndex] = useState(-1);
|
|
@@ -2208,35 +1736,18 @@ function PackPage({
|
|
|
2208
1736
|
|
|
2209
1737
|
return html`
|
|
2210
1738
|
<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>
|
|
1739
|
+
<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
1740
|
|
|
2219
1741
|
<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
1742
|
<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
|
-
/>
|
|
1743
|
+
<img src=${cover} alt=${`Capa ${pack?.name || 'Pack'}`} className=${`h-full w-full object-cover ${packLockedByNsfw ? 'blur-md scale-105' : ''}`} loading="lazy" />
|
|
2227
1744
|
<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}
|
|
1745
|
+
${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
1746
|
<div className="absolute inset-x-0 bottom-0 p-4 sm:p-5">
|
|
2232
1747
|
<div className="max-w-4xl">
|
|
2233
1748
|
<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>
|
|
1749
|
+
<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>
|
|
1750
|
+
<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
1751
|
</div>
|
|
2241
1752
|
</div>
|
|
2242
1753
|
</div>
|
|
@@ -2247,13 +1758,7 @@ function PackPage({
|
|
|
2247
1758
|
<div className="rounded-xl border border-amber-500/35 bg-amber-500/10 p-3">
|
|
2248
1759
|
<p className="text-sm font-semibold text-amber-100">Este pack foi marcado como sensível (+18).</p>
|
|
2249
1760
|
<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>
|
|
1761
|
+
<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
1762
|
</div>
|
|
2258
1763
|
`
|
|
2259
1764
|
: null}
|
|
@@ -2264,88 +1769,20 @@ function PackPage({
|
|
|
2264
1769
|
<span className="text-slate-200">👁 <strong className="font-semibold">${shortNum(engagement.openCount)}</strong></span>
|
|
2265
1770
|
</div>
|
|
2266
1771
|
|
|
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}
|
|
1772
|
+
${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
1773
|
|
|
2280
1774
|
<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}
|
|
1775
|
+
<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>
|
|
1776
|
+
<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>
|
|
1777
|
+
${reactionNotice?.message ? html` <span className=${`text-xs ${reactionNotice.type === 'error' ? 'text-rose-300' : 'text-emerald-300'}`}> ${reactionNotice.message} </span> ` : null}
|
|
2304
1778
|
</div>
|
|
2305
1779
|
|
|
2306
|
-
${pack?.description
|
|
2307
|
-
? html`<p className="text-sm leading-6 text-slate-300">${pack.description}</p>`
|
|
2308
|
-
: null}
|
|
2309
|
-
|
|
2310
|
-
<section className="rounded-xl border border-cyan-500/25 bg-cyan-500/5 p-3">
|
|
2311
|
-
<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>
|
|
2315
|
-
<pre className="mt-2 overflow-auto rounded-lg border border-slate-800 bg-slate-950/60 p-2 text-[11px] text-slate-300">
|
|
2316
|
-
GET ${packApiPath}
|
|
2317
|
-
POST ${packApiPath}/open
|
|
2318
|
-
POST ${packApiPath}/like
|
|
2319
|
-
</pre>
|
|
2320
|
-
<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
|
-
Ver API e exemplos
|
|
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>
|
|
2330
|
-
</div>
|
|
2331
|
-
</section>
|
|
2332
|
-
|
|
1780
|
+
${pack?.description ? html`<p className="text-sm leading-6 text-slate-300">${pack.description}</p>` : null}
|
|
2333
1781
|
${tags.length
|
|
2334
1782
|
? html`
|
|
2335
1783
|
<div className="space-y-1">
|
|
2336
1784
|
<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>
|
|
1785
|
+
<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
1786
|
</div>
|
|
2350
1787
|
`
|
|
2351
1788
|
: null}
|
|
@@ -2363,57 +1800,35 @@ POST ${packApiPath}/like
|
|
|
2363
1800
|
<div className="rounded-2xl border border-dashed border-amber-500/35 bg-slate-900/55 p-8 text-center">
|
|
2364
1801
|
<p className="text-sm font-semibold text-amber-100">Stickers bloqueados por conteúdo sensível.</p>
|
|
2365
1802
|
<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>
|
|
1803
|
+
<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
1804
|
</div>
|
|
2374
1805
|
`
|
|
2375
1806
|
: items.length
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
(item, index) => {
|
|
1807
|
+
? html`
|
|
1808
|
+
<div className="pack-stickers-grid gap-2 sm:gap-3">
|
|
1809
|
+
${items.map((item, index) => {
|
|
2380
1810
|
const stickerLockedByNsfw = isStickerMarkedNsfw(item) && !hasNsfwAccess;
|
|
2381
1811
|
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
|
-
/>
|
|
1812
|
+
<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">
|
|
1813
|
+
<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
1814
|
${stickerLockedByNsfw
|
|
2397
1815
|
? html`
|
|
2398
1816
|
<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>
|
|
1817
|
+
<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
1818
|
</div>
|
|
2403
1819
|
`
|
|
2404
1820
|
: null}
|
|
2405
1821
|
</button>
|
|
2406
1822
|
`;
|
|
2407
|
-
}
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
`
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
`}
|
|
1823
|
+
})}
|
|
1824
|
+
</div>
|
|
1825
|
+
`
|
|
1826
|
+
: html`
|
|
1827
|
+
<div className="rounded-2xl border border-dashed border-slate-700 bg-slate-900/40 p-8 text-center">
|
|
1828
|
+
<p className="text-sm font-semibold text-slate-100">Este pack ainda não tem stickers visíveis.</p>
|
|
1829
|
+
<p className="mt-1 text-xs text-slate-400">Volte mais tarde ou abra outro pack do catálogo.</p>
|
|
1830
|
+
</div>
|
|
1831
|
+
`}
|
|
2417
1832
|
</section>
|
|
2418
1833
|
|
|
2419
1834
|
${relatedPacks.length
|
|
@@ -2423,27 +1838,11 @@ POST ${packApiPath}/like
|
|
|
2423
1838
|
<h2 className="text-lg font-bold">Packs relacionados</h2>
|
|
2424
1839
|
<span className="text-xs text-slate-500">${relatedPacks.length} sugestões</span>
|
|
2425
1840
|
</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>
|
|
1841
|
+
<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
1842
|
</section>
|
|
2432
1843
|
`
|
|
2433
1844
|
: 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}
|
|
1845
|
+
${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
1846
|
</section>
|
|
2448
1847
|
`;
|
|
2449
1848
|
}
|
|
@@ -2571,11 +1970,7 @@ function StickersApp() {
|
|
|
2571
1970
|
|
|
2572
1971
|
if (activeCategory && !sortedTags.some((entry) => entry.value === activeCategory)) {
|
|
2573
1972
|
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
|
-
);
|
|
1973
|
+
sortedTags.unshift(meta ? { value: activeCategory, label: `${meta.icon} ${meta.label}`, icon: meta.icon } : { value: activeCategory, label: `🏷️ ${activeCategory.replace(/-/g, ' ')}`, icon: '🏷️' });
|
|
2579
1974
|
}
|
|
2580
1975
|
|
|
2581
1976
|
return [{ value: '', label: '🔥 Em alta', icon: '🔥' }, ...sortedTags];
|
|
@@ -2624,9 +2019,7 @@ function StickersApp() {
|
|
|
2624
2019
|
icon: '🕘',
|
|
2625
2020
|
}));
|
|
2626
2021
|
}
|
|
2627
|
-
return tagSuggestions
|
|
2628
|
-
.filter((item) => normalizeToken(item.value).includes(q) || normalizeToken(item.label).includes(q))
|
|
2629
|
-
.slice(0, 8);
|
|
2022
|
+
return tagSuggestions.filter((item) => normalizeToken(item.value).includes(q) || normalizeToken(item.label).includes(q)).slice(0, 8);
|
|
2630
2023
|
}, [query, tagSuggestions, recentSearches]);
|
|
2631
2024
|
|
|
2632
2025
|
const sortedPacks = useMemo(() => {
|
|
@@ -2662,10 +2055,7 @@ function StickersApp() {
|
|
|
2662
2055
|
return list;
|
|
2663
2056
|
}, [packs, sortBy]);
|
|
2664
2057
|
|
|
2665
|
-
const categoryActiveLabel =
|
|
2666
|
-
catalogFilter === 'trending'
|
|
2667
|
-
? 'Em alta agora'
|
|
2668
|
-
: dynamicCategoryOptions.find((entry) => entry.value === activeCategory)?.label?.replace(/^.+?\s/, '') || 'Todas';
|
|
2058
|
+
const categoryActiveLabel = catalogFilter === 'trending' ? 'Em alta agora' : dynamicCategoryOptions.find((entry) => entry.value === activeCategory)?.label?.replace(/^.+?\s/, '') || 'Todas';
|
|
2669
2059
|
const growingNowPacks = useMemo(() => {
|
|
2670
2060
|
return [...packs]
|
|
2671
2061
|
.map((pack) => {
|
|
@@ -2779,13 +2169,11 @@ function StickersApp() {
|
|
|
2779
2169
|
const recentPublishedPacks = useMemo(
|
|
2780
2170
|
() =>
|
|
2781
2171
|
[...packs]
|
|
2782
|
-
.sort(
|
|
2783
|
-
(a, b)
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
},
|
|
2788
|
-
)
|
|
2172
|
+
.sort((a, b) => {
|
|
2173
|
+
const completenessDelta = comparePacksByCompleteness(a, b);
|
|
2174
|
+
if (completenessDelta !== 0) return completenessDelta;
|
|
2175
|
+
return new Date(b?.created_at || b?.updated_at || 0).getTime() - new Date(a?.created_at || a?.updated_at || 0).getTime();
|
|
2176
|
+
})
|
|
2789
2177
|
.slice(0, 10),
|
|
2790
2178
|
[packs],
|
|
2791
2179
|
);
|
|
@@ -2893,8 +2281,7 @@ function StickersApp() {
|
|
|
2893
2281
|
const hasGoogleLogin = Boolean(googleAuth.user?.sub);
|
|
2894
2282
|
const hasNsfwAccess = hasGoogleLogin;
|
|
2895
2283
|
const googleLoginEnabled = Boolean(googleAuthConfig.enabled && googleAuthConfig.clientId);
|
|
2896
|
-
const shouldRenderGoogleButton =
|
|
2897
|
-
isProfileView && googleLoginEnabled && !hasGoogleLogin && googleSessionChecked && !googleAuthBusy;
|
|
2284
|
+
const shouldRenderGoogleButton = isProfileView && googleLoginEnabled && !hasGoogleLogin && googleSessionChecked && !googleAuthBusy;
|
|
2898
2285
|
|
|
2899
2286
|
const fetchJson = async (url, options = undefined) => {
|
|
2900
2287
|
const opts = options && typeof options === 'object' ? { ...options } : {};
|
|
@@ -3034,10 +2421,12 @@ function StickersApp() {
|
|
|
3034
2421
|
});
|
|
3035
2422
|
};
|
|
3036
2423
|
|
|
3037
|
-
const buildManagePackApiPath = (packKey, suffix = '') =>
|
|
3038
|
-
`${config.apiBasePath}/${encodeURIComponent(String(packKey || ''))}/manage${suffix}`;
|
|
2424
|
+
const buildManagePackApiPath = (packKey, suffix = '') => `${config.apiBasePath}/${encodeURIComponent(String(packKey || ''))}/manage${suffix}`;
|
|
3039
2425
|
|
|
3040
|
-
const getManagedMutationStatus = (payload) =>
|
|
2426
|
+
const getManagedMutationStatus = (payload) =>
|
|
2427
|
+
String(payload?.data?.status || payload?.status || '')
|
|
2428
|
+
.trim()
|
|
2429
|
+
.toLowerCase();
|
|
3041
2430
|
|
|
3042
2431
|
const applyManagedPackToMyList = (managedData) => {
|
|
3043
2432
|
const pack = managedData?.pack || null;
|
|
@@ -3130,8 +2519,7 @@ function StickersApp() {
|
|
|
3130
2519
|
applyManagedPackToMyList(managed);
|
|
3131
2520
|
}
|
|
3132
2521
|
|
|
3133
|
-
const targetKey =
|
|
3134
|
-
String(managed?.pack?.pack_key || envelope?.data?.pack_key || managePackTargetKey || '').trim() || '';
|
|
2522
|
+
const targetKey = String(managed?.pack?.pack_key || envelope?.data?.pack_key || managePackTargetKey || '').trim() || '';
|
|
3135
2523
|
if (targetKey && managePackOpen && String(managePackTargetKey || '') === targetKey) {
|
|
3136
2524
|
try {
|
|
3137
2525
|
await loadManagePackData(targetKey, { openModal: true, silent: true });
|
|
@@ -3192,9 +2580,7 @@ function StickersApp() {
|
|
|
3192
2580
|
const applyPackEngagement = (packKey, engagement) => {
|
|
3193
2581
|
if (!packKey || !engagement) return;
|
|
3194
2582
|
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
|
-
);
|
|
2583
|
+
setRelatedPacks((prev) => prev.map((entry) => (entry?.pack_key === packKey ? mergeEngagementInPack(entry, engagement) : entry)));
|
|
3198
2584
|
setCurrentPack((prev) => (prev?.pack_key === packKey ? mergeEngagementInPack(prev, engagement) : prev));
|
|
3199
2585
|
};
|
|
3200
2586
|
|
|
@@ -3252,7 +2638,7 @@ function StickersApp() {
|
|
|
3252
2638
|
|
|
3253
2639
|
setPacks((prev) => (reset ? data : prev.concat(data)));
|
|
3254
2640
|
setPackHasMore(hasMore);
|
|
3255
|
-
setPackOffset(Number.isFinite(next) ? next :
|
|
2641
|
+
setPackOffset(Number.isFinite(next) ? next : reset ? data.length : nextOffset + data.length);
|
|
3256
2642
|
} catch (err) {
|
|
3257
2643
|
setError(err?.message || 'Falha ao carregar packs');
|
|
3258
2644
|
if (reset) setPacks([]);
|
|
@@ -3363,9 +2749,7 @@ function StickersApp() {
|
|
|
3363
2749
|
else if (Array.isArray(pack?.tags) && pack.tags[0]) relatedParams.set('categories', pack.tags[0]);
|
|
3364
2750
|
|
|
3365
2751
|
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);
|
|
2752
|
+
const relatedList = (Array.isArray(relatedPayload?.data) ? relatedPayload.data : []).filter((entry) => entry.pack_key && entry.pack_key !== pack?.pack_key).slice(0, 8);
|
|
3369
2753
|
setRelatedPacks(relatedList);
|
|
3370
2754
|
};
|
|
3371
2755
|
|
|
@@ -3414,14 +2798,14 @@ function StickersApp() {
|
|
|
3414
2798
|
}
|
|
3415
2799
|
};
|
|
3416
2800
|
|
|
3417
|
-
const buildCatalogWebUrl = ({
|
|
3418
|
-
q = appliedQuery,
|
|
3419
|
-
category = activeCategory,
|
|
3420
|
-
sort = sortBy,
|
|
3421
|
-
filter = catalogFilter,
|
|
3422
|
-
} = {}) => {
|
|
2801
|
+
const buildCatalogWebUrl = ({ q = appliedQuery, category = activeCategory, sort = sortBy, filter = catalogFilter } = {}) => {
|
|
3423
2802
|
const params = new URLSearchParams();
|
|
3424
|
-
const normalizedFilter =
|
|
2803
|
+
const normalizedFilter =
|
|
2804
|
+
String(filter || '')
|
|
2805
|
+
.trim()
|
|
2806
|
+
.toLowerCase() === 'trending'
|
|
2807
|
+
? 'trending'
|
|
2808
|
+
: '';
|
|
3425
2809
|
const normalizedSort = normalizeCatalogSort(sort || DEFAULT_CATALOG_SORT);
|
|
3426
2810
|
if (normalizedFilter === 'trending') {
|
|
3427
2811
|
params.set('filter', 'trending');
|
|
@@ -3441,10 +2825,19 @@ function StickersApp() {
|
|
|
3441
2825
|
};
|
|
3442
2826
|
|
|
3443
2827
|
const applyCatalogViewState = ({ q = '', category = '', sort = DEFAULT_CATALOG_SORT, filter = '' } = {}) => {
|
|
3444
|
-
const normalizedFilter =
|
|
2828
|
+
const normalizedFilter =
|
|
2829
|
+
String(filter || '')
|
|
2830
|
+
.trim()
|
|
2831
|
+
.toLowerCase() === 'trending'
|
|
2832
|
+
? 'trending'
|
|
2833
|
+
: '';
|
|
3445
2834
|
const normalizedSort = normalizeCatalogSort(normalizedFilter ? 'trending' : sort || DEFAULT_CATALOG_SORT);
|
|
3446
2835
|
const nextQ = normalizedFilter ? '' : String(q || '').trim();
|
|
3447
|
-
const nextCategory = normalizedFilter
|
|
2836
|
+
const nextCategory = normalizedFilter
|
|
2837
|
+
? ''
|
|
2838
|
+
: String(category || '')
|
|
2839
|
+
.trim()
|
|
2840
|
+
.toLowerCase();
|
|
3448
2841
|
|
|
3449
2842
|
setCatalogFilter(normalizedFilter);
|
|
3450
2843
|
setSortBy(normalizedSort);
|
|
@@ -3475,7 +2868,12 @@ function StickersApp() {
|
|
|
3475
2868
|
q,
|
|
3476
2869
|
category,
|
|
3477
2870
|
sort: normalizeCatalogSort(sort || DEFAULT_CATALOG_SORT),
|
|
3478
|
-
filter:
|
|
2871
|
+
filter:
|
|
2872
|
+
String(filter || '')
|
|
2873
|
+
.trim()
|
|
2874
|
+
.toLowerCase() === 'trending'
|
|
2875
|
+
? 'trending'
|
|
2876
|
+
: '',
|
|
3479
2877
|
};
|
|
3480
2878
|
if (push) window.history.pushState({}, '', buildCatalogWebUrl(nextState));
|
|
3481
2879
|
applyCatalogViewState(nextState);
|
|
@@ -3518,7 +2916,9 @@ function StickersApp() {
|
|
|
3518
2916
|
};
|
|
3519
2917
|
|
|
3520
2918
|
const openCatalogTagFilter = (tag) => {
|
|
3521
|
-
const nextTag = String(tag || '')
|
|
2919
|
+
const nextTag = String(tag || '')
|
|
2920
|
+
.trim()
|
|
2921
|
+
.toLowerCase();
|
|
3522
2922
|
if (!nextTag) return;
|
|
3523
2923
|
openCatalogWithState({
|
|
3524
2924
|
q: '',
|
|
@@ -3834,7 +3234,9 @@ function StickersApp() {
|
|
|
3834
3234
|
};
|
|
3835
3235
|
|
|
3836
3236
|
const handleCategoryChipPress = (value) => {
|
|
3837
|
-
const nextValue = String(value || '')
|
|
3237
|
+
const nextValue = String(value || '')
|
|
3238
|
+
.trim()
|
|
3239
|
+
.toLowerCase();
|
|
3838
3240
|
const previousScrollY = window.scrollY || 0;
|
|
3839
3241
|
|
|
3840
3242
|
if (!nextValue) {
|
|
@@ -3871,9 +3273,7 @@ function StickersApp() {
|
|
|
3871
3273
|
like_count: safeNumber(sourcePack?.engagement?.like_count),
|
|
3872
3274
|
dislike_count: safeNumber(sourcePack?.engagement?.dislike_count),
|
|
3873
3275
|
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),
|
|
3276
|
+
score: safeNumber(sourcePack?.engagement?.score) || safeNumber(sourcePack?.engagement?.like_count) - safeNumber(sourcePack?.engagement?.dislike_count),
|
|
3877
3277
|
updated_at: sourcePack?.engagement?.updated_at || null,
|
|
3878
3278
|
};
|
|
3879
3279
|
const optimistic = {
|
|
@@ -3973,7 +3373,12 @@ function StickersApp() {
|
|
|
3973
3373
|
const raw = localStorage.getItem(SEARCH_HISTORY_KEY);
|
|
3974
3374
|
const parsed = JSON.parse(raw || '[]');
|
|
3975
3375
|
if (Array.isArray(parsed)) {
|
|
3976
|
-
setRecentSearches(
|
|
3376
|
+
setRecentSearches(
|
|
3377
|
+
parsed
|
|
3378
|
+
.map((entry) => String(entry || '').trim())
|
|
3379
|
+
.filter(Boolean)
|
|
3380
|
+
.slice(0, 8),
|
|
3381
|
+
);
|
|
3977
3382
|
}
|
|
3978
3383
|
} catch {}
|
|
3979
3384
|
}, []);
|
|
@@ -4262,9 +3667,7 @@ function StickersApp() {
|
|
|
4262
3667
|
}`}
|
|
4263
3668
|
</style>
|
|
4264
3669
|
|
|
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
|
-
}`}>
|
|
3670
|
+
<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
3671
|
<div className="max-w-7xl mx-auto h-14 px-3 flex items-center gap-2.5">
|
|
4269
3672
|
<a href="/" className="shrink-0 flex items-center gap-2">
|
|
4270
3673
|
<img src="https://iili.io/FC3FABe.jpg" alt="OmniZap" className="w-7 h-7 rounded-full border border-slate-700" />
|
|
@@ -4274,10 +3677,10 @@ function StickersApp() {
|
|
|
4274
3677
|
${currentView === 'catalog'
|
|
4275
3678
|
? html`
|
|
4276
3679
|
<form onSubmit=${onSubmit} className="flex-1 relative">
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
3680
|
+
<span className="absolute left-2.5 top-1/2 -translate-y-1/2 text-xs text-slate-400">🔎</span>
|
|
3681
|
+
<input
|
|
3682
|
+
type="search"
|
|
3683
|
+
value=${query}
|
|
4281
3684
|
onChange=${(e) => setQuery(e.target.value)}
|
|
4282
3685
|
onFocus=${() => setShowAutocomplete(true)}
|
|
4283
3686
|
onBlur=${() => setTimeout(() => setShowAutocomplete(false), 120)}
|
|
@@ -4286,20 +3689,15 @@ function StickersApp() {
|
|
|
4286
3689
|
setShowAutocomplete(false);
|
|
4287
3690
|
}
|
|
4288
3691
|
}}
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
3692
|
+
placeholder="Buscar packs..."
|
|
3693
|
+
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"
|
|
3694
|
+
/>
|
|
4292
3695
|
${showAutocomplete && filteredSuggestions.length
|
|
4293
3696
|
? html`
|
|
4294
3697
|
<div className="absolute z-40 mt-2 w-full rounded-2xl border border-slate-800 bg-slate-900 shadow-xl overflow-hidden">
|
|
4295
3698
|
${filteredSuggestions.map(
|
|
4296
3699
|
(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
|
-
>
|
|
3700
|
+
<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
3701
|
<span className="inline-flex items-center gap-2">
|
|
4304
3702
|
<span>${item.icon || '🏷'}</span>
|
|
4305
3703
|
<span className="truncate">${item.label}</span>
|
|
@@ -4316,489 +3714,206 @@ function StickersApp() {
|
|
|
4316
3714
|
: html`<div className="flex-1"></div>`}
|
|
4317
3715
|
|
|
4318
3716
|
<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
|
-
>
|
|
3717
|
+
<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
3718
|
<span className="sm:hidden">👤</span>
|
|
4330
3719
|
<span className="hidden sm:inline">Meus Packs</span>
|
|
4331
3720
|
</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
|
-
>
|
|
3721
|
+
<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
3722
|
<span className="sm:hidden">➕</span>
|
|
4338
3723
|
<span className="hidden sm:inline">✨ Criar pack agora</span>
|
|
4339
3724
|
</a>
|
|
4340
3725
|
${supportInfo?.url
|
|
4341
3726
|
? 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
|
-
>
|
|
3727
|
+
<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
3728
|
<span className="sm:hidden">💬</span>
|
|
4350
3729
|
<span className="hidden sm:inline">Suporte</span>
|
|
4351
3730
|
</a>
|
|
4352
3731
|
`
|
|
4353
3732
|
: null}
|
|
4354
3733
|
<div className="hidden sm:flex items-center gap-2">
|
|
4355
|
-
|
|
4356
|
-
|
|
3734
|
+
<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>
|
|
3735
|
+
<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
3736
|
</div>
|
|
4358
3737
|
</div>
|
|
4359
3738
|
</div>
|
|
4360
3739
|
</header>
|
|
4361
3740
|
|
|
4362
3741
|
<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
|
-
|
|
3742
|
+
${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
3743
|
${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
|
-
`
|
|
3744
|
+
? 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
3745
|
: isCreatorsView
|
|
4390
|
-
? html`
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
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>
|
|
3746
|
+
? html` <${CreatorsRankingPage} creators=${sortedCreatorRanking} loading=${creatorRankingLoading} error=${creatorRankingError} sort=${creatorSort} onSortChange=${handleCreatorsSortChange} onBack=${goCatalog} onRetry=${loadCreatorRanking} onOpenCreator=${openCreatorProfileFromRanking} onOpenPack=${openPack} /> `
|
|
3747
|
+
: currentPackKey
|
|
3748
|
+
? 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} />`} `
|
|
3749
|
+
: html`
|
|
3750
|
+
<div className="lg:grid lg:grid-cols-[220px_minmax(0,1fr)] lg:gap-4">
|
|
3751
|
+
<aside className="hidden lg:block">
|
|
3752
|
+
<div className="sticky top-[72px] space-y-2.5 rounded-2xl border border-slate-800 bg-slate-900/80 p-2.5">
|
|
3753
|
+
<div className="rounded-xl border border-slate-800 bg-slate-950/40 p-2">
|
|
3754
|
+
<div className="flex items-center justify-between gap-2">
|
|
3755
|
+
<h3 className="text-xs font-semibold uppercase tracking-wide text-slate-300">Filtros</h3>
|
|
3756
|
+
<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>
|
|
3757
|
+
</div>
|
|
3758
|
+
<p className="mt-1 text-[11px] text-slate-500">${packs.length}${packHasMore ? '+' : ''} packs · ${orphans.length} sem pack</p>
|
|
3759
|
+
</div>
|
|
4456
3760
|
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
${
|
|
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
|
-
)}
|
|
3761
|
+
<details open className="rounded-xl border border-slate-800 bg-slate-950/40 p-2">
|
|
3762
|
+
<summary className="cursor-pointer list-none text-xs font-semibold text-slate-200">Ordenar catálogo</summary>
|
|
3763
|
+
<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>
|
|
3764
|
+
</details>
|
|
3765
|
+
|
|
3766
|
+
${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}
|
|
4517
3767
|
</div>
|
|
4518
|
-
</
|
|
4519
|
-
|
|
3768
|
+
</aside>
|
|
3769
|
+
|
|
3770
|
+
<div className="space-y-3 min-w-0">
|
|
3771
|
+
<section className="space-y-2 min-w-0">
|
|
3772
|
+
<div className="relative min-w-0">
|
|
3773
|
+
<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>
|
|
3774
|
+
<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>
|
|
3775
|
+
<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>
|
|
3776
|
+
</div>
|
|
3777
|
+
</section>
|
|
4520
3778
|
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
3779
|
+
${packs.length
|
|
3780
|
+
? html`
|
|
3781
|
+
<section className="space-y-2">
|
|
3782
|
+
<div className="rounded-2xl border border-slate-800 bg-slate-900/70 p-2.5">
|
|
3783
|
+
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
3784
|
+
<div>
|
|
3785
|
+
<p className="text-[11px] uppercase tracking-wide text-slate-400">Descobrir</p>
|
|
3786
|
+
<h3 className="text-sm font-semibold text-slate-100">Painel oficial do marketplace</h3>
|
|
3787
|
+
<p className="text-[11px] text-slate-500">Métricas globais reais da plataforma (cache ~${globalMarketplaceStats?.cacheSeconds || 45}s, atualização automática).</p>
|
|
3788
|
+
</div>
|
|
3789
|
+
<div className="flex items-center gap-2">
|
|
3790
|
+
${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}
|
|
3791
|
+
<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>
|
|
3792
|
+
</div>
|
|
3793
|
+
</div>
|
|
3794
|
+
|
|
3795
|
+
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
3796
|
+
${[
|
|
3797
|
+
{ key: 'growing', label: '🔥 Crescendo' },
|
|
3798
|
+
{ key: 'top', label: '🏆 Top 10' },
|
|
3799
|
+
{ key: 'creators', label: '⭐ Criadores' },
|
|
3800
|
+
].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> `)}
|
|
3801
|
+
</div>
|
|
3802
|
+
|
|
3803
|
+
<div className="mt-2 hidden lg:block">
|
|
3804
|
+
${discoverTab === 'growing'
|
|
3805
|
+
? 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> `
|
|
3806
|
+
: discoverTab === 'top'
|
|
3807
|
+
? 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> `
|
|
3808
|
+
: html`
|
|
3809
|
+
<div className="grid grid-cols-1 xl:grid-cols-2 gap-2">
|
|
3810
|
+
${featuredCreators.map(
|
|
3811
|
+
(creator) => html`
|
|
3812
|
+
<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">
|
|
3813
|
+
<img src=${getAvatarUrl(creator.publisher)} alt="" className="w-9 h-9 rounded-full bg-slate-800" />
|
|
3814
|
+
<span className="min-w-0 flex-1">
|
|
3815
|
+
<span className="block truncate text-xs font-medium text-slate-100">${creator.publisher}</span>
|
|
3816
|
+
<span className="block truncate text-[10px] text-slate-400">${creator.packCount} packs · ❤️ ${shortNum(creator.likes)} · ⬇ ${shortNum(creator.opens)}</span>
|
|
3817
|
+
</span>
|
|
3818
|
+
<span className="text-[10px] text-slate-500">filtrar</span>
|
|
3819
|
+
</button>
|
|
3820
|
+
`,
|
|
3821
|
+
)}
|
|
3822
|
+
</div>
|
|
3823
|
+
`}
|
|
3824
|
+
</div>
|
|
3825
|
+
|
|
3826
|
+
<div className="mt-2 lg:hidden">
|
|
3827
|
+
${discoverTab === 'growing'
|
|
3828
|
+
? html`
|
|
3829
|
+
<section className="space-y-1.5">
|
|
3830
|
+
<div className="flex items-center justify-between">
|
|
3831
|
+
<h4 className="text-xs font-semibold text-slate-200">🔥 Em alta agora</h4>
|
|
3832
|
+
<button type="button" onClick=${openTrendingCatalog} className="text-[10px] text-cyan-300">ver lista</button>
|
|
3833
|
+
</div>
|
|
3834
|
+
<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>
|
|
3835
|
+
</section>
|
|
3836
|
+
`
|
|
3837
|
+
: discoverTab === 'top'
|
|
3838
|
+
? html`
|
|
3839
|
+
<section className="space-y-1.5">
|
|
3840
|
+
<div className="flex items-center justify-between">
|
|
3841
|
+
<h4 className="text-xs font-semibold text-slate-200">🏆 Top 10 da semana</h4>
|
|
3842
|
+
<button type="button" onClick=${() => openCatalogWithState({ q: '', category: '', sort: 'trending', filter: '', push: true })} className="text-[10px] text-cyan-300">ver lista</button>
|
|
3843
|
+
</div>
|
|
3844
|
+
<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>
|
|
3845
|
+
</section>
|
|
3846
|
+
`
|
|
3847
|
+
: html`
|
|
3848
|
+
<section className="space-y-1.5">
|
|
3849
|
+
<div className="flex items-center justify-between">
|
|
3850
|
+
<h4 className="text-xs font-semibold text-slate-200">👑 Criadores populares</h4>
|
|
3851
|
+
<button type="button" onClick=${() => openCreatorsRanking('popular', true)} className="text-[10px] text-cyan-300">ver lista</button>
|
|
3852
|
+
</div>
|
|
3853
|
+
<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>
|
|
3854
|
+
</section>
|
|
3855
|
+
`}
|
|
3856
|
+
</div>
|
|
4543
3857
|
</div>
|
|
4544
|
-
</div>
|
|
4545
|
-
|
|
4546
|
-
<div className="mt-2 flex flex-wrap gap-1.5">
|
|
4547
|
-
${[
|
|
4548
|
-
{ key: 'growing', label: '🔥 Crescendo' },
|
|
4549
|
-
{ key: 'top', label: '🏆 Top 10' },
|
|
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>
|
|
4566
|
-
|
|
4567
|
-
<div className="mt-2 hidden lg:block">
|
|
4568
|
-
${discoverTab === 'growing'
|
|
4569
|
-
? html`
|
|
4570
|
-
<div className="grid grid-cols-1 xl:grid-cols-2 gap-2">
|
|
4571
|
-
${growingNowPacks.slice(0, 6).map(
|
|
4572
|
-
(entry) => html`<${DiscoverPackRowItem}
|
|
4573
|
-
key=${`grow-${entry.pack_key}`}
|
|
4574
|
-
pack=${entry}
|
|
4575
|
-
onOpen=${openPack}
|
|
4576
|
-
hasNsfwAccess=${hasNsfwAccess}
|
|
4577
|
-
onRequireLogin=${requestNsfwUnlock}
|
|
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>
|
|
4616
|
-
|
|
4617
|
-
<div className="mt-2 lg:hidden">
|
|
4618
|
-
${discoverTab === 'growing'
|
|
4619
|
-
? html`
|
|
4620
|
-
<section className="space-y-1.5">
|
|
4621
|
-
<div className="flex items-center justify-between">
|
|
4622
|
-
<h4 className="text-xs font-semibold text-slate-200">🔥 Em alta agora</h4>
|
|
4623
|
-
<button type="button" onClick=${openTrendingCatalog} className="text-[10px] text-cyan-300">ver lista</button>
|
|
4624
|
-
</div>
|
|
4625
|
-
<div className="flex gap-2 overflow-x-auto pb-1">
|
|
4626
|
-
${growingNowPacks.slice(0, 8).map(
|
|
4627
|
-
(entry) => html`<${DiscoverPackMiniCard}
|
|
4628
|
-
key=${`mobile-grow-${entry.pack_key}`}
|
|
4629
|
-
pack=${entry}
|
|
4630
|
-
onOpen=${openPack}
|
|
4631
|
-
hasNsfwAccess=${hasNsfwAccess}
|
|
4632
|
-
onRequireLogin=${requestNsfwUnlock}
|
|
4633
|
-
/>`,
|
|
4634
|
-
)}
|
|
4635
|
-
</div>
|
|
4636
|
-
</section>
|
|
4637
|
-
`
|
|
4638
|
-
: discoverTab === 'top'
|
|
4639
|
-
? html`
|
|
4640
|
-
<section className="space-y-1.5">
|
|
4641
|
-
<div className="flex items-center justify-between">
|
|
4642
|
-
<h4 className="text-xs font-semibold text-slate-200">🏆 Top 10 da semana</h4>
|
|
4643
|
-
<button
|
|
4644
|
-
type="button"
|
|
4645
|
-
onClick=${() => openCatalogWithState({ q: '', category: '', sort: 'trending', filter: '', push: true })}
|
|
4646
|
-
className="text-[10px] text-cyan-300"
|
|
4647
|
-
>
|
|
4648
|
-
ver lista
|
|
4649
|
-
</button>
|
|
4650
|
-
</div>
|
|
4651
|
-
<div className="flex gap-2 overflow-x-auto pb-1">
|
|
4652
|
-
${topWeekPacks.slice(0, 8).map(
|
|
4653
|
-
(entry) => html`<${DiscoverPackMiniCard}
|
|
4654
|
-
key=${`mobile-top-${entry.pack_key}`}
|
|
4655
|
-
pack=${entry}
|
|
4656
|
-
onOpen=${openPack}
|
|
4657
|
-
hasNsfwAccess=${hasNsfwAccess}
|
|
4658
|
-
onRequireLogin=${requestNsfwUnlock}
|
|
4659
|
-
/>`,
|
|
4660
|
-
)}
|
|
4661
|
-
</div>
|
|
4662
|
-
</section>
|
|
4663
|
-
`
|
|
4664
|
-
: html`
|
|
4665
|
-
<section className="space-y-1.5">
|
|
4666
|
-
<div className="flex items-center justify-between">
|
|
4667
|
-
<h4 className="text-xs font-semibold text-slate-200">👑 Criadores populares</h4>
|
|
4668
|
-
<button type="button" onClick=${() => openCreatorsRanking('popular', true)} className="text-[10px] text-cyan-300">ver lista</button>
|
|
4669
|
-
</div>
|
|
4670
|
-
<div className="flex gap-2 overflow-x-auto pb-1">
|
|
4671
|
-
${featuredCreators.map((creator) => html`
|
|
4672
|
-
<${DiscoverCreatorMiniCard}
|
|
4673
|
-
key=${`mobile-tab-creator-${creator.publisher}`}
|
|
4674
|
-
creator=${creator}
|
|
4675
|
-
onPick=${(publisher) => openCatalogWithState({ q: publisher, category: '', sort: DEFAULT_CATALOG_SORT, filter: '', push: true })}
|
|
4676
|
-
/>
|
|
4677
|
-
`)}
|
|
4678
|
-
</div>
|
|
4679
|
-
</section>
|
|
4680
|
-
`}
|
|
4681
|
-
</div>
|
|
4682
|
-
</div>
|
|
4683
3858
|
|
|
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>
|
|
3859
|
+
<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
3860
|
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
3861
|
+
<div className="lg:hidden space-y-2">
|
|
3862
|
+
<section className="space-y-1.5">
|
|
3863
|
+
<div className="flex items-center justify-between">
|
|
3864
|
+
<h4 className="text-xs font-semibold text-slate-200">🆕 Recém publicados</h4>
|
|
3865
|
+
<button type="button" onClick=${openSortPicker} disabled=${sortPickerBusy} className="text-[10px] text-cyan-300 disabled:opacity-50">ordenar</button>
|
|
3866
|
+
</div>
|
|
3867
|
+
<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>
|
|
3868
|
+
</section>
|
|
4705
3869
|
</div>
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
)}
|
|
3870
|
+
|
|
3871
|
+
<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">
|
|
3872
|
+
<div className="flex items-center justify-between gap-3">
|
|
3873
|
+
<div>
|
|
3874
|
+
<p className="text-xs font-semibold text-emerald-100">Quer aparecer em destaque?</p>
|
|
3875
|
+
<p className="text-[11px] text-slate-300">Publique seu pack e melhore capa/tags para ganhar mais cliques.</p>
|
|
3876
|
+
</div>
|
|
3877
|
+
<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>
|
|
3878
|
+
</div>
|
|
4716
3879
|
</div>
|
|
4717
3880
|
</section>
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
<
|
|
3881
|
+
`
|
|
3882
|
+
: null}
|
|
3883
|
+
${packs.length
|
|
3884
|
+
? html`
|
|
3885
|
+
<section className="space-y-3 min-w-0">
|
|
3886
|
+
<div className="flex items-end justify-between gap-3">
|
|
3887
|
+
<div>
|
|
3888
|
+
<h2 className="text-lg sm:text-xl font-bold">Packs</h2>
|
|
3889
|
+
<p className="text-xs text-slate-400">${sortedPacks.length}${packHasMore ? '+' : ''} resultados · ${categoryActiveLabel}</p>
|
|
3890
|
+
</div>
|
|
3891
|
+
<div className="hidden md:flex items-center gap-2">
|
|
3892
|
+
<span className="text-xs text-slate-400">Ordenar por</span>
|
|
3893
|
+
<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">
|
|
3894
|
+
<span>${catalogSortLabel(sortBy)}</span>
|
|
3895
|
+
<span className="text-[10px] text-slate-400">▾</span>
|
|
3896
|
+
</button>
|
|
3897
|
+
</div>
|
|
4725
3898
|
</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}
|
|
3899
|
+
<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>
|
|
3900
|
+
<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>
|
|
3901
|
+
</section>
|
|
3902
|
+
`
|
|
3903
|
+
: null}
|
|
3904
|
+
${packsLoading ? html`<${SkeletonGrid} count=${10} />` : null} ${!packsLoading && !hasAnyResult ? html`<${EmptyState} onClear=${clearFilters} />` : null}
|
|
4773
3905
|
|
|
4774
|
-
|
|
4775
|
-
|
|
3906
|
+
<section className="space-y-2.5">
|
|
3907
|
+
<div className="flex items-center justify-between">
|
|
3908
|
+
<h2 className="text-base sm:text-lg font-bold">Stickers sem pack</h2>
|
|
3909
|
+
<span className="text-xs text-slate-400">${orphans.length} resultados</span>
|
|
3910
|
+
</div>
|
|
4776
3911
|
|
|
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>
|
|
3912
|
+
${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> `}
|
|
3913
|
+
</section>
|
|
4781
3914
|
</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
|
-
`}
|
|
3915
|
+
</div>
|
|
3916
|
+
`}
|
|
4802
3917
|
</main>
|
|
4803
3918
|
<${UploadTaskWidget}
|
|
4804
3919
|
task=${uploadTask}
|
|
@@ -4809,55 +3924,11 @@ function StickersApp() {
|
|
|
4809
3924
|
setUploadTask(null);
|
|
4810
3925
|
}}
|
|
4811
3926
|
/>
|
|
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
|
-
/>
|
|
3927
|
+
<${CatalogSortPicker} open=${sortPickerOpen} currentSort=${sortBy} busy=${sortPickerBusy} onClose=${closeSortPicker} onSelect=${handleCatalogSortSelection} />
|
|
3928
|
+
<${PackActionsSheet} open=${Boolean(packActionsSheetPack)} pack=${packActionsSheetPack} busyAction=${packActionsSheetPack ? packActionBusyByKey?.[packActionsSheetPack.pack_key] || '' : ''} onClose=${closePackActionsSheet} onAction=${handlePackActionsSheetAction} />
|
|
3929
|
+
<${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)} />
|
|
3930
|
+
<${PackAnalyticsModal} open=${analyticsModalOpen} pack=${analyticsModalPack} data=${analyticsModalData} loading=${analyticsModalLoading} error=${analyticsModalError} onClose=${closeAnalyticsModal} />
|
|
3931
|
+
<${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
3932
|
<${ToastStack} toasts=${profileToasts} onDismiss=${dismissProfileToast} />
|
|
4862
3933
|
</div>
|
|
4863
3934
|
`;
|