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:
@@ -4,6 +4,7 @@
|
||||
import { useConvexClient, useQuery } from 'convex-svelte';
|
||||
import { Pencil, Plus, Trash2, X, Search, Check, FileText } from 'lucide-svelte';
|
||||
import { resolve } from '$app/paths';
|
||||
import { formatarDataBR } from '$lib/utils/datas';
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
@@ -41,7 +42,8 @@
|
||||
numeroSei: '',
|
||||
empresaId: '' as Id<'empresas'> | '',
|
||||
dataInicio: '',
|
||||
dataFim: ''
|
||||
dataFim: '',
|
||||
dataProrrogacao: ''
|
||||
});
|
||||
let selectedObjetos = $state<Id<'objetos'>[]>([]);
|
||||
type ObjetoAtaConfig = {
|
||||
@@ -72,7 +74,8 @@
|
||||
numeroSei: ata.numeroSei,
|
||||
empresaId: ata.empresaId,
|
||||
dataInicio: ata.dataInicio || '',
|
||||
dataFim: ata.dataFim || ''
|
||||
dataFim: ata.dataFim || '',
|
||||
dataProrrogacao: ata.dataProrrogacao || ''
|
||||
};
|
||||
// Fetch linked objects
|
||||
const linkedObjetos = await client.query(api.atas.getObjetos, { id: ata._id });
|
||||
@@ -96,7 +99,8 @@
|
||||
numeroSei: '',
|
||||
empresaId: '',
|
||||
dataInicio: '',
|
||||
dataFim: ''
|
||||
dataFim: '',
|
||||
dataProrrogacao: ''
|
||||
};
|
||||
selectedObjetos = [];
|
||||
objetosConfig = {};
|
||||
@@ -180,6 +184,7 @@
|
||||
empresaId: formData.empresaId as Id<'empresas'>,
|
||||
dataInicio: formData.dataInicio || undefined,
|
||||
dataFim: formData.dataFim || undefined,
|
||||
dataProrrogacao: formData.dataProrrogacao || undefined,
|
||||
objetos
|
||||
};
|
||||
|
||||
@@ -416,7 +421,13 @@
|
||||
{getEmpresaNome(ata.empresaId)}
|
||||
</td>
|
||||
<td class="text-base-content/70 whitespace-nowrap">
|
||||
{ata.dataInicio || '-'} a {ata.dataFim || '-'}
|
||||
{ata.dataInicio ? formatarDataBR(ata.dataInicio) : '-'} a
|
||||
{ata.dataFim ? formatarDataBR(ata.dataFim) : '-'}
|
||||
{#if ata.dataProrrogacao}
|
||||
<span class="text-base-content/50">
|
||||
(prorrogação: {formatarDataBR(ata.dataProrrogacao)})</span
|
||||
>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="text-right whitespace-nowrap">
|
||||
<div class="flex items-center justify-end gap-1">
|
||||
@@ -508,7 +519,7 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||
<div class="form-control w-full">
|
||||
<label class="label" for="dataInicio">
|
||||
<span class="label-text font-semibold">Data Início</span>
|
||||
@@ -531,6 +542,17 @@
|
||||
bind:value={formData.dataFim}
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control w-full">
|
||||
<label class="label" for="dataProrrogacao">
|
||||
<span class="label-text font-semibold">Data Prorrogação</span>
|
||||
</label>
|
||||
<input
|
||||
id="dataProrrogacao"
|
||||
class="input input-bordered focus:input-primary w-full"
|
||||
type="date"
|
||||
bind:value={formData.dataProrrogacao}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user