Pular para o conteúdo principal
← Voltar ao changelog

Changelog - 28/04/2026

Sistema de Ícones — migração lucide-react → unplugin-icons

  • lucide-react removido por completo e substituído por unplugin-icons apoiado em três datasets Iconify (@iconify-json/lucide, @iconify-json/simple-icons, @iconify-json/mdi) mais uma coleção custom lida via FileSystemIconLoader de app/icons/custom/. Cada ícone agora resolve para um componente SVG inline em build-time — zero fetch em runtime, SSR-safe no Cloudflare Workers, e o chunk manual vendor-lucide foi descontinuado.
  • API de uso por dataset: import Trophy from "~icons/lucide/trophy" para ícones genéricos de UI, ~icons/simple-icons/<name> para logos de marca (WhatsApp, Facebook, X, Instagram, TikTok), ~icons/mdi/<name> para esportes e equivalentes a emojis, e ~icons/custom/<name> para SVGs próprios em app/icons/custom/ (auto-discovered). Naming kebab-case obrigatório (AlarmClockalarm-clock, XCirclex-circle).
  • Tipo IconComponent substitui LucideIcon em todo o codebase via app/types/icon.ts (superset de SVGProps<SVGSVGElement> com alias legacy de size). unplugin-icons não suporta a prop size do Lucide — codemod scripts/migrate-icon-size-prop.mjs converteu 471 ocorrências de size={X} para width={X} height={X}.
  • Registry string-driven em app/icons/registry.ts para configs que referenciam ícones por string (ex: icon: "mdi:soccer") consumidos via <Icon name="..." /> ou <SmartIcon icon={...} /> (smart dispatch que renderiza o ícone registrado ou cai no texto literal — preserva configs legados com emoji).
  • Exceções aceitáveis para SVG inline: spinners/loaders animados customizados, checkmarks de success animados, indicadores visuais brand-specific que não existem em nenhum dataset Iconify, e seals de regulação (ANJL, Anatel, Compulsafe) servidos via <img> apontando para public/assets/seals/.
  • Migração automatizada via codemods em scripts/: migrate-lucide-to-unplugin-icons.mjs (converte imports nomeados em default imports kebab-case, ordenado via biome check --write), migrate-icon-size-prop.mjs (size → width/height em JSX) e migrate-emoji-icons.mjs (emoji literais → IconName registrado). Fix posterior em toKebabCase para acrônimos consecutivos (XCirclex-circle, antes virava xcircle quebrado).

Arquitetura de Layout — Shell Architecture

  • DefaultLayout deixa de ser monolítico e vira um dispatcher que delega o macro-arrangement do viewport a um componente Shell resolvido via layoutConfig.shell. Dois shells nascem prontos: header-top (default — header full-width sticky no topo, comportamento atual de todas as brands) e split-shell (sidebar full-height à esquerda do viewport, topbar + header + main + footer empilhados na coluna direita — replica o 7k.bet.br produção).
  • Quatro camadas independentes e ortogonais na composição do layout: shell (macro-arrangement), structure (colunas internas + flags on/off), slots (variant de cada peça: header, sidebar, footer, rightPanel, banner) e componentVariants (variants globais de primitivas compartilhadas). Trocar uma camada não invalida as outras — cl-bet7k-com adota split-shell mantendo slots.sidebar = "sidebar-narrow" e structure.columns = "left-main".
  • Sidebars shell-agnósticas via CSS custom properties (--sidebar-top, --sidebar-h, --sidebar-w-collapsed, --sidebar-w-expanded) publicadas pelo shell ativo — qualquer variant funciona dentro de qualquer shell sem fork.
  • Mobile permanece universal: independente do shell, sidebar vira drawer overlay e header sticky no topo. Shell só altera comportamento em lg+.
  • sidebar-accordion inspirada no layout legado 7k entra como terceira opção (junto com sidebar-narrow default e sidebar-wide). Header h-[72px] com logo + toggle expand/collapse, widget SidebarButtons no topo, seções full-width como acordeões (chevron rotativo, body com grid-template-rows: 0fr → 1fr para altura auto animada) e bloco de bottom items abaixo, separados por border-t full-width.
  • Comportamento route-aware: um useEffect([pathname]) abre casino e fecha sports ao navegar para rota de cassino, e o inverso para esportes. Home e rotas neutras preservam a escolha manual.
  • <SidebarPillItem> compartilhado com três modos (link / external / action) e visual gradient pill alinhado ao widget SidebarButtons — renderiza accordion items, grupos aninhados (popular, top-sports) e bottom items (promotions, help center, suporte ao vivo, referral, install app, blog, FAQ).

