DB:
- sar.vw_contatos: view sobre gestao.contato com join sig.corrent
(id_entidade CHAR20 = id_corrent; cod_vendedor para filtro por rep)
- sar.contatos_novos: staging com nome, cargo, departamento, telefone,
celular, whatsapp, email, dt_aniversario, anotacoes
- trg_contato_novo: AFTER INSERT → gestao.contato; grava id_contato_erp
API:
- GET /clients/:id/contacts — lista contatos ativos do cliente
- POST /clients/:id/contacts — cria via staging; retorna { idContatoErp, sincronizado }
Web:
- ClientContacts: tabela de contatos no detalhe do cliente
- Links diretos WhatsApp (wa.me) + mailto por contato
- Modal "Novo Contato" com todos os campos; feedback de código ERP no sucesso
- whatsapp é o campo principal para envio futuro via ChatWoot
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
99 lines
3.0 KiB
TypeScript
99 lines
3.0 KiB
TypeScript
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
import {
|
|
ClientDetailSchema,
|
|
ClientListResponseSchema,
|
|
ClientNovoResultSchema,
|
|
ContatoResultSchema,
|
|
ContatoSchema,
|
|
type ClientDetail,
|
|
type ClientListQuery,
|
|
type ClientListResponse,
|
|
type ClientNovoResult,
|
|
type Contato,
|
|
type ContatoResult,
|
|
type CreateClientNovo,
|
|
type CreateContato,
|
|
} from '@sar/api-interface';
|
|
import { z } from 'zod';
|
|
import { apiFetch } from '../api-client';
|
|
|
|
export const CLIENT_KEYS = {
|
|
all: ['clients'] as const,
|
|
list: (params: Partial<ClientListQuery>) => ['clients', 'list', params] as const,
|
|
detail: (id: number) => ['clients', 'detail', id] as const,
|
|
};
|
|
|
|
export function useClientList(params: Partial<ClientListQuery> = {}) {
|
|
const qs = new URLSearchParams();
|
|
if (params.q) qs.set('q', params.q);
|
|
if (params.status) qs.set('status', params.status);
|
|
if (params.page) qs.set('page', String(params.page));
|
|
if (params.limit) qs.set('limit', String(params.limit));
|
|
|
|
const query = qs.toString();
|
|
|
|
return useQuery<ClientListResponse, Error>({
|
|
queryKey: CLIENT_KEYS.list(params),
|
|
queryFn: async () => {
|
|
const res = await apiFetch(`/clients${query ? `?${query}` : ''}`);
|
|
return ClientListResponseSchema.parse(res);
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useClientDetail(id: number | string | undefined) {
|
|
return useQuery<ClientDetail, Error>({
|
|
queryKey: CLIENT_KEYS.detail(Number(id)),
|
|
queryFn: async () => {
|
|
const res = await apiFetch(`/clients/${id}`);
|
|
return ClientDetailSchema.parse(res);
|
|
},
|
|
enabled: !!id,
|
|
});
|
|
}
|
|
|
|
export function useClientContacts(idCliente: number | undefined) {
|
|
return useQuery<Contato[]>({
|
|
queryKey: ['clients', 'contacts', idCliente],
|
|
queryFn: async () => {
|
|
const res = await apiFetch(`/clients/${idCliente}/contacts`);
|
|
return z.array(ContatoSchema).parse(res);
|
|
},
|
|
enabled: !!idCliente,
|
|
});
|
|
}
|
|
|
|
export function useCreateContato(idCliente: number | undefined) {
|
|
const queryClient = useQueryClient();
|
|
return useMutation<ContatoResult, Error, CreateContato>({
|
|
mutationFn: async (data) => {
|
|
const res = await apiFetch(`/clients/${idCliente}/contacts`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data),
|
|
});
|
|
return ContatoResultSchema.parse(res);
|
|
},
|
|
onSuccess: () => {
|
|
void queryClient.invalidateQueries({ queryKey: ['clients', 'contacts', idCliente] });
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useCreateClientNovo() {
|
|
const queryClient = useQueryClient();
|
|
return useMutation<ClientNovoResult, Error, CreateClientNovo>({
|
|
mutationFn: async (data) => {
|
|
const res = await apiFetch('/clients/novo', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data),
|
|
});
|
|
return ClientNovoResultSchema.parse(res);
|
|
},
|
|
onSuccess: () => {
|
|
void queryClient.invalidateQueries({ queryKey: CLIENT_KEYS.all });
|
|
},
|
|
});
|
|
}
|