Pular para o conteúdo principal

State Management

O template usa Zustand para estado client-side (15 stores) e React Router loaders para dados server-side.

Zustand Stores

auth.ts

interface AuthState {
user: AuthUser | null;
userInfo: AuthUserInfo | null;
isAuthenticated: boolean;
hydrated: boolean;
authModal: 'login' | 'register' | null;

setUser(user: AuthUser, userInfo: AuthUserInfo): void;
clearAuth(): void;
setHydrated(): void;
openAuthModal(modal: 'login' | 'register' | null): void;
}

Principais ações:

  • setUser() — salva user + userInfo, marca isAuthenticated: true
  • clearAuth() — limpa user/userInfo, marca não autenticado, também chama useWalletStore.getState().clearWallet() (side effect cross-store)
  • setHydrated() — indica fim do bootstrap inicial
  • openAuthModal() — controla qual modal está aberto

layout.ts

interface LayoutState {
mobileMenuOpen: boolean;
sidebarOpen: boolean;
sectionToggles: Record<string, boolean>;

setMobileMenuOpen(open: boolean): void;
toggleMobileMenu(): void;
setSidebarOpen(open: boolean): void;
toggleSidebar(): void;
setSectionToggle(key: string, open: boolean): void;
toggleSection(key: string): void;
}

Controla estado de navegação: menu mobile, sidebar colapsável e seções expansíveis do sidebar.

brand.ts

interface BrandState {
brand: BrandConfig | null;
setBrand(brand: BrandConfig): void;
}

Armazena a configuração de marca carregada pelo loader. Usado para acesso global via useBrandStore().

gamification.ts

interface GamificationState {
sdkReady: boolean;
sdkIdentified: boolean;
profile: UserProfile | null;
missions: Mission[];
badges: Mission[];
tournaments: Tournament[];
storeItems: StoreItem[];
miniGames: MiniGameTemplate[];
levels: Level[];
currentLevel: LevelCurrent | null;
bonuses: Bonus[];
jackpots: JackpotDetails[];
raffles: Raffle[];
inboxUnread: number;
translations: Record<string, string>;

// Setters para cada campo
setSdkReady(ready: boolean): void;
setSdkIdentified(identified: boolean): void;
setProfile(profile: UserProfile | null): void;
setMissions(missions: Mission[]): void;
setBadges(badges: Mission[]): void;
setTournaments(tournaments: Tournament[]): void;
setStoreItems(items: StoreItem[]): void;
setMiniGames(games: MiniGameTemplate[]): void;
setLevels(levels: Level[]): void;
setCurrentLevel(level: LevelCurrent | null): void;
setBonuses(bonuses: Bonus[]): void;
setJackpots(jackpots: JackpotDetails[]): void;
setRaffles(raffles: Raffle[]): void;
setInboxUnread(count: number): void;
setTranslations(translations: Record<string, string>): void;
clear(): void;
}

Principais ações:

  • setSdkReady() — SDK Smartico inicializado
  • setSdkIdentified() — usuário identificado no Smartico
  • setTranslations() — traduções i18n do Smartico (resolvem {{label.key}})
  • clear() — volta ao estado inicial (usado no cleanup/logout)

kyc.ts

interface KycState {
open: boolean;
source: string;
recoveryToken: string | null;
kycId: number | null;
operator: KycOperatorName | null;
iframeUrl: string;
flow: KycFlowType;
step: KycVerificationStep;
isKycLoading: boolean;
isCheckStatusLoading: boolean;
isPollingRunning: boolean;
error: string | null;
autoStart: boolean;
reloadOnValidated: boolean;
onValidated: ((payload?) => void) | null;
loadingMessageIndex: number;

// Setters para cada campo
setOpen(open: boolean): void;
setSource(source: string): void;
setRecoveryToken(token: string | null): void;
setKycId(id: number | null): void;
setOperator(operator: KycOperatorName | null): void;
setIframeUrl(url: string): void;
setFlow(flow: KycFlowType): void;
setStep(step: KycVerificationStep): void;
setIsKycLoading(loading: boolean): void;
setIsCheckStatusLoading(loading: boolean): void;
setIsPollingRunning(running: boolean): void;
setError(error: string | null): void;
setAutoStart(auto: boolean): void;
setReloadOnValidated(reload: boolean): void;
setOnValidated(cb: ((payload?) => void) | null): void;
setLoadingMessageIndex(index: number): void;
}

