Pular para o conteúdo principal

User Account

Gerenciamento de conta do usuario: perfil, seguranca, historico de login, protecao (responsible gaming) e IRPF via @cactus-agents/user.

Arquivos principais

ArquivoTipoDescricao
store/auth.tsStore (Zustand)Auth state (user, userInfo, isAuthenticated)
services/user.client.tsServiceChamadas client-side para API routes
services/userLimits.client.tsServiceLimites de responsible gaming
hooks/useAccountMenuItems.tsHookMenu dinamico da conta (country + brand aware)
config/account-menu.config.tsConfigDefinicao estatica dos items do menu (fork-overridable)

O menu e construido dinamicamente pelo hook useAccountMenuItems():

import { useAccountMenuItems } from "~/hooks/useAccountMenuItems";

function UserAccountMenu() {
const items = useAccountMenuItems(); // filtrado por pais + brand features
// ...
}

Filtros aplicados

CondicaoComportamento
legal.hasIncomeTaxReport === falseItem "IRPF" oculto (ex: CHL, MEX)
features.accountSetLimits === falseItem "Protecao da conta" sem subsecao de limites
features.accountTimeoutLimits === falseItem "Protecao da conta" sem pausa/autoexclusao
item.condition === falseItem oculto (mecanismo legado de config estatica)

Grupos e items

GrupoItems
PerfilDados pessoais, Seguranca, Historico de login, Protecao
SaldoCarteira, Deposito (action), Saque (action), Transacoes
HistoricoApostas casino, Apostas esportes (badge "Em breve")
IRPFInforme de rendimentos (oculto quando hasIncomeTaxReport: false)
SairLogout (action)

Paginas

RotaDescricao
/user/accountDados pessoais, secao bancaria por pais, contratos, marketing
/user/securitySenha, 2FA, contas sociais
/user/login-historyHistorico de logins (IP, localizacao, data)
/user/account-protectionResponsible gaming — feature-flagged pela API
/user/irpfInforme de rendimentos — guard: redireciona paises sem suporte
/user/notificationsCentral de notificacoes (inbox Smartico)
/user/walletCarteira (ver Wallet)
/user/validate/:type/:tokenValidacao via link (email, KYC)

Todas as paginas /user/* sao protegidas por AuthGuard no layout.

Componentes

Conta (components/user/account/)

  • InfosSection — nome, data de nascimento, genero, username
  • DocumentSection — documento (CPF, RUT, etc.)
  • EmailSection — email com verificacao
  • PhoneSection — telefone com verificacao SMS
  • AddressSection — endereco completo com estados (por pais)
  • ContractsSection — termos aceitos, dinamico: usa legal.requiredTerms do country-config (CHL ve apenas tc+privacy; BRA ve tc+privacy+lgpd+law)
  • MarketingSection — preferencias de comunicacao

Secao bancaria (por pais)

Renderizada em account.tsx condicionada por useCountry().payments.bankAccountSection:

ValorComponentePais
"pix"PixSectionBRA — chave Pix via /api/payments/bank-account
"clabe"ClabeSectionMEX — conta CLABE via /api/payments/bank-account
"chl-banks"ChlBanksSectionCHL — conta bancaria generica via /api/payments/bank-account
null(nenhum)Outros paises

A secao bancaria nao e mostrada se bankAccountSection for null.

Seguranca (components/user/security/)

  • PasswordSection — alterar senha (senha atual + nova)
  • TwoFactorSection — ativar/desativar 2FA (SMS ou email)
  • SocialAccountsSection — contas vinculadas; filtrada por features.socialAuth.* — so exibe providers habilitados para a brand

Protecao / Responsible Gaming (components/user/protection/)

A pagina de protecao e controlada por feature flags vindas da API (/bff/features):

Feature FlagSecoes visiveis
features.accountSetLimits === trueLimite deposito, Aposta, Perda, Tempo
features.accountTimeoutLimits === truePausa temporaria, Autoexclusao
Ambas falseMensagem "funcionalidade indisponivel nesta regiao"

Componentes:

  • DepositLimitSection — limite de deposito; pre-popula com valores atuais de userInfo
  • BetLimitSection — limite de aposta; pre-popula com userInfo
  • LossLimitSection — limite de perda; pre-popula com userInfo
  • TimeLimitSection — limite de tempo de sessao; pre-popula com userInfo; converte ISO 8601 via isoDurationToHours() do SDK
  • TimeoutSection — pausa temporaria; opcoes definidas por TIMEOUT_DAYS_OPTIONS do SDK; permite extensao quando pausa ativa (filtra opcoes via filterTimeoutOptions)
  • SelfExclusionSection — autoexclusao; opcoes definidas por SELF_EXCLUSION_MONTHS_OPTIONS do SDK; permite extensao quando exclusao ativa nao-permanente (filtra opcoes via filterSelfExclusionOptions)
  • ExclusionAlternatives — exibido antes da confirmacao de autoexclusao quando o motivo selecionado tem hasAlternative: true; sugere pausa temporaria ou limites como alternativas menos restritivas
  • AlertOnlyWithdraw — alerta quando conta esta restrita; detecta tipo (pausa, autoexclusao, operador, SPA) e exibe data de termino quando disponivel
  • LimitAccordion — UI compartilhada (accordion com indicador verde "Ativo" quando limite esta configurado)

:::info Constantes e helpers de responsible gaming Todas as constantes (TIMEOUT_DAYS_OPTIONS, SELF_EXCLUSION_MONTHS_OPTIONS) e helpers (parseLimitPeriod, isoDurationToHours, isPermanentExclusion, filterSelfExclusionOptions, filterTimeoutOptions) vem de @cactus-agents/user. O template nunca redefine essas regras de negocio. :::

Extensao de restricoes ativas

Quando o usuario ja tem uma autoexclusao ou pausa ativa, ele pode estender o periodo (selecionar uma duracao maior). Nao e possivel reduzir ou remover a restricao ativa.

EstadoSelfExclusionSectionTimeoutSection
InativoForm completo — todas as opcoesForm completo — todas as opcoes
Self-exclusion ativa (nao permanente)Alerta + form com opcoes filtradas (>= minimo)N/A
Pausa ativaN/AAlerta + form com opcoes filtradas (>= minimo)
Permanente (99 ou -1)Alerta — form ocultoN/A
Operator exclusionAlerta — form ocultoN/A
SPA exclusionAlerta — form ocultoN/A
Pausa ativa sem opcoes disponiveisN/AAlerta + mensagem "periodo maximo atingido"

A filtragem usa as funcoes puras do SDK:

import { filterSelfExclusionOptions, filterTimeoutOptions } from "@cactus-agents/user";

// userInfo vem do store; as funcoes aceitam null/undefined com seguranca
const availableExclusionOptions = filterSelfExclusionOptions(userInfo);
const availableTimeoutOptions = filterTimeoutOptions(userInfo);

O botao de submit muda o label automaticamente: "Ativar" quando nenhuma restricao esta ativa, "Estender" quando ja existe uma restricao ativa.

:::caution Restricoes impostas externamente Quando a restricao foi imposta por operador ou SPA (autoridade reguladora), o usuario nao pode modificar — apenas o alerta e exibido. A logica de deteccao usa isAccountRestricted() e getAccountRestriction() do SDK. :::

Header/Menu

  • HeaderUserArea — area do usuario logado (avatar, nome, saldo)
  • UserBox — box compacto do usuario
  • UserAccountMenu — sidebar menu dinamico (usa useAccountMenuItems())
  • UserPageTitle — titulo das paginas da conta

API Routes

RotaMetodoDescricao
api/user/login-historyGETHistorico de logins (com transform normalizado via SDK)
api/user/update-limitsPOST/PATCHLimites de responsible gaming
api/user/timeout-limitsPOSTPausa temporaria
api/user/self-exclusionPOSTAutoexclusao
api/user/update-addressPOSTAtualiza endereco
api/user/check-passwordPOSTVerifica senha atual
api/user/add-phonePOSTAdiciona telefone
api/auth/profileGETPerfil completo do usuario
api/payments/bank-accountPOSTConta bancaria (PIX / CLABE / CHL) — usa PaymentsService

:::note Padrao de proxy Todas as rotas api/* sao proxies server-side. Leem o JWT do cookie HttpOnly, delegam ao servico SDK correspondente, e retornam dados ja transformados. Nenhuma logica de negocio fica no route handler. :::

IRPF

A pagina /user/irpf tem guard de pais:

// routes/user/irpf.tsx
const { legal } = useCountry();
if (!legal.hasIncomeTaxReport) {
return <Navigate to={routeHref("user.account")} replace />;
}

Paises com hasIncomeTaxReport: false (CHL, MEX, PER, FIN, XYZ) sao redirecionados automaticamente. O item do menu tambem fica oculto via useAccountMenuItems().

Para paises com suporte (atualmente BRA), o fluxo usa polling via useIncomeReportPolling():

  1. Usuario seleciona ano e formato (PDF/email)
  2. Request gera o informe no backend
  3. Polling ate o status mudar para completed
  4. Download do PDF ou confirmacao de envio por email

Contratos por pais

ContractsSection usa legal.requiredTerms do country-config:

// Nao tem if(country === "BRA")
// Usa o que o SDK define para o pais configurado
const { legal } = useCountry();
// legal.requiredTerms: ["tc", "privacy", "lgpd", "law"] para BRA
// ["tc", "privacy"] para CHL

Quando features.userMigration === true, o termo "migrate" e adicionado automaticamente.