feat: add empresa and contatos
- Introduced a new utility function `maskCNPJ` for formatting CNPJ values. - Updated the dashboard pages to replace icons and improve layout, including the addition of a link to manage companies. - Enhanced the display of upcoming features for both the Licitações and Programas Esportivos modules, indicating their development status.
This commit is contained in:
626
apps/web/src/routes/(dashboard)/licitacoes/empresas/+page.svelte
Normal file
626
apps/web/src/routes/(dashboard)/licitacoes/empresas/+page.svelte
Normal file
@@ -0,0 +1,626 @@
|
||||
<script lang="ts">
|
||||
import { useConvexClient, useQuery } from "convex-svelte";
|
||||
import { api } from "@sgse-app/backend/convex/_generated/api";
|
||||
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";
|
||||
|
||||
const client = useConvexClient();
|
||||
const empresasQuery = useQuery(api.empresas.list, {});
|
||||
|
||||
let modalAberto = false;
|
||||
|
||||
type ContatoForm = {
|
||||
_id?: Id<"contatosEmpresa">;
|
||||
nome: string;
|
||||
funcao: string;
|
||||
email: string;
|
||||
telefone: string;
|
||||
descricao?: string;
|
||||
_deleted?: boolean;
|
||||
};
|
||||
|
||||
type EmpresaForm = {
|
||||
id?: string;
|
||||
nome: string;
|
||||
cnpj: string;
|
||||
telefone: string;
|
||||
email: string;
|
||||
descricao?: string;
|
||||
contatos: ContatoForm[];
|
||||
};
|
||||
|
||||
let empresaForm: EmpresaForm = {
|
||||
nome: "",
|
||||
cnpj: "",
|
||||
telefone: "",
|
||||
email: "",
|
||||
descricao: "",
|
||||
contatos: [],
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function handleEmpresaTelefoneInput(event: Event) {
|
||||
const target = event.target as HTMLInputElement;
|
||||
empresaForm.telefone = maskPhone(target.value);
|
||||
target.value = empresaForm.telefone;
|
||||
}
|
||||
|
||||
function handleContatoTelefoneInput(event: Event) {
|
||||
const target = event.target as HTMLInputElement;
|
||||
if (!contatoEmEdicao) return;
|
||||
contatoEmEdicao = { ...contatoEmEdicao, telefone: maskPhone(target.value) };
|
||||
target.value = contatoEmEdicao.telefone;
|
||||
}
|
||||
|
||||
function abrirNovaEmpresa() {
|
||||
empresaForm = {
|
||||
nome: "",
|
||||
cnpj: "",
|
||||
telefone: "",
|
||||
email: "",
|
||||
descricao: "",
|
||||
contatos: [],
|
||||
};
|
||||
modalAberto = true;
|
||||
}
|
||||
|
||||
async function editarEmpresa(id: Id<"empresas">) {
|
||||
const detalhes = await client.query(api.empresas.getById, { id });
|
||||
if (!detalhes) return;
|
||||
|
||||
empresaForm = {
|
||||
id: detalhes._id,
|
||||
nome: detalhes.nome,
|
||||
cnpj: detalhes.cnpj,
|
||||
telefone: detalhes.telefone,
|
||||
email: detalhes.email,
|
||||
descricao: detalhes.descricao ?? "",
|
||||
contatos:
|
||||
detalhes.contatos?.map((c) => ({
|
||||
_id: c._id,
|
||||
nome: c.nome,
|
||||
funcao: c.funcao,
|
||||
email: c.email,
|
||||
telefone: c.telefone,
|
||||
descricao: c.descricao ?? "",
|
||||
})) ?? [],
|
||||
};
|
||||
modalAberto = true;
|
||||
}
|
||||
|
||||
async function verContatos(empresaId: Id<"empresas">, nome: string) {
|
||||
const detalhes = await client.query(api.empresas.getById, { id: empresaId });
|
||||
contatosDaEmpresa = detalhes?.contatos ?? [];
|
||||
empresaContatosNome = nome;
|
||||
contatosModalAberto = true;
|
||||
}
|
||||
|
||||
function fecharContatosModal() {
|
||||
contatosModalAberto = false;
|
||||
contatosDaEmpresa = [];
|
||||
empresaContatosNome = "";
|
||||
}
|
||||
|
||||
function fecharModal() {
|
||||
modalAberto = false;
|
||||
contatoEmEdicao = null;
|
||||
contatoIndiceEdicao = null;
|
||||
erroFormulario = "";
|
||||
}
|
||||
|
||||
function adicionarContato() {
|
||||
contatoEmEdicao = {
|
||||
nome: "",
|
||||
funcao: "",
|
||||
email: "",
|
||||
telefone: "",
|
||||
descricao: "",
|
||||
};
|
||||
contatoIndiceEdicao = null;
|
||||
}
|
||||
|
||||
function editarContato(index: number) {
|
||||
const contato = empresaForm.contatos[index];
|
||||
if (!contato || contato._deleted) return;
|
||||
contatoEmEdicao = { ...contato };
|
||||
contatoIndiceEdicao = index;
|
||||
}
|
||||
|
||||
function removerContato(index: number) {
|
||||
const contato = empresaForm.contatos[index];
|
||||
if (!contato) return;
|
||||
if (contato._id) {
|
||||
empresaForm.contatos[index] = { ...contato, _deleted: true };
|
||||
} else {
|
||||
empresaForm.contatos = empresaForm.contatos.filter((_, i) => i !== index);
|
||||
}
|
||||
}
|
||||
|
||||
function salvarContatoAtual() {
|
||||
if (!contatoEmEdicao) return;
|
||||
if (!contatoEmEdicao.nome || !contatoEmEdicao.email || !contatoEmEdicao.telefone) {
|
||||
erroFormulario = "Preencha pelo menos nome, e-mail e telefone do contato.";
|
||||
return;
|
||||
}
|
||||
erroFormulario = "";
|
||||
|
||||
if (contatoIndiceEdicao === null) {
|
||||
empresaForm.contatos = [...empresaForm.contatos, contatoEmEdicao];
|
||||
} else {
|
||||
empresaForm.contatos[contatoIndiceEdicao] = {
|
||||
...(empresaForm.contatos[contatoIndiceEdicao] ?? {}),
|
||||
...contatoEmEdicao,
|
||||
_deleted: false,
|
||||
};
|
||||
}
|
||||
contatoEmEdicao = null;
|
||||
contatoIndiceEdicao = null;
|
||||
}
|
||||
|
||||
async function salvarEmpresa() {
|
||||
if (!empresaForm.nome || !empresaForm.cnpj || !empresaForm.telefone || !empresaForm.email) {
|
||||
erroFormulario = "Preencha todos os campos obrigatórios da empresa.";
|
||||
return;
|
||||
}
|
||||
salvando = true;
|
||||
erroFormulario = "";
|
||||
try {
|
||||
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,
|
||||
cnpj: empresaForm.cnpj,
|
||||
telefone: empresaForm.telefone,
|
||||
email: empresaForm.email,
|
||||
descricao: empresaForm.descricao || undefined,
|
||||
contatos: empresaForm.contatos,
|
||||
});
|
||||
}
|
||||
fecharModal();
|
||||
} catch (error) {
|
||||
erroFormulario = error instanceof Error ? error.message : "Erro ao salvar empresa.";
|
||||
} finally {
|
||||
salvando = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<main class="container mx-auto px-4 py-4">
|
||||
<div class="text-sm breadcrumbs mb-4">
|
||||
<ul>
|
||||
<li><a href={resolve('/')} class="text-primary hover:underline">Dashboard</a></li>
|
||||
<li><a href={resolve('/licitacoes')} class="text-primary hover:underline">Licitações</a></li>
|
||||
<li>Empresas</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="mb-6 flex items-center justify-between gap-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="p-3 bg-primary/10 rounded-xl">
|
||||
<Building2 class="h-8 w-8 text-primary" strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-primary">Empresas</h1>
|
||||
<p class="text-base-content/70">
|
||||
Cadastro, listagem e contatos de empresas fornecedoras.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary gap-2" type="button" onclick={abrirNovaEmpresa}>
|
||||
<Plus class="h-4 w-4" strokeWidth={2} />
|
||||
Nova empresa
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body">
|
||||
{#if empresasQuery.isLoading}
|
||||
<div class="flex justify-center py-8">
|
||||
<span class="loading loading-spinner loading-lg text-primary"></span>
|
||||
</div>
|
||||
{:else if empresasQuery.error}
|
||||
<div class="alert alert-error">
|
||||
<span>Erro ao carregar empresas.</span>
|
||||
</div>
|
||||
{:else if empresasQuery.data && empresasQuery.data.length === 0}
|
||||
<div class="text-center py-10">
|
||||
<p class="text-base-content/70 mb-4">Nenhuma empresa cadastrada ainda.</p>
|
||||
<button class="btn btn-primary gap-2" type="button" onclick={abrirNovaEmpresa}>
|
||||
<Plus class="h-4 w-4" strokeWidth={2} />
|
||||
Cadastrar primeira empresa
|
||||
</button>
|
||||
</div>
|
||||
{:else if empresasQuery.data}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-zebra">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>CNPJ</th>
|
||||
<th>Telefone</th>
|
||||
<th>E-mail</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each empresasQuery.data as empresa (empresa._id)}
|
||||
<tr>
|
||||
<td>{empresa.nome}</td>
|
||||
<td>{empresa.cnpj}</td>
|
||||
<td class="flex items-center gap-2">
|
||||
<Phone class="h-4 w-4 text-base-content/60" strokeWidth={2} />
|
||||
<span>{empresa.telefone}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex items-center gap-2">
|
||||
<Mail class="h-4 w-4 text-base-content/60" strokeWidth={2} />
|
||||
<span>{empresa.email}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<div class="flex justify-end gap-2">
|
||||
<button
|
||||
class="btn btn-outline btn-sm gap-2"
|
||||
type="button"
|
||||
onclick={() => verContatos(empresa._id, empresa.nome)}
|
||||
>
|
||||
<Users class="h-4 w-4" strokeWidth={2} />
|
||||
Contatos
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-ghost btn-sm gap-2"
|
||||
type="button"
|
||||
onclick={() => editarEmpresa(empresa._id)}
|
||||
>
|
||||
<Pencil class="h-4 w-4" strokeWidth={2} />
|
||||
Editar
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if modalAberto}
|
||||
<div class="modal modal-open">
|
||||
<div class="modal-box max-w-5xl">
|
||||
<div class="flex items-start justify-between gap-4 mb-4">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold">
|
||||
{empresaForm.id ? "Editar empresa" : "Nova empresa"}
|
||||
</h2>
|
||||
<p class="text-sm text-base-content/70">
|
||||
Preencha os dados da empresa e cadastre contatos associados.
|
||||
</p>
|
||||
</div>
|
||||
<button class="btn btn-ghost btn-sm" type="button" onclick={fecharModal}>
|
||||
<X class="h-4 w-4" strokeWidth={2} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if erroFormulario}
|
||||
<div class="alert alert-error mb-4">
|
||||
<span>{erroFormulario}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
||||
<div class="form-control">
|
||||
<label class="label" for="nomeEmpresa"><span class="label-text">Nome da empresa *</span></label>
|
||||
<input
|
||||
id="nomeEmpresa"
|
||||
class="input input-bordered w-full"
|
||||
bind:value={empresaForm.nome}
|
||||
placeholder="Nome"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="cnpjEmpresa"><span class="label-text">CNPJ *</span></label>
|
||||
<input
|
||||
id="cnpjEmpresa"
|
||||
class="input input-bordered w-full"
|
||||
value={empresaForm.cnpj}
|
||||
inputmode="numeric"
|
||||
oninput={handleEmpresaCnpjInput}
|
||||
placeholder="00.000.000/0000-00"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="telefoneEmpresa"><span class="label-text">Telefone *</span></label>
|
||||
<input
|
||||
id="telefoneEmpresa"
|
||||
class="input input-bordered w-full"
|
||||
value={empresaForm.telefone}
|
||||
inputmode="numeric"
|
||||
oninput={handleEmpresaTelefoneInput}
|
||||
placeholder="(00) 00000-0000"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="emailEmpresa"><span class="label-text">E-mail *</span></label>
|
||||
<input
|
||||
id="emailEmpresa"
|
||||
class="input input-bordered w-full"
|
||||
type="email"
|
||||
bind:value={empresaForm.email}
|
||||
placeholder="contato@empresa.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control md:col-span-2">
|
||||
<label class="label" for="descricaoEmpresa">
|
||||
<span class="label-text">Descrição (opcional)</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="descricaoEmpresa"
|
||||
class="textarea textarea-bordered w-full"
|
||||
rows={3}
|
||||
bind:value={empresaForm.descricao}
|
||||
placeholder="Descrição, observações ou informações adicionais da empresa"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider my-4">Contatos da empresa</div>
|
||||
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<Users class="h-5 w-5 text-primary" strokeWidth={2} />
|
||||
<span class="font-semibold">Contatos cadastrados</span>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-sm btn-outline gap-2"
|
||||
type="button"
|
||||
onclick={adicionarContato}
|
||||
>
|
||||
<Plus class="h-4 w-4" strokeWidth={2} />
|
||||
Adicionar contato
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if empresaForm.contatos.filter((c) => !c._deleted).length === 0}
|
||||
<p class="text-sm text-base-content/60 mb-3">
|
||||
Nenhum contato cadastrado. Clique em "Adicionar contato" para incluir.
|
||||
</p>
|
||||
{:else}
|
||||
<div class="overflow-x-auto mb-4">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Função</th>
|
||||
<th>E-mail</th>
|
||||
<th>Telefone</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each empresaForm.contatos as contato, index (contato._id ?? `${contato.email}-${index}`)}
|
||||
{#if !contato._deleted}
|
||||
<tr>
|
||||
<td>{contato.nome}</td>
|
||||
<td>{contato.funcao}</td>
|
||||
<td>{contato.email}</td>
|
||||
<td>{contato.telefone}</td>
|
||||
<td class="text-right">
|
||||
<div class="flex justify-end gap-2">
|
||||
<button
|
||||
class="btn btn-ghost btn-xs"
|
||||
type="button"
|
||||
onclick={() => editarContato(index)}
|
||||
>
|
||||
<Pencil class="h-3 w-3" strokeWidth={2} />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-ghost btn-xs text-error"
|
||||
type="button"
|
||||
onclick={() => removerContato(index)}
|
||||
>
|
||||
<X class="h-3 w-3" strokeWidth={2} />
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if contatoEmEdicao}
|
||||
<div class="mt-4 p-4 rounded-lg bg-base-200">
|
||||
<h3 class="font-semibold mb-3">
|
||||
{contatoIndiceEdicao === null ? "Novo contato" : "Editar contato"}
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div class="form-control">
|
||||
<label class="label" for="contatoNome"><span class="label-text">Nome *</span></label>
|
||||
<input
|
||||
id="contatoNome"
|
||||
class="input input-bordered input-sm w-full"
|
||||
bind:value={contatoEmEdicao.nome}
|
||||
placeholder="Nome do contato"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="contatoFuncao"><span class="label-text">Função *</span></label>
|
||||
<input
|
||||
id="contatoFuncao"
|
||||
class="input input-bordered input-sm w-full"
|
||||
bind:value={contatoEmEdicao.funcao}
|
||||
placeholder="Função / cargo"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="contatoEmail"><span class="label-text">E-mail *</span></label>
|
||||
<input
|
||||
id="contatoEmail"
|
||||
class="input input-bordered input-sm w-full"
|
||||
type="email"
|
||||
bind:value={contatoEmEdicao.email}
|
||||
placeholder="email@exemplo.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label" for="contatoTelefone"><span class="label-text">Telefone *</span></label>
|
||||
<input
|
||||
id="contatoTelefone"
|
||||
class="input input-bordered input-sm w-full"
|
||||
value={contatoEmEdicao.telefone}
|
||||
inputmode="numeric"
|
||||
oninput={handleContatoTelefoneInput}
|
||||
placeholder="(00) 00000-0000"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control md:col-span-2">
|
||||
<label class="label" for="contatoDescricao">
|
||||
<span class="label-text">Descrição (opcional)</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="contatoDescricao"
|
||||
class="textarea textarea-bordered textarea-sm w-full"
|
||||
rows={2}
|
||||
bind:value={contatoEmEdicao.descricao}
|
||||
placeholder="Observações sobre o contato"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex justify-end gap-2">
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
type="button"
|
||||
onclick={() => {
|
||||
contatoEmEdicao = null;
|
||||
contatoIndiceEdicao = null;
|
||||
}}
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary btn-sm"
|
||||
type="button"
|
||||
onclick={salvarContatoAtual}
|
||||
>
|
||||
Salvar contato
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="modal-action">
|
||||
<button class="btn btn-ghost" type="button" onclick={fecharModal} disabled={salvando}>
|
||||
Cancelar
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
type="button"
|
||||
onclick={salvarEmpresa}
|
||||
disabled={salvando}
|
||||
>
|
||||
{#if salvando}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
Salvando...
|
||||
{:else}
|
||||
Salvar
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if contatosModalAberto}
|
||||
<div class="modal modal-open">
|
||||
<div class="modal-box max-w-3xl">
|
||||
<div class="flex items-start justify-between gap-4 mb-4">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold">Contatos da empresa</h2>
|
||||
<p class="text-sm text-base-content/70">
|
||||
{empresaContatosNome}
|
||||
</p>
|
||||
</div>
|
||||
<button class="btn btn-ghost btn-sm" type="button" onclick={fecharContatosModal}>
|
||||
<X class="h-4 w-4" strokeWidth={2} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if !contatosDaEmpresa.length}
|
||||
<p class="text-sm text-base-content/60">
|
||||
Nenhum contato cadastrado para esta empresa.
|
||||
</p>
|
||||
{:else}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Função</th>
|
||||
<th>E-mail</th>
|
||||
<th>Telefone</th>
|
||||
<th>Descrição</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each contatosDaEmpresa as contato (contato._id ?? `${contato.email}`)}
|
||||
<tr>
|
||||
<td>{contato.nome}</td>
|
||||
<td>{contato.funcao}</td>
|
||||
<td>{contato.email}</td>
|
||||
<td>{contato.telefone}</td>
|
||||
<td>{contato.descricao}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="modal-action">
|
||||
<button class="btn" type="button" onclick={fecharContatosModal}>Fechar</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user