Estado completo do fluxo KYC. Ver KYC para detalhes.

validationSteps.ts

interface ValidationStepsState {
open: boolean;
context: ValidationContextName | null;
currentModuleIndex: number;
modules: ValidationModuleName[];
snapshot: ValidationSnapshot | null;
onContinue: ((payload?) => void) | null;
pendingType: ValidationPendingType | null;

// Setters
setOpen(open: boolean): void;
setContext(context: ValidationContextName | null): void;
setCurrentModuleIndex(index: number): void;
setModules(modules: ValidationModuleName[]): void;
setSnapshot(snapshot: ValidationSnapshot | null): void;
setOnContinue(cb: ((payload?) => void) | null): void;
setPendingType(type: ValidationPendingType | null): void;
openValidationStepsModal(payload): void;
closeValidationStepsModal(): void;
}

Controla o modal de validações frequentes/contextuais (deposit, withdraw, etc.).

validationRuntime.ts

interface ValidationRuntimeState {
allValidations: FetchAllValidationsResult | null;
setAllValidations(result: FetchAllValidationsResult): void;
clearAllValidations(): void;
}

Armazena o resultado computado de fetchAllValidations(). Sincronizado pelo hook useValidationRuntimeSync no AuthInitializer.

passwordValidation.ts

interface PasswordValidationState {
open: boolean;
loading: boolean;
error: string | null;
callback: ((payload?) => void) | null;

setOpen(open: boolean): void;
setLoading(loading: boolean): void;
setError(error: string | null): void;
setCallback(cb: ((payload?) => void) | null): void;
openPasswordValidationModal(options): void;
closePasswordValidationModal(): void;
}

Modal standalone de validação de senha (usado quando password é o único módulo pendente).

games.ts

interface GamesState {
// Dados de home/catálogo
homeRows: HomeRow[];
categories: Category[];
providers: Provider[];
topWins: Win[];
lastWins: Win[];

// Busca
searchOpen: boolean;
searchTerm: string;
searchResults: Game[];
searchTotal: number;
searchLoading: boolean;

// Jogo ativo
activeGame: Game | null;
gameStats: GameStats | null;
gameIframe: string | null;
gameIframeLoading: boolean;
userVote: boolean | null;
voteCount: VoteCount | null;

// Métodos de hydrate (sincroniza dados do loader SSR)
hydrate(data: Partial<GamesState>): void;
// Actions para busca, jogo ativo, votos, etc.
setSearchOpen(open: boolean): void;
setSearchTerm(term: string): void;
setSearchResults(results: Game[], total: number): void;
setSearchLoading(loading: boolean): void;
setActiveGame(game: Game | null): void;
setGameStats(stats: GameStats | null): void;
setGameIframe(url: string | null): void;
setGameIframeLoading(loading: boolean): void;
setUserVote(vote: boolean | null): void;
setVoteCount(count: VoteCount | null): void;
}

Store central de jogos/cassino. Dados iniciais vêm do loader SSR via hydrate(), ações do client atualizam busca e jogo ativo.

payments.ts

interface PaymentsState {
paymentModal: 'deposit' | 'withdraw' | null;
providers: PaymentProvider[];
providersLoading: boolean;
selectedDepositMethod: PaymentMethod | null;
selectedWithdrawMethod: PaymentMethod | null;
depositLoading: boolean;
withdrawLoading: boolean;
coupon: string;

// Setters para cada campo
setPaymentModal(modal: 'deposit' | 'withdraw' | null): void;
setProviders(providers: PaymentProvider[]): void;
setProvidersLoading(loading: boolean): void;
setSelectedDepositMethod(method: PaymentMethod | null): void;
setSelectedWithdrawMethod(method: PaymentMethod | null): void;
setDepositLoading(loading: boolean): void;
setWithdrawLoading(loading: boolean): void;
setCoupon(coupon: string): void;
}

Controla modais e estado de depósito/saque.

