Add CEP autocompletion by ViaCEP/ECT
Signed-off-by: Rodrigo Martins <rodrigo.sousa.577@gmail.com>
This commit is contained in:
1 parent
1a9f71ab64
commit
feaebdf655
1 file changed
+67
-35
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in new issue
Block a user