feat: enhance ata management by adding dataProrrogacao field and updating related logic for effective date handling, improving data integrity and user experience in pedidos

This commit is contained in:
2025-12-17 10:39:33 -03:00
parent fbf00c824e
commit 9072619e26
8 changed files with 390 additions and 115 deletions

View File

@@ -24,6 +24,7 @@
import { resolve } from '$app/paths';
import { page } from '$app/state';
import { maskCurrencyBRL } from '$lib/utils/masks';
import { formatarDataBR } from '$lib/utils/datas';
const pedidoId = $derived(page.params.id as Id<'pedidos'>);
const client = useConvexClient();
@@ -84,7 +85,7 @@
// Atas por objeto (carregadas sob demanda)
type AtasComLimite = FunctionReturnType<typeof api.objetos.getAtasComLimite>;
let atasPorObjeto = $state<Record<string, AtasComLimite>>({});
let atasPorObjetoExtra = $state<Record<string, AtasComLimite>>({});
let editingItems = $state<Record<string, EditingItem>>({});
@@ -110,14 +111,39 @@
let selectedCount = $derived(selectedItemIds.size);
let hasSelection = $derived(selectedCount > 0);
// Garante que, para todos os itens existentes, as atas do respectivo objeto
// sejam carregadas independentemente do formulário de criação.
$effect(() => {
for (const item of items as unknown as PedidoItemForEdit[]) {
if (!atasPorObjeto[item.objetoId]) {
void loadAtasForObjeto(item.objetoId);
// Pela regra do backend, um pedido só pode ter uma ata (quando houver).
// Usamos isso para “forçar incluir” a ata atual do pedido na listagem do objeto,
// mesmo que esteja fora da janela (ex.: vencida há mais de 3 meses).
let pedidoAtaId = $derived.by(() => {
const withAta = (items as unknown as PedidoItemForEdit[]).find((i) => i.ataId);
return (withAta?.ataId ?? null) as Id<'atas'> | null;
});
// Carrega atas (para itens existentes) via query batch, evitando efeitos que mutam estado.
const atasBatchQuery = $derived.by(() =>
useQuery(api.objetos.getAtasComLimiteBatch, () => {
const ids: Id<'objetos'>[] = [];
const seen: Record<string, true> = {};
for (const item of items as unknown as PedidoItemForEdit[]) {
const key = String(item.objetoId);
if (seen[key]) continue;
seen[key] = true;
ids.push(item.objetoId);
}
return {
objetoIds: ids,
includeAtaIds: pedidoAtaId ? [pedidoAtaId] : undefined
};
})
);
let atasPorObjetoFromBatch = $derived.by(() => {
const map: Record<string, AtasComLimite> = {};
const data = atasBatchQuery.data || [];
for (const row of data) {
map[String(row.objetoId)] = row.atas;
}
return map;
});
// Group items by user
@@ -663,19 +689,20 @@
}
async function loadAtasForObjeto(objetoId: string) {
if (atasPorObjeto[objetoId]) return;
if (atasPorObjetoExtra[objetoId]) return;
try {
const linkedAtas = await client.query(api.objetos.getAtasComLimite, {
objetoId: objetoId as Id<'objetos'>
objetoId: objetoId as Id<'objetos'>,
includeAtaIds: pedidoAtaId ? [pedidoAtaId] : undefined
});
atasPorObjeto[objetoId] = linkedAtas;
atasPorObjetoExtra[objetoId] = linkedAtas;
} catch (e) {
console.error('Erro ao carregar atas para objeto', objetoId, e);
}
}
function getAtasForObjeto(objetoId: string): AtasComLimite {
return atasPorObjeto[objetoId] || [];
return atasPorObjetoExtra[objetoId] || atasPorObjetoFromBatch[objetoId] || [];
}
function handleObjetoChange(id: string) {
@@ -1664,11 +1691,18 @@
<option value="">Nenhuma</option>
{#each getAtasForObjeto(newItem.objetoId) as ata (ata._id)}
{@const isSelectedAta = String(ata._id) === newItem.ataId}
{@const reason = !ata.quantidadeTotal
? 'não configurada'
: ata.quantidadeUsada >= ata.limitePermitido
? 'limite atingido'
: null}
{@const reason =
ata.lockReason === 'nao_configurada'
? 'não configurada'
: ata.lockReason === 'limite_atingido'
? 'limite atingido'
: ata.lockReason === 'vigencia_expirada'
? `vigência encerrada em ${
ata.dataFimEfetiva || ata.dataFim
? formatarDataBR((ata.dataFimEfetiva || ata.dataFim) as string)
: '-'
}`
: null}
<option value={ata._id} disabled={ata.isLocked && !isSelectedAta}>
Ata {ata.numero} (SEI: {ata.numeroSei}){reason ? ` (${reason})` : ''}
</option>
@@ -1920,11 +1954,20 @@
{#each getAtasForObjeto(item.objetoId) as ata (ata._id)}
{@const currentAtaId = ensureEditingItem(item).ataId}
{@const isSelectedAta = String(ata._id) === currentAtaId}
{@const reason = !ata.quantidadeTotal
? 'não configurada'
: ata.quantidadeUsada >= ata.limitePermitido
? 'limite atingido'
: null}
{@const reason =
ata.lockReason === 'nao_configurada'
? 'não configurada'
: ata.lockReason === 'limite_atingido'
? 'limite atingido'
: ata.lockReason === 'vigencia_expirada'
? `vigência encerrada em ${
ata.dataFimEfetiva || ata.dataFim
? formatarDataBR(
(ata.dataFimEfetiva || ata.dataFim) as string
)
: '-'
}`
: null}
<option value={ata._id} disabled={ata.isLocked && !isSelectedAta}>
Ata {ata.numero} (SEI: {ata.numeroSei}){reason ? ` (${reason})` : ''}
</option>

View File

@@ -6,6 +6,7 @@
import { Plus, Trash2, X, Info } from 'lucide-svelte';
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { formatarDataBR } from '$lib/utils/datas';
const client = useConvexClient();
@@ -635,11 +636,18 @@
>
<option value="">Nenhuma</option>
{#each availableAtas as ata (ata._id)}
{@const reason = !ata.quantidadeTotal
? 'não configurada'
: ata.quantidadeUsada >= ata.limitePermitido
? 'limite atingido'
: null}
{@const reason =
ata.lockReason === 'nao_configurada'
? 'não configurada'
: ata.lockReason === 'limite_atingido'
? 'limite atingido'
: ata.lockReason === 'vigencia_expirada'
? `vigência encerrada em ${
ata.dataFimEfetiva || ata.dataFim
? formatarDataBR((ata.dataFimEfetiva || ata.dataFim) as string)
: '-'
}`
: null}
<option value={ata._id} disabled={ata.isLocked}>
Ata {ata.numero} (SEI: {ata.numeroSei}){reason ? ` (${reason})` : ''}
</option>
@@ -735,7 +743,12 @@
{#if detailsItem.ata.dataInicio}
<p class="text-green-800">
<strong>Vigência:</strong>
{detailsItem.ata.dataInicio} até {detailsItem.ata.dataFim || 'Indefinido'}
{formatarDataBR(detailsItem.ata.dataInicio)} até {detailsItem.ata
.dataFimEfetiva || detailsItem.ata.dataFim
? formatarDataBR(
(detailsItem.ata.dataFimEfetiva || detailsItem.ata.dataFim) as string
)
: 'Indefinido'}
</p>
{/if}
</div>