wallet.ts

interface WalletState {
wallet: Wallet | null;
walletLoading: boolean;
transactions: Transaction[];
transactionsMeta: PaginationMeta | null;
transactionsLoading: boolean;
transactionsFilter: TransactionsFilter;
rollover: Rollover | null;
walletModal: 'deposit' | 'withdraw' | null;

// Setters
setWallet(wallet: Wallet | null): void;
setWalletLoading(loading: boolean): void;
setTransactions(txs: Transaction[], meta: PaginationMeta): void;
setTransactionsLoading(loading: boolean): void;
setTransactionsFilter(filter: TransactionsFilter): void;
setRollover(rollover: Rollover | null): void;
setWalletModal(modal: 'deposit' | 'withdraw' | null): void;
clearWallet(): void;
}

Estado da carteira do usuário. clearWallet() é chamado pelo clearAuth() da auth store (side effect cross-store).

user.ts

interface UserState {
clearUserData(): void;
}

Store mínima para limpeza de dados do usuário. Lógica principal de perfil fica no loader SSR e nos services.

accountMenu.ts

interface AccountMenuState {
currentGroup: string | null;
setCurrentGroup(group: string | null): void;
}

Controla qual grupo/seção está ativo no menu da conta do usuário.

recovery.ts

type RecoveryStep =
| "document"
| "options"
| "email"
| "sms"
| "kyc"
| "new-password"
| "success";

interface RecoveryState {
open: boolean;
step: RecoveryStep;
isLoading: boolean;
error: string | null;
token: string | null; // token temporario do fluxo de recuperacao
maskedEmail: string | null; // ex: "p*****@email.com"
maskedPhone: string | null; // ex: "7766" (ultimos digitos)

openRecoveryModal(): void;
closeRecoveryModal(): void;
setStep(step: RecoveryStep): void;
setFlowData(data: { token: string; email: string; final_phone: string }): void;
setLoading(loading: boolean): void;
setError(error: string | null): void;
reset(): void;
}

Controla o fluxo do modal de recuperação de senha. Os dados sensíveis transitam apenas server-side via HttpOnly cookie — code e kyc_id não são armazenados nesta store.

topbar.ts

interface TopbarState {
activeConfig: TopbarNotificationConfig | null; // notificacao ativa (ou null)
initialized: boolean; // true apos init() ser chamado
queue: TopbarNotificationConfig[]; // fila de notificacoes da brand

init(notifications: TopbarNotificationConfig[]): void;
dismiss(type: TopbarNotificationType, reason: "close" | "cta"): void;
}

Gerencia a topbar de notificações inteligente. A init() avalia predicados shouldShow e estado de cookie/sessionStorage para determinar a primeira notificação elegível. O dismiss() registra o fechamento (cookie 7 dias ou sessionStorage para "close", cookie 1 ano para "cta") e avança para a próxima elegível na fila.

React Router Loaders (server-side)

Dados que vêm do server (brand, clientEnv, auth profile) são carregados via loader e passados via props/context. Não ficam no Zustand.

// _layout.tsx
export async function loader({ context, request }: Route.LoaderArgs) {
const result = await loadBrandConfig(env);
const clientEnv = { /* 17 campos */ };
const auth = await loadAuthProfile(request, env);
const smarticoHash = generateSmarticoUserHash(...);
return { brand, clientEnv, auth, smarticoHash };
}

Padrão de acesso

  • Server data (brand, config): useBrand(), useClientEnv() — via React Context
  • Client state (auth, layout, games): useAuthStore(), useLayoutStore(), useGamesStore() — via Zustand
  • Financeiro: useWalletStore(), usePaymentsStore() — via Zustand
  • Gamificação: useGamificationStore() — via Zustand (dados do Smartico SDK)
  • Validações: useValidationRuntimeStore() — resultado global, useValidationStepsStore() — modal contextual
  • KYC: useKycStore() — fluxo completo de verificação
  • Modais: controlados por stores dedicadas — qualquer componente pode abrir/fechar
  • Navegação: useLayoutStore() — sidebar, menu mobile, seções expansíveis
  • Menu da conta: useAccountMenuStore() — grupo ativo