Add CEP autocompletion by ViaCEP/ECT

Signed-off-by: Rodrigo Martins <rodrigo.sousa.577@gmail.com>
This commit is contained in:
rodrigo committed 2025-04-11 21:34:35 -03:00
1 parent 1a9f71ab64
commit feaebdf655
1 file changed
+67 -35
+67 -35
View File
@@ -176,6 +176,15 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
}) => { }) => {
const [activeTab, setActiveTab] = useState(initialTab); const [activeTab, setActiveTab] = useState(initialTab);
const [isLoadingCep, setIsLoadingCep] = useState(false); 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 { const {
register, register,
handleSubmit, handleSubmit,
@@ -183,6 +192,7 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
reset, reset,
setValue, setValue,
watch, watch,
getValues,
} = useForm(); } = useForm();
const { createCliente, updateCliente } = useClientes(); const { createCliente, updateCliente } = useClientes();
const formPopulated = useRef(false); const formPopulated = useRef(false);
@@ -190,7 +200,7 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
// Monitorar o valor do CEP para autopreenchimento // Monitorar o valor do CEP para autopreenchimento
const cepValue = watch("cep"); 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) => { const fetchAddressByCep = useCallback(async (cep: string) => {
if (!cep || cep.length !== 8) return; if (!cep || cep.length !== 8) return;
@@ -202,40 +212,30 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
); );
if (response.data && !response.data.erro) { if (response.data && !response.data.erro) {
// Força atualização imediata dos campos com notificação de mudança // Usar estado local para garantir re-renderização imediata
setValue("endereco", response.data.logradouro || "", { // Forçar preenchimento dos campos e atualização da interface
shouldDirty: true, const endereco = response.data.logradouro || "";
shouldTouch: true, const bairro = response.data.bairro || "";
shouldValidate: true, const cidade = response.data.localidade || "";
}); const estado = response.data.uf || "";
setValue("bairro", response.data.bairro || "", {
shouldDirty: true, // Atualizar valores no formulário
shouldTouch: true, setValue("endereco", endereco, { shouldValidate: true });
shouldValidate: true, setValue("bairro", bairro, { shouldValidate: true });
}); setValue("cidade", cidade, { shouldValidate: true });
setValue("cidade", response.data.localidade || "", { setValue("estado", estado, { shouldValidate: true });
shouldDirty: true,
shouldTouch: true, // Garantir re-renderização do componente
shouldValidate: true, // Esta é a parte crucial: forçar o componente a re-renderizar com novo estado
}); setEnderecoState({
setValue("estado", response.data.uf || "", { endereco,
shouldDirty: true, bairro,
shouldTouch: true, cidade,
shouldValidate: true, estado,
preenchido: true,
}); });
// Forçar atualização da UI - esta é a parte crucial // Focar no próximo campo
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
setTimeout(() => { setTimeout(() => {
const numeroInput = document.querySelector( const numeroInput = document.querySelector(
'input[name="numero"]' 'input[name="numero"]'
@@ -244,7 +244,6 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
}, 100); }, 100);
} else { } else {
console.error("CEP não encontrado"); 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."); alert("CEP não encontrado. Por favor, verifique o CEP informado.");
} }
} catch (error) { } catch (error) {
@@ -256,17 +255,23 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
}, [setValue]); }, [setValue]);
// Efeito para monitorar mudanças no CEP com debounce otimizado // Efeito para monitorar mudanças no CEP com debounce otimizado
const [autoFetchEnabled, setAutoFetchEnabled] = useState(true);
useEffect(() => { useEffect(() => {
if (!autoFetchEnabled) return;
// Limpar qualquer timer existente // Limpar qualquer timer existente
const timer = setTimeout(() => { const timer = setTimeout(() => {
const cleanCep = cepValue?.replace(/\D/g, ""); const cleanCep = cepValue?.replace(/\D/g, "");
if (cleanCep && cleanCep.length === 8) { if (cleanCep && cleanCep.length === 8) {
fetchAddressByCep(cleanCep); 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 }, 200); // Reduzir debounce para resposta mais rápida
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [cepValue, fetchAddressByCep]); }, [cepValue, fetchAddressByCep, autoFetchEnabled]);
// Função para preencher o formulário com os dados do cliente // Função para preencher o formulário com os dados do cliente
const populateForm = useCallback( const populateForm = useCallback(
@@ -568,6 +573,13 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
} }
e.target.value = value; 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 && ( {isLoadingCep && (
<div className="absolute right-3 top-9"> <div className="absolute right-3 top-9">
@@ -601,6 +613,11 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
required: "Endereço é obrigatório", required: "Endereço é obrigatório",
})} })}
error={errors.endereco?.message} error={errors.endereco?.message}
value={enderecoState.preenchido ? enderecoState.endereco : undefined}
onChange={(e) => {
setValue("endereco", e.target.value);
setEnderecoState(prev => ({...prev, endereco: e.target.value}));
}}
/> />
<FormInput <FormInput
label="Número" label="Número"
@@ -619,6 +636,11 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
required: "Bairro é obrigatório", required: "Bairro é obrigatório",
})} })}
error={errors.bairro?.message} error={errors.bairro?.message}
value={enderecoState.preenchido ? enderecoState.bairro : undefined}
onChange={(e) => {
setValue("bairro", e.target.value);
setEnderecoState(prev => ({...prev, bairro: e.target.value}));
}}
/> />
<FormInput <FormInput
label="Cidade" label="Cidade"
@@ -626,6 +648,11 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
required: "Cidade é obrigatória", required: "Cidade é obrigatória",
})} })}
error={errors.cidade?.message} error={errors.cidade?.message}
value={enderecoState.preenchido ? enderecoState.cidade : undefined}
onChange={(e) => {
setValue("cidade", e.target.value);
setEnderecoState(prev => ({...prev, cidade: e.target.value}));
}}
/> />
<FormSelect <FormSelect
label="Estado" label="Estado"
@@ -634,6 +661,11 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
})} })}
options={estadosOptions} options={estadosOptions}
error={errors.estado?.message} error={errors.estado?.message}
value={enderecoState.preenchido ? enderecoState.estado : undefined}
onChange={(e) => {
setValue("estado", e.target.value);
setEnderecoState(prev => ({...prev, estado: e.target.value}));
}}
/> />
</div> </div>
)} )}