From feaebdf65543f860f4b3bd4f2c39f2dede293381 Mon Sep 17 00:00:00 2001 From: Rodrigo Martins Date: Fri, 11 Apr 2025 21:34:35 -0300 Subject: [PATCH] Add CEP autocompletion by ViaCEP/ECT Signed-off-by: Rodrigo Martins --- src/components/clientes/ClienteModal.tsx | 102 +++++++++++++++-------- 1 file changed, 67 insertions(+), 35 deletions(-) diff --git a/src/components/clientes/ClienteModal.tsx b/src/components/clientes/ClienteModal.tsx index b8410ae..43b52d1 100644 --- a/src/components/clientes/ClienteModal.tsx +++ b/src/components/clientes/ClienteModal.tsx @@ -176,6 +176,15 @@ export const ClienteModal: React.FC = ({ }) => { const [activeTab, setActiveTab] = useState(initialTab); const [isLoadingCep, setIsLoadingCep] = useState(false); + // Novo estado para controlar os valores do endereço + const [enderecoState, setEnderecoState] = useState({ + endereco: "", + bairro: "", + cidade: "", + estado: "", + preenchido: false, + }); + const { register, handleSubmit, @@ -183,6 +192,7 @@ export const ClienteModal: React.FC = ({ reset, setValue, watch, + getValues, } = useForm(); const { createCliente, updateCliente } = useClientes(); const formPopulated = useRef(false); @@ -190,7 +200,7 @@ export const ClienteModal: React.FC = ({ // Monitorar o valor do CEP para autopreenchimento const cepValue = watch("cep"); - // Função para buscar dados do CEP - versão otimizada para forçar atualização da UI + // Função para buscar dados do CEP - versão com gerenciamento de estados do componente const fetchAddressByCep = useCallback(async (cep: string) => { if (!cep || cep.length !== 8) return; @@ -202,40 +212,30 @@ export const ClienteModal: React.FC = ({ ); if (response.data && !response.data.erro) { - // Força atualização imediata dos campos com notificação de mudança - setValue("endereco", response.data.logradouro || "", { - shouldDirty: true, - shouldTouch: true, - shouldValidate: true, - }); - setValue("bairro", response.data.bairro || "", { - shouldDirty: true, - shouldTouch: true, - shouldValidate: true, - }); - setValue("cidade", response.data.localidade || "", { - shouldDirty: true, - shouldTouch: true, - shouldValidate: true, - }); - setValue("estado", response.data.uf || "", { - shouldDirty: true, - shouldTouch: true, - shouldValidate: true, + // Usar estado local para garantir re-renderização imediata + // Forçar preenchimento dos campos e atualização da interface + const endereco = response.data.logradouro || ""; + const bairro = response.data.bairro || ""; + const cidade = response.data.localidade || ""; + const estado = response.data.uf || ""; + + // Atualizar valores no formulário + setValue("endereco", endereco, { shouldValidate: true }); + setValue("bairro", bairro, { shouldValidate: true }); + setValue("cidade", cidade, { shouldValidate: true }); + setValue("estado", estado, { shouldValidate: true }); + + // Garantir re-renderização do componente + // Esta é a parte crucial: forçar o componente a re-renderizar com novo estado + setEnderecoState({ + endereco, + bairro, + cidade, + estado, + preenchido: true, }); - // Forçar atualização da UI - esta é a parte crucial - const event = new Event('input', { bubbles: true }); - - // Disparar eventos nos campos para garantir atualização visual - ['endereco', 'bairro', 'cidade', 'estado'].forEach(field => { - const element = document.querySelector(`[name="${field}"]`) as HTMLInputElement; - if (element) { - element.dispatchEvent(event); - } - }); - - // Focar no campo número após o preenchimento automático + // Focar no próximo campo setTimeout(() => { const numeroInput = document.querySelector( 'input[name="numero"]' @@ -244,7 +244,6 @@ export const ClienteModal: React.FC = ({ }, 100); } else { console.error("CEP não encontrado"); - // Notificar o usuário sobre o CEP não encontrado alert("CEP não encontrado. Por favor, verifique o CEP informado."); } } catch (error) { @@ -256,17 +255,23 @@ export const ClienteModal: React.FC = ({ }, [setValue]); // Efeito para monitorar mudanças no CEP com debounce otimizado + const [autoFetchEnabled, setAutoFetchEnabled] = useState(true); + useEffect(() => { + if (!autoFetchEnabled) return; + // Limpar qualquer timer existente const timer = setTimeout(() => { const cleanCep = cepValue?.replace(/\D/g, ""); if (cleanCep && cleanCep.length === 8) { fetchAddressByCep(cleanCep); + setAutoFetchEnabled(false); // Desativar busca automática após primeiro preenchimento + setTimeout(() => setAutoFetchEnabled(true), 1000); // Reativar depois de 1 segundo } }, 200); // Reduzir debounce para resposta mais rápida return () => clearTimeout(timer); - }, [cepValue, fetchAddressByCep]); + }, [cepValue, fetchAddressByCep, autoFetchEnabled]); // Função para preencher o formulário com os dados do cliente const populateForm = useCallback( @@ -568,6 +573,13 @@ export const ClienteModal: React.FC = ({ } e.target.value = value; }} + onBlur={(e) => { + // Forçar busca ao sair do campo + const cleanCep = e.target.value.replace(/\D/g, ""); + if (cleanCep && cleanCep.length === 8) { + fetchAddressByCep(cleanCep); + } + }} /> {isLoadingCep && (
@@ -601,6 +613,11 @@ export const ClienteModal: React.FC = ({ required: "Endereço é obrigatório", })} error={errors.endereco?.message} + value={enderecoState.preenchido ? enderecoState.endereco : undefined} + onChange={(e) => { + setValue("endereco", e.target.value); + setEnderecoState(prev => ({...prev, endereco: e.target.value})); + }} /> = ({ required: "Bairro é obrigatório", })} error={errors.bairro?.message} + value={enderecoState.preenchido ? enderecoState.bairro : undefined} + onChange={(e) => { + setValue("bairro", e.target.value); + setEnderecoState(prev => ({...prev, bairro: e.target.value})); + }} /> = ({ required: "Cidade é obrigatória", })} error={errors.cidade?.message} + value={enderecoState.preenchido ? enderecoState.cidade : undefined} + onChange={(e) => { + setValue("cidade", e.target.value); + setEnderecoState(prev => ({...prev, cidade: e.target.value})); + }} /> = ({ })} options={estadosOptions} error={errors.estado?.message} + value={enderecoState.preenchido ? enderecoState.estado : undefined} + onChange={(e) => { + setValue("estado", e.target.value); + setEnderecoState(prev => ({...prev, estado: e.target.value})); + }} />
)}