User Account
Gerenciamento de conta do usuario: perfil, seguranca, historico de login, protecao (responsible gaming) e IRPF via @cactus-agents/user.
Arquivos principais
| Arquivo | Tipo | Descricao |
|---|---|---|
store/auth.ts | Store (Zustand) | Auth state (user, userInfo, isAuthenticated) |
services/user.client.ts | Service | Chamadas client-side para API routes |
services/userLimits.client.ts | Service | Limites de responsible gaming |
hooks/useAccountMenuItems.ts | Hook | Menu dinamico da conta (country + brand aware) |
config/account-menu.config.ts | Config | Definicao estatica dos items do menu (fork-overridable) |
Menu da conta — dinamico por pais e por brand
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
| Condicao | Comportamento |
|---|---|
legal.hasIncomeTaxReport === false | Item "IRPF" oculto (ex: CHL, MEX) |
features.accountSetLimits === false | Item "Protecao da conta" sem subsecao de limites |
features.accountTimeoutLimits === false | Item "Protecao da conta" sem pausa/autoexclusao |
item.condition === false | Item oculto (mecanismo legado de config estatica) |
Grupos e items
| Grupo | Items |
|---|---|
| Perfil | Dados pessoais, Seguranca, Historico de login, Protecao |
| Saldo | Carteira, Deposito (action), Saque (action), Transacoes |
| Historico | Apostas casino, Apostas esportes (badge "Em breve") |
| IRPF | Informe de rendimentos (oculto quando hasIncomeTaxReport: false) |
| Sair | Logout (action) |
Paginas
| Rota | Descricao |
|---|---|
/user/account | Dados pessoais, secao bancaria por pais, contratos, marketing |
/user/security | Senha, 2FA, contas sociais |
/user/login-history | Historico de logins (IP, localizacao, data) |
/user/account-protection | Responsible gaming — feature-flagged pela API |
/user/irpf | Informe de rendimentos — guard: redireciona paises sem suporte |
/user/notifications | Central de notificacoes (inbox Smartico) |
/user/wallet | Carteira (ver Wallet) |
/user/validate/:type/:token | Validacao 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, usernameDocumentSection— documento (CPF, RUT, etc.)EmailSection— email com verificacaoPhoneSection— telefone com verificacao SMSAddressSection— endereco completo com estados (por pais)ContractsSection— termos aceitos, dinamico: usalegal.requiredTermsdo 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:
| Valor | Componente | Pais |
|---|---|---|
"pix" | PixSection | BRA — chave Pix via /api/payments/bank-account |
"clabe" | ClabeSection | MEX — conta CLABE via /api/payments/bank-account |
"chl-banks" | ChlBanksSection | CHL — 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 porfeatures.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 Flag | Secoes visiveis |
|---|---|
features.accountSetLimits === true | Limite deposito, Aposta, Perda, Tempo |
features.accountTimeoutLimits === true | Pausa temporaria, Autoexclusao |
Ambas false | Mensagem "funcionalidade indisponivel nesta regiao" |
Componentes:
DepositLimitSection— limite de deposito; pre-popula com valores atuais deuserInfoBetLimitSection— limite de aposta; pre-popula comuserInfoLossLimitSection— limite de perda; pre-popula comuserInfoTimeLimitSection— limite de tempo de sessao; pre-popula comuserInfo; converte ISO 8601 viaisoDurationToHours()do SDKTimeoutSection— pausa temporaria; opcoes definidas porTIMEOUT_DAYS_OPTIONSdo SDK; permite extensao quando pausa ativa (filtra opcoes viafilterTimeoutOptions)SelfExclusionSection— autoexclusao; opcoes definidas porSELF_EXCLUSION_MONTHS_OPTIONSdo SDK; permite extensao quando exclusao ativa nao-permanente (filtra opcoes viafilterSelfExclusionOptions)ExclusionAlternatives— exibido antes da confirmacao de autoexclusao quando o motivo selecionado temhasAlternative: true; sugere pausa temporaria ou limites como alternativas menos restritivasAlertOnlyWithdraw— alerta quando conta esta restrita; detecta tipo (pausa, autoexclusao, operador, SPA) e exibe data de termino quando disponivelLimitAccordion— 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.
| Estado | SelfExclusionSection | TimeoutSection |
|---|---|---|
| Inativo | Form completo — todas as opcoes | Form completo — todas as opcoes |
| Self-exclusion ativa (nao permanente) | Alerta + form com opcoes filtradas (>= minimo) | N/A |
| Pausa ativa | N/A | Alerta + form com opcoes filtradas (>= minimo) |
| Permanente (99 ou -1) | Alerta — form oculto | N/A |
| Operator exclusion | Alerta — form oculto | N/A |
| SPA exclusion | Alerta — form oculto | N/A |
| Pausa ativa sem opcoes disponiveis | N/A | Alerta + 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 usuarioUserAccountMenu— sidebar menu dinamico (usauseAccountMenuItems())UserPageTitle— titulo das paginas da conta
API Routes
| Rota | Metodo | Descricao |
|---|---|---|
api/user/login-history | GET | Historico de logins (com transform normalizado via SDK) |
api/user/update-limits | POST/PATCH | Limites de responsible gaming |
api/user/timeout-limits | POST | Pausa temporaria |
api/user/self-exclusion | POST | Autoexclusao |
api/user/update-address | POST | Atualiza endereco |
api/user/check-password | POST | Verifica senha atual |
api/user/add-phone | POST | Adiciona telefone |
api/auth/profile | GET | Perfil completo do usuario |
api/payments/bank-account | POST | Conta 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():
- Usuario seleciona ano e formato (PDF/email)
- Request gera o informe no backend
- Polling ate o status mudar para
completed - 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.