Detalhes do Cliente
Última atualização: 15 de julho de 2026
Visão Geral
A tela de detalhes é outro hub de abas, parecido em espírito com a carteira: uma única rota, /detalhes.cliente, concentra dados cadastrais, endereços, contatos, atividades, análise, engajamento, últimas compras, registro do promotor e títulos em aberto, cada sub-aba ligada à sua própria flag de configuração.
Arquivos-chave
| Arquivo | Responsabilidade |
|---|---|
app/detalhes.cliente.tsx | Rota |
features/client-details/client.details.tsx | Screen, monta a barra de sub-abas e renderiza a sub-aba ativa |
features/client-details/client.details.controller.ts | Hub de estado: cliente atual, aba ativa, contato selecionado, lista de botões de abas |
features/client-details/subscreens/* | Uma pasta por sub-aba (clientInfo, clientAddress, clientContacts, clientActivities, clientLastSales, clientOpenFinancialStatements, clientAttachments) |
Chegando na tela
A rota recebe o cliente inteiro serializado como parâmetro, e opcionalmente qual aba deve abrir primeiro:
// client.details.controller.ts
const params = useLocalSearchParams<{ client: string; clientTab?: string }>();
const parsedClient = useMemo(() => {
if (!params.client) return null;
try {
return JSON.parse(params.client) as Client;
} catch {
return null;
}
}, [params.client]);
Se não houver cliente algum nos parâmetros, ou se o JSON vier corrompido, a tela simplesmente volta para a tela anterior (router.back()) em vez de renderizar algo sem sentido. A aba inicial só respeita clientTab quando é "UltimasCompras" e a flag correspondente está ativa; em qualquer outro caso, a tela sempre abre em "Detalhes".
Os dados são atualizados a cada vez que a tela ganha foco
O cliente vindo por parâmetro de rota é só o ponto de partida. A cada vez que a tela volta a ficar em foco, ela busca a versão mais atual do cliente no banco local, e só usa o valor vindo por parâmetro como fallback:
const refreshClient = useCallback(async () => {
if (!parsedClient?.id) return;
try {
const localClient = await clientService.findById(parsedClient.id);
setClientData(localClient ?? parsedClient);
} catch {
setClientData(parsedClient);
}
}, [parsedClient]);
useFocusEffect(useCallback(() => { void refreshClient(); }, [refreshClient]));
Isso é o que garante que, depois de editar o cliente na tela de cadastro (ver Cadastro e Edição de Cliente) e voltar para os detalhes, a tela mostre os dados já atualizados, sem precisar navegar de novo a partir da carteira.
As abas visíveis dependem de flags, e se auto-corrigem
Cada botão de aba só entra na lista se sua flag estiver ativa, no mesmo padrão já visto na carteira. Além disso, um efeito garante que, se o usuário estiver numa aba cuja flag foi desligada nesse meio tempo, a tela volta sozinha para "Detalhes":
useEffect(() => {
if (
(isActivitiesEnabled || navigatorBarFilter !== "Atividades") &&
(isClientAnalysisEnabled || navigatorBarFilter !== "AnaliseCliente") &&
// ... mesma checagem para as demais abas
) {
return;
}
setNavigatorBarFilter("Detalhes");
}, [/* ... todas as flags e navigatorBarFilter ... */]);
Entrando em modo de edição
O botão de editar cliente vive dentro da sub-aba "Detalhes" (clientInfo), não na tela de detalhes em si, e reaproveita a mesma rota de cadastro documentada em Cadastro e Edição de Cliente:
// clientInfo/client.details.info.tsx
{isClientRegisterEnabled && (
<FabButton
onPress={() =>
router.push({
pathname: "/cliente/registrar-cliente",
params: { mode: "edit", client: JSON.stringify(client) },
})
}
/>
)}
O botão de editar usa a mesma flag cfg-ativarcadastrocliente que controla a criação de clientes novos. Desligar essa flag esconde tanto a opção de cadastrar quanto o botão de editar, mesmo que a rota de edição em si não dependa dela (ver a nota em Cadastro e Edição de Cliente).
A sub-aba de Atividades ainda é dados de exemplo
Vale saber isso antes de estender a aba "Atividades": ela não lê nenhuma atividade real do cliente ainda. buildClientActivities só retorna algo quando o idExterno do cliente é exatamente "57", e nesse caso devolve uma única atividade com data, fotos e texto de observação fixos no código:
// client.details.controller.ts
function buildClientActivities(client: Client | null): ActivityCardData[] {
const externalId = client?.idExterno?.trim();
if (!client || externalId !== "57") {
return [];
}
// ... retorna um array com uma atividade fixa, com
// geoTimestampLabel: "Quinta, 27 de Marco de 2026 - 10:00 ate 13:39"
// e fotos de "@/assets/images/teste-image.png"
}
Para qualquer outro cliente, a lista de atividades vem vazia. Não é um bug de dados faltando, é dado de demonstração amarrado a um único cliente de teste, esperando a integração real ser implementada.
Armadilhas conhecidas
Não assuma que a aba "Atividades" reflete atividades reais do cliente selecionado. Ela só existe hoje para um idExterno fixo ("57"), com conteúdo hardcoded, e retorna vazio para qualquer outro cliente. Ao editar um cliente e voltar para os detalhes, lembre que a tela relê do banco local a cada foco (useFocusEffect); se uma alteração não aparece, o problema mais provável é a escrita local não ter chegado a tempo, não a tela estar exibindo um cache desatualizado.