feat: add filtering functionality for empresas in dashboard, allowing users to search by name or CNPJ, and enhance UI with clear feedback for no results
This commit is contained in:
@@ -7,7 +7,16 @@
|
||||
import { maskCEP, maskCNPJ, maskPhone, maskUF, onlyDigits } from '$lib/utils/masks';
|
||||
|
||||
const client = useConvexClient();
|
||||
const empresasQuery = useQuery(api.empresas.list, {});
|
||||
let filtroEmpresa = $state('');
|
||||
|
||||
const empresasTotalQuery = useQuery(api.empresas.list, {});
|
||||
const empresasQuery = useQuery(api.empresas.list, () => ({
|
||||
query: filtroEmpresa.trim() || undefined
|
||||
}));
|
||||
|
||||
function limparFiltroEmpresa() {
|
||||
filtroEmpresa = '';
|
||||
}
|
||||
|
||||
let modalAberto = $state(false);
|
||||
|
||||
@@ -425,6 +434,32 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card bg-base-100/90 border-base-300 mb-6 border shadow-xl backdrop-blur-sm">
|
||||
<div class="card-body">
|
||||
<div class="grid gap-4 sm:grid-cols-[1fr_auto] sm:items-end">
|
||||
<div class="form-control w-full">
|
||||
<label class="label" for="filtro_empresa">
|
||||
<span class="label-text font-semibold">Buscar empresa</span>
|
||||
</label>
|
||||
<input
|
||||
id="filtro_empresa"
|
||||
class="input input-bordered focus:input-primary w-full"
|
||||
type="text"
|
||||
placeholder="Nome fantasia, razão social ou CNPJ"
|
||||
bind:value={filtroEmpresa}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-3 sm:min-w-max sm:justify-end">
|
||||
<div class="text-base-content/70 text-sm">
|
||||
{empresasQuery.data?.length ?? 0} de {empresasTotalQuery.data?.length ?? 0}
|
||||
</div>
|
||||
<button type="button" class="btn btn-ghost" onclick={limparFiltroEmpresa}>Limpar</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body">
|
||||
{#if empresasQuery.isLoading}
|
||||
@@ -437,11 +472,21 @@
|
||||
</div>
|
||||
{:else if empresasQuery.data && empresasQuery.data.length === 0}
|
||||
<div class="py-10 text-center">
|
||||
{#if (empresasTotalQuery.data?.length ?? 0) === 0}
|
||||
<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>
|
||||
{:else}
|
||||
<p class="text-base-content/70 mb-2">Nenhum resultado encontrado.</p>
|
||||
<p class="text-base-content/60 mb-4 text-sm">
|
||||
Ajuste ou limpe o filtro para ver empresas.
|
||||
</p>
|
||||
<button type="button" class="btn btn-ghost" onclick={limparFiltroEmpresa}>
|
||||
Limpar filtro
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if empresasQuery.data}
|
||||
<div class="overflow-x-auto">
|
||||
|
||||
@@ -5,15 +5,37 @@ import { getCurrentUserFunction } from './auth';
|
||||
import type { Id } from './_generated/dataModel';
|
||||
|
||||
export const list = query({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
args: {
|
||||
query: v.optional(v.string())
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
await ctx.runQuery(internal.permissoesAcoes.assertPermissaoAcaoAtual, {
|
||||
recurso: 'empresas',
|
||||
acao: 'listar'
|
||||
});
|
||||
|
||||
const empresas = await ctx.db.query('empresas').collect();
|
||||
return empresas;
|
||||
const term = args.query?.trim();
|
||||
if (!term) return empresas;
|
||||
|
||||
const termLower = term.toLowerCase();
|
||||
const termDigits = term.replace(/\D/g, '');
|
||||
|
||||
return empresas.filter((empresa) => {
|
||||
const razao = (empresa.razao_social || '').toLowerCase();
|
||||
const fantasia = (empresa.nome_fantasia || '').toLowerCase();
|
||||
|
||||
const cnpjRaw = empresa.cnpj || '';
|
||||
const cnpjLower = cnpjRaw.toLowerCase();
|
||||
const cnpjDigits = cnpjRaw.replace(/\D/g, '');
|
||||
|
||||
const matchNome = razao.includes(termLower) || fantasia.includes(termLower);
|
||||
const matchCnpj = termDigits
|
||||
? cnpjDigits.includes(termDigits)
|
||||
: cnpjLower.includes(termLower);
|
||||
|
||||
return matchNome || matchCnpj;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user