Widgets Configuráveis (top-games, home-banner, game-card, provider-card)

  • Top-games migrado para app/widgets/top-games/ seguindo a convenção do sidebar-buttons. Tipos centralizados em app/types/top-games.ts (TopGamesRankStyle, TopGamesDecoration discriminado none | asset, TopGamesConfig, TopGamesVariantProps) e config única em app/config/widgets/top-games.ts overrideable por brand via file-replacement. Nova variant corner-diagonal replica o widget legado 7k mantendo brand-awareness via theme tokens.
  • Home-banner extraído para app/widgets/home-banner/ com config HomeBannerConfig brand-overrideable. Variant single-fade (default, reusa ~/components/ui/Slideshow com 1 slide, fade, autoplay 6s, preserva o visual pré-refactor) e multi-scroll (reusa ~/components/ui/Carousel com N cards de largura fixa e scroll-snap). Dimensões propagadas via CSS custom properties consumidas por classes Tailwind arbitrary literais (w-[var(--hb-w-d)]) — JIT escaneia, runtime substitui.
  • GameCard virou config-driven dispatcher (em vez de monolito de 305 linhas) com duas variants: classic (default) e stacked. Componente permanece em app/components/games/ (é primitive de domínio usado em ~9 surfaces — mover causaria churn de imports sem ganho). Tipos em app/types/game-card.ts, config em app/config/widgets/game-card.ts, variant key declarada via componentVariants.gameCard no layoutConfig.
  • ProviderCard extraído do inline em ProvidersCarousel para dispatcher com variants classic (default) e logo-only. Mesma estrutura: tipos em app/types/provider-card.ts, config em app/config/widgets/provider-card.ts, variant key em componentVariants.providerCard.
  • Cards de detalhe de jogo (GameStats, GameWinners) ganham per-brand decoration + gradient config via opt-in fields — sem variant system nem reorg estrutural; defaults preservam o look atual de todas as brands.

Reorganização de Configs

  • features.ts dividido por domínio: novo app/config/features/account.ts agrupa as 5 flags account-scoped (accountSetLimits, accountTimeoutLimits, pixEvpEnabled, redeemableRewards, userPreferences); novo app/config/features/auth.ts agrupa as 5 flags do fluxo de auth (registro + password recovery). Padrão para evitar inflar features.ts quando um domínio cresce.
  • Notificações consolidadas em app/config/widgets/: lista ordenada de tipos ativos sai de featuresConfig.topbarNotifications / .bottomNotifications e passa a viver junto com a metadata e parâmetros em widgets/topbar-notifications.ts / widgets/bottom-notifications.ts — fim do double source-of-truth.
  • Home-leagues vira widget config-driven com variants default (round avatars, agora prefere iconName brand quando declarado, country flag como fallback gracioso) e square (cards 104×144 verticais replicando o layout legado 7k — região superior diagonal com gradient, label title/subtitle em duas linhas, brand-aware via theme tokens).

Performance — Home Cold-Start

  • Loader da home filtra de getAllGames() em vez de fan-out de N chamadas paralelas getDetail para resolver favoritos. allGames já é cacheado como entry quente; 1 cache hit + Map.get por slug substitui até 30 round-trips ao BFF. Mesma migração aplicada em /favorites. FAVORITE_GAMES_RESOLVE_LIMIT na home reduzido de 30 → 15 para casar com o card count visível em FavoritesRow.
  • Sugestões para FavoritesRow e RecentlyPlayed agora anon-gated. As duas rows são login-gated no render, mas o loader as computava para todos quando rowSuggestionsInEmptyStates estava true — anon SSR pagava ~200-500ms cold e ~10-20KB de payload descartado. Gate explícito.
  • /api/games/by-slugs filtra de allGames em vez de fan-out de getDetail por slug — mesmo padrão da migração do loader.
  • Icon registry split em duas etapas (keys-only + Icon lazy) corta SectionTitle de 142KB para 5.6KB. O registry estaticamente importava 60+ componentes SVG; qualquer um que importasse <SmartIcon> (primitive usada em 14+ componentes da home/cassino) arrastava o registry inteiro para o chunk graph.
  • tailwind.config.js corrigido para incluir overrides/**/app/** no content scan — classes Tailwind usadas só em arquivos de override deixam de ser purgadas em prod.
  • Bundle analyzer (ANALYZE=1) + chunk warnings mais apertados + lighthouserc adicionados para gating de regressão de performance.
  • @cactus-agents/platform-cache bumpado para 0.11.0 com KV snapshots para warm tier cross-datacenter, single-flight coalescing e cache_decision logging estruturado.

Brand Overrides

  • cl-bet7k-com: ativação completa do tema legado 7k — split-shell com sidebar widths legacy, sidebar-accordion variant, header stacked com subheader route-aware, top-games corner-diagonal, home-leagues square com glyphs próprios (20 logos de liga + 3 sport glyphs importados do legado, mais SVG da Primera División chilena), provider-card logo-only, game-card stacked, decoration brandizada em GameStats e GameWinners, home-banner multi-scroll, content max-width alinhado ao 7k produção (1440px), search row da home desabilitada, topbar notifications desativadas, e migração de configs com emoji literais para ícones MDI vetoriais.
  • 7k-bet-br: replica do layout/tema cl-bet7k-com aplicada à brand BR — mesmo conjunto de variants e configs. Adicionado FeaturedGameWidget exclusivo (Aviator 7K com preview animado em GIF, thumbnail desktop opcional, badge "EXCLUSIVO" SVG, ícone custom:destaque-aviator, slug banana/aviator-7k) inserido após a row de top-games em home-rows.legacy.ts. Search row da home desabilitada. CDN-CGI image proxy temporariamente desativado até stage worker ser configurado.
  • betpontobet-bet-br: mantém ícones emoji intencionalmente em row e catalog configs (não migrou para MDI vetorial — decisão de brand).

Core / SDK

  • @cactus-agents/gamification ganha campo opcional smartico.exclusiveTournaments?: string[] em GamificationConfig. IDs listados são tratados como invite-only — filtrados da lista pública e exibidos apenas quando a URL carrega ?exclusive, ?premium, ?telegram ou ?whatsapp. Originalmente uma feature 7k-only do legado Vue (env SMARTICO_EXCLUSIVE_TOURNAMENTS), promovida ao SDK para qualquer brand Smartico-backed poder optar. Backward compatible (campo opcional).
  • @cactus-agents/platform-cache 0.11.0 + @cactus-agents/gamification bumpados nas dependências do base.

Docs / Infra

  • Nova doc architecture/icon-system.md cobrindo a migração lucide-react → unplugin-icons, datasets disponíveis, naming kebab-case, registry string-driven, exceções aceitáveis para SVG inline e troubleshooting de codemods.
  • Nova doc architecture/performance-psi.md com requisito duro de PSI (LCP, FCP, CLS, TBT) para mudanças visuais above-the-fold, checklist de CLS = 0, padrões de LCP image, valores dinâmicos via CSS custom properties + Tailwind arbitrary literal, e regra "zero carousel libs" (estender Slideshow.tsx ou Carousel.tsx em app/components/ui/).
  • architecture/layout-composition.md expandido com a seção sobre as quatro camadas independentes (shell, structure, slots, componentVariants), descrições detalhadas dos shells header-top e split-shell, e papel de cada camada na composição.
  • Sidebar interna ganha entradas para icon-system e performance-psi.