diff --git a/apps/web/src/routes/(dashboard)/licitacoes/empresas/+page.svelte b/apps/web/src/routes/(dashboard)/licitacoes/empresas/+page.svelte index 6ed1c60..189872f 100644 --- a/apps/web/src/routes/(dashboard)/licitacoes/empresas/+page.svelte +++ b/apps/web/src/routes/(dashboard)/licitacoes/empresas/+page.svelte @@ -4,12 +4,13 @@ import type { Id } from "@sgse-app/backend/convex/_generated/dataModel"; import { Building2, Phone, Mail, Plus, Users, Pencil, X } from "lucide-svelte"; import { resolve } from "$app/paths"; - import { maskCNPJ, maskPhone } from "$lib/utils/masks"; + import { maskCNPJ, maskCEP, maskPhone, maskUF, onlyDigits } from "$lib/utils/masks"; + import "$lib/svelte-compat"; const client = useConvexClient(); const empresasQuery = useQuery(api.empresas.list, {}); - let modalAberto = false; + let modalAberto = $state(false); type ContatoForm = { _id?: Id<"contatosEmpresa">; @@ -21,40 +22,137 @@ _deleted?: boolean; }; + type EnderecoForm = { + cep: string; + logradouro: string; + numero: string; + complemento: string; + bairro: string; + cidade: string; + uf: string; + }; + type EmpresaForm = { - id?: string; - nome: string; + id?: Id<"empresas">; + razao_social: string; + nome_fantasia?: string; cnpj: string; telefone: string; email: string; descricao?: string; + endereco: EnderecoForm; contatos: ContatoForm[]; }; - let empresaForm: EmpresaForm = { - nome: "", + let empresaForm = $state({ + razao_social: "", + nome_fantasia: "", cnpj: "", telefone: "", email: "", descricao: "", + endereco: { + cep: "", + logradouro: "", + numero: "", + complemento: "", + bairro: "", + cidade: "", + uf: "", + }, contatos: [], + }); + + let contatoEmEdicao = $state(null); + let contatoIndiceEdicao = $state(null); + let erroFormulario = $state(""); + let salvando = $state(false); + + let contatosModalAberto = $state(false); + let contatosDaEmpresa = $state([]); + let empresaContatosNome = $state(""); + let carregandoCep = $state(false); + let erroCep = $state(""); + let carregandoCnpj = $state(false); + let erroCnpj = $state(""); + + type ReceitaWsResponse = { + status?: string; + message?: string; + nome?: string; + fantasia?: string; + telefone?: string; + email?: string; + cep?: string; + logradouro?: string; + numero?: string; + complemento?: string; + bairro?: string; + municipio?: string; + uf?: string; }; - let contatoEmEdicao: ContatoForm | null = null; - let contatoIndiceEdicao: number | null = null; - let erroFormulario = ""; - let salvando = false; - - let contatosModalAberto = false; - let contatosDaEmpresa: ContatoForm[] = []; - let empresaContatosNome = ""; - function handleEmpresaCnpjInput(event: Event) { const target = event.target as HTMLInputElement; empresaForm.cnpj = maskCNPJ(target.value); target.value = empresaForm.cnpj; } + async function handleEmpresaCnpjBlur() { + const digits = onlyDigits(empresaForm.cnpj); + if (digits.length !== 14) return; + + carregandoCnpj = true; + erroCnpj = ""; + try { + const response = await fetch(`https://www.receitaws.com.br/v1/cnpj/${digits}`); + const data: ReceitaWsResponse = await response.json(); + + if (data.status === "ERROR") { + throw new Error(data.message || "CNPJ não encontrado."); + } + + if (data.nome && !empresaForm.razao_social) { + empresaForm.razao_social = data.nome; + } + if (data.fantasia && !empresaForm.nome_fantasia) { + empresaForm.nome_fantasia = data.fantasia; + } + if (data.telefone && !empresaForm.telefone) { + empresaForm.telefone = maskPhone(data.telefone); + } + if (data.email && !empresaForm.email) { + empresaForm.email = data.email; + } + + if ( + data.cep || + data.logradouro || + data.bairro || + data.municipio || + data.uf + ) { + empresaForm.endereco = { + ...empresaForm.endereco, + cep: data.cep ? maskCEP(data.cep) : empresaForm.endereco.cep, + logradouro: data.logradouro ?? empresaForm.endereco.logradouro, + numero: data.numero ?? empresaForm.endereco.numero, + complemento: data.complemento ?? empresaForm.endereco.complemento, + bairro: data.bairro ?? empresaForm.endereco.bairro, + cidade: data.municipio ?? empresaForm.endereco.cidade, + uf: data.uf ? maskUF(data.uf) : empresaForm.endereco.uf, + }; + } + } catch (error) { + erroCnpj = + error instanceof Error + ? error.message + : "Não foi possível buscar os dados do CNPJ."; + } finally { + carregandoCnpj = false; + } + } + function handleEmpresaTelefoneInput(event: Event) { const target = event.target as HTMLInputElement; empresaForm.telefone = maskPhone(target.value); @@ -68,13 +166,87 @@ target.value = contatoEmEdicao.telefone; } + function handleCepInput(event: Event) { + const target = event.target as HTMLInputElement; + empresaForm.endereco.cep = maskCEP(target.value); + target.value = empresaForm.endereco.cep; + + const digits = onlyDigits(empresaForm.endereco.cep); + if (digits.length === 8) { + void buscarCep(digits); + } + } + + async function buscarCep(cepDigits: string) { + carregandoCep = true; + erroCep = ""; + try { + const response = await fetch(`https://viacep.com.br/ws/${cepDigits}/json/`); + const data = await response.json(); + + if (data.erro) { + throw new Error("CEP não encontrado."); + } + + empresaForm.endereco = { + ...empresaForm.endereco, + cep: maskCEP(cepDigits), + logradouro: data.logradouro ?? empresaForm.endereco.logradouro, + bairro: data.bairro ?? empresaForm.endereco.bairro, + cidade: data.localidade ?? empresaForm.endereco.cidade, + uf: data.uf ? maskUF(data.uf) : empresaForm.endereco.uf, + }; + } catch (error) { + erroCep = + error instanceof Error + ? error.message + : "Não foi possível buscar o endereço pelo CEP."; + } finally { + carregandoCep = false; + } + } + + function normalizeEnderecoForSave(endereco: EnderecoForm) { + const hasData = + endereco.cep || + endereco.logradouro || + endereco.numero || + endereco.bairro || + endereco.cidade || + endereco.uf; + + if (!hasData) { + return undefined; + } + + return { + cep: endereco.cep, + logradouro: endereco.logradouro, + numero: endereco.numero, + complemento: endereco.complemento || undefined, + bairro: endereco.bairro, + cidade: endereco.cidade, + uf: endereco.uf, + }; + } + function abrirNovaEmpresa() { empresaForm = { - nome: "", + razao_social: "", + nome_fantasia: "", cnpj: "", telefone: "", email: "", descricao: "", + endereco: { + cep: "", + logradouro: "", + numero: "", + complemento: "", + bairro: "", + cidade: "", + uf: "", + }, contatos: [], }; modalAberto = true; @@ -86,11 +258,21 @@ empresaForm = { id: detalhes._id, - nome: detalhes.nome, + razao_social: detalhes.razao_social, + nome_fantasia: detalhes.nome_fantasia, cnpj: detalhes.cnpj, telefone: detalhes.telefone, email: detalhes.email, descricao: detalhes.descricao ?? "", + endereco: { + cep: detalhes.endereco?.cep ?? "", + logradouro: detalhes.endereco?.logradouro ?? "", + numero: detalhes.endereco?.numero ?? "", + complemento: detalhes.endereco?.complemento ?? "", + bairro: detalhes.endereco?.bairro ?? "", + cidade: detalhes.endereco?.cidade ?? "", + uf: detalhes.endereco?.uf ?? "", + }, contatos: detalhes.contatos?.map((c) => ({ _id: c._id, @@ -104,10 +286,10 @@ modalAberto = true; } - async function verContatos(empresaId: Id<"empresas">, nome: string) { + async function verContatos(empresaId: Id<"empresas">, razaoSocial: string) { const detalhes = await client.query(api.empresas.getById, { id: empresaId }); contatosDaEmpresa = detalhes?.contatos ?? []; - empresaContatosNome = nome; + empresaContatosNome = razaoSocial; contatosModalAberto = true; } @@ -174,36 +356,53 @@ } async function salvarEmpresa() { - if (!empresaForm.nome || !empresaForm.cnpj || !empresaForm.telefone || !empresaForm.email) { + if ( + !empresaForm.razao_social || + !empresaForm.cnpj || + !empresaForm.telefone || + !empresaForm.email + ) { erroFormulario = "Preencha todos os campos obrigatórios da empresa."; return; } salvando = true; erroFormulario = ""; try { + const enderecoPayload = normalizeEnderecoForSave(empresaForm.endereco); + if (empresaForm.id) { - await client.mutation( - api.empresas.update as typeof api.empresas.update, - { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - id: empresaForm.id as any, - nome: empresaForm.nome, - cnpj: empresaForm.cnpj, - telefone: empresaForm.telefone, - email: empresaForm.email, - descricao: empresaForm.descricao || undefined, - contatos: empresaForm.contatos, - } - ); - } else { - await client.mutation(api.empresas.create, { - nome: empresaForm.nome, + const baseArgs = { + id: empresaForm.id, + razao_social: empresaForm.razao_social, + nome_fantasia: empresaForm.nome_fantasia, cnpj: empresaForm.cnpj, telefone: empresaForm.telefone, email: empresaForm.email, descricao: empresaForm.descricao || undefined, contatos: empresaForm.contatos, - }); + }; + + const args = enderecoPayload + ? { ...baseArgs, endereco: enderecoPayload } + : baseArgs; + + await client.mutation(api.empresas.update, args); + } else { + const baseArgs = { + razao_social: empresaForm.razao_social, + nome_fantasia: empresaForm.nome_fantasia, + cnpj: empresaForm.cnpj, + telefone: empresaForm.telefone, + email: empresaForm.email, + descricao: empresaForm.descricao || undefined, + contatos: empresaForm.contatos, + }; + + const args = enderecoPayload + ? { ...baseArgs, endereco: enderecoPayload } + : baseArgs; + + await client.mutation(api.empresas.create, args); } fecharModal(); } catch (error) { @@ -264,34 +463,39 @@ - + - + {#each empresasQuery.data as empresa (empresa._id)} - + -
NomeRazão social / Nome fantasia CNPJ Telefone E-mailAções
{empresa.nome} +
+ {empresa.razao_social} + {#if empresa.nome_fantasia} + {empresa.nome_fantasia} + {/if} +
+
{empresa.cnpj} {empresa.telefone} -
- - {empresa.email} -
+
+ + {empresa.email}