feat: Implement Ata de Registro de Preços management and linking to objetos and pedidos
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { FileText, Package, ShoppingCart } from 'lucide-svelte';
|
||||
import { Building2, FileText, Package, ShoppingCart } from 'lucide-svelte';
|
||||
import { resolve } from '$app/paths';
|
||||
import ProtectedRoute from '$lib/components/ProtectedRoute.svelte';
|
||||
</script>
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<a
|
||||
href={resolve('/compras/produtos')}
|
||||
href={resolve('/compras/objetos')}
|
||||
class="card bg-base-100 border-base-200 hover:border-primary border shadow-md transition-shadow hover:shadow-lg"
|
||||
>
|
||||
<div class="card-body">
|
||||
@@ -35,10 +35,44 @@
|
||||
<div class="bg-primary/10 rounded-lg p-2">
|
||||
<Package class="text-primary h-6 w-6" strokeWidth={2} />
|
||||
</div>
|
||||
<h4 class="font-semibold">Produtos</h4>
|
||||
<h4 class="font-semibold">Objetos</h4>
|
||||
</div>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Cadastro, listagem e edição de produtos e serviços disponíveis para compra.
|
||||
Cadastro, listagem e edição de objetos e serviços disponíveis para compra.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={resolve('/compras/atas')}
|
||||
class="card bg-base-100 border-base-200 hover:border-accent border shadow-md transition-shadow hover:shadow-lg"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="mb-2 flex items-center gap-3">
|
||||
<div class="bg-accent/10 rounded-lg p-2">
|
||||
<FileText class="text-accent h-6 w-6" strokeWidth={2} />
|
||||
</div>
|
||||
<h4 class="font-semibold">Atas de Registro</h4>
|
||||
</div>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Gerencie Atas de Registro de Preços e seus vínculos com objetos.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={resolve('/licitacoes/empresas')}
|
||||
class="card bg-base-100 border-base-200 hover:border-info border shadow-md transition-shadow hover:shadow-lg"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="mb-2 flex items-center gap-3">
|
||||
<div class="bg-info/10 rounded-lg p-2">
|
||||
<Building2 class="text-info h-6 w-6" strokeWidth={2} />
|
||||
</div>
|
||||
<h4 class="font-semibold">Empresas</h4>
|
||||
</div>
|
||||
<p class="text-base-content/70 text-sm">
|
||||
Cadastro e gestão de empresas fornecedoras e seus contatos.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -7,12 +7,15 @@
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
// Reactive query
|
||||
// Reactive queries
|
||||
const objetosQuery = useQuery(api.objetos.list, {});
|
||||
let objetos = $derived(objetosQuery.data || []);
|
||||
let loading = $derived(objetosQuery.isLoading);
|
||||
let error = $derived(objetosQuery.error?.message || null);
|
||||
|
||||
const atasQuery = useQuery(api.atas.list, {});
|
||||
let atas = $derived(atasQuery.data || []);
|
||||
|
||||
// Modal state
|
||||
let showModal = $state(false);
|
||||
let editingId: string | null = $state(null);
|
||||
@@ -23,13 +26,16 @@
|
||||
codigoEfisco: '',
|
||||
codigoCatmat: '',
|
||||
codigoCatserv: '',
|
||||
unidade: ''
|
||||
unidade: '',
|
||||
atas: [] as Id<'atas'>[]
|
||||
});
|
||||
let saving = $state(false);
|
||||
|
||||
function openModal(objeto?: Doc<'objetos'>) {
|
||||
async function openModal(objeto?: Doc<'objetos'>) {
|
||||
if (objeto) {
|
||||
editingId = objeto._id;
|
||||
// Fetch linked Atas
|
||||
const linkedAtas = await client.query(api.objetos.getAtas, { objetoId: objeto._id });
|
||||
formData = {
|
||||
nome: objeto.nome,
|
||||
valorEstimado: maskCurrencyBRL(objeto.valorEstimado || ''),
|
||||
@@ -37,7 +43,8 @@
|
||||
codigoEfisco: objeto.codigoEfisco || '',
|
||||
codigoCatmat: objeto.codigoCatmat || '',
|
||||
codigoCatserv: objeto.codigoCatserv || '',
|
||||
unidade: objeto.unidade || ''
|
||||
unidade: objeto.unidade || '',
|
||||
atas: linkedAtas.map((a) => a._id)
|
||||
};
|
||||
} else {
|
||||
editingId = null;
|
||||
@@ -48,7 +55,8 @@
|
||||
codigoEfisco: '',
|
||||
codigoCatmat: '',
|
||||
codigoCatserv: '',
|
||||
unidade: ''
|
||||
unidade: '',
|
||||
atas: []
|
||||
};
|
||||
}
|
||||
showModal = true;
|
||||
@@ -70,7 +78,8 @@
|
||||
codigoEfisco: formData.codigoEfisco,
|
||||
codigoCatmat: formData.codigoCatmat || undefined,
|
||||
codigoCatserv: formData.codigoCatserv || undefined,
|
||||
unidade: formData.unidade
|
||||
unidade: formData.unidade,
|
||||
atas: formData.atas
|
||||
};
|
||||
|
||||
if (editingId) {
|
||||
@@ -97,6 +106,14 @@
|
||||
alert('Erro ao excluir: ' + (e as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAtaSelection(ataId: Id<'atas'>) {
|
||||
if (formData.atas.includes(ataId)) {
|
||||
formData.atas = formData.atas.filter((id) => id !== ataId);
|
||||
} else {
|
||||
formData.atas = [...formData.atas, ataId];
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container mx-auto p-6">
|
||||
@@ -303,6 +320,31 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mb-6">
|
||||
<label class="mb-2 block text-sm font-bold text-gray-700" for="atas">
|
||||
Vincular Atas
|
||||
</label>
|
||||
<div class="max-h-40 overflow-y-auto rounded border p-2">
|
||||
{#each atas as ata (ata._id)}
|
||||
<div class="mb-2 flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={`ata-${ata._id}`}
|
||||
checked={formData.atas.includes(ata._id)}
|
||||
onchange={() => toggleAtaSelection(ata._id)}
|
||||
class="mr-2 h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
<label for={`ata-${ata._id}`} class="text-sm text-gray-700">
|
||||
{ata.numero} ({ata.numeroSei})
|
||||
</label>
|
||||
</div>
|
||||
{/each}
|
||||
{#if atas.length === 0}
|
||||
<p class="text-sm text-gray-500">Nenhuma ata disponível.</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end">
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user