Skip to content

Gestão de Estado & Hidratação (Zustand & TanStack Query)

Gestão de Estado & Hidratação (Zustand & TanStack Query)

Section titled “Gestão de Estado & Hidratação (Zustand & TanStack Query)”

O Prime Crown utiliza uma arquitetura híbrida de gerenciamento de estado desenhada para eliminação de re-renders desnecessários e sincronização transparente com o banco de dados.

O salvamento do Perfil possui lock síncrono antes das gravações de cliente/funcionário e epoch vinculado ao usuário autenticado. As duas persistências ainda podem executar em paralelo, mas somente o lifecycle que as iniciou atualiza o idioma da sessão, mostra sucesso ou encerra loading; troca de conta não recebe feedback nem estado de uma gravação anterior.

O fallback manual de snapshot e as mutações compartilhadas de requests, configurações, onboarding e séries recorrentes separam diagnóstico de resposta. O servidor registra exceções sanitizadas por domínio e devolve uma mensagem estável nas oito saídas de falha total. Quando um snapshot ou grupo de configurações falha parcialmente, errors[] conserva somente label e id para orientar o retry; a mensagem do D1, do Resend ou de outro provedor nunca atravessa para PWA, desktop, Android ou iOS. Validação, autenticação, autorização e IDs funcionais continuam específicos.


⚡ 1. Arquitetura de Sincronização de Estado

Section titled “⚡ 1. Arquitetura de Sincronização de Estado”
sequenceDiagram
    autonumber
    actor User as Usuário
    participant UI as Componente React
    participant Query as TanStack Query Cache
    participant Store as Zustand Store (Derived View)
    participant Sync as syncAtomic / EntitiesApiClient
    participant D1 as Cloudflare D1 DB

    Note over UI, D1: Boot Inicial (/api/boot)
    UI->>Query: bootData loaded via /api/boot
    Query->>Store: useStoreQuerySync (Projeta cache no Zustand)
    Store-->>UI: Re-renderiza apenas seletores específicos

    Note over UI, D1: Mutação com Atualização Otimista
    User->>UI: Executa ação (ex: Alterar Status de Agendamento)
    UI->>Store: Aplicação Imediata de Update Otimista
    UI->>Sync: Disparo Assíncrono para /api/atomic/*
    Sync->>Query: Espelha alteração no Cache do TanStack Query
    Sync->>D1: Persistência Atômica no D1
    alt Sucesso no Banco
        D1-->>Sync: OK 200
    else Falha no Banco
        D1-->>Sync: Erro 500
        Sync->>UI: Exibe Toast "Sync Failed" e Reverte Estado
    end

📦 2. Estrutura dos Zustand Stores (src/stores/*)

Section titled “📦 2. Estrutura dos Zustand Stores (src/stores/*)”

Para evitar re-renders globais que afetavam a performance antiga do React Context, o estado da aplicação é fatiado nas seguintes stores Zustand:

Store Arquivo Responsabilidade
requestsStore.ts src/stores/requestsStore.ts Coleção de solicitações de serviço, agendamentos e faturas.
entityStore.ts src/stores/entityStore.ts Entidades base do sistema (Clientes, Funcionários, Serviços, Entidades Fiscais, Leads, cotações de leads e Addons).
payrollStore.ts src/stores/payrollStore.ts Lançamentos de folha de pagamento, fecho de períodos e saldos de Reserva de Caixa (Cash Reserve).
systemStore.ts src/stores/systemStore.ts Configurações globais do sistema, regras de cancelamento e logs de auditoria.
appStatusStore.ts src/stores/appStatusStore.ts Status de conexão da aplicação, banner PWA e notificações não lidas.

leadQuotes é hidratado por /api/boot: ADMIN/MANAGER recebem a fila operacional e uma conta referenciada como LEAD recebe apenas suas próprias cotações. useLeadQuotes e useOpenLeadQuoteCount expõem seletores estreitos. Criar/atualizar uma intenção pelo RequestService faz upsertLeadQuote; confirmar ou recusar substitui o registro autoritativo, sem representar a cotação como service_request provisório.

requestsStore calcula alterações individuais e em lote a partir de um snapshot síncrono, publica a nova coleção uma única vez e somente depois chama syncAtomic. Persistência nunca ocorre dentro do updater Zustand: middleware ou instrumentação não podem repetir o request, e um batch envia exatamente uma gravação para cada tarefa realmente alterada.

O gate de pureza percorre tanto setters React (setX) quanto o set minúsculo do Zustand. A recarga de carteira também calcula seu cliente atualizado dentro do updater, mas inicia a persistência somente depois da publicação; uma reinvocação de cálculo nunca duplica crédito ou request.

Dados operacionais nunca usam Web Storage: estado offline e histórico de alertas ficam em IndexedDB, enquanto o servidor/D1 continua sendo a fonte de verdade. Preferências exclusivamente visuais podem usar localStorage, mas chaves associadas a uma conta passam por opaqueStorageMarker com namespace próprio. Preferências de som, posição e categorias de notificação usam notif_prefs_v2_<marker>; a leitura migra notif_prefs_v1_<userId> e remove a chave antiga sem perder a configuração. Esse marcador evita IDs legíveis no armazenamento inspecionável da PWA/WebView, mas não é criptografia e nunca deve guardar tokens ou segredos.