feat: Implement batch item removal and pedido splitting for pedidos, and add document management for atas.
This commit is contained in:
@@ -37,6 +37,9 @@
|
||||
|
||||
let selectedItems = $state<SelectedItem[]>([]);
|
||||
let selectedObjetoIds = $derived(selectedItems.map((i) => i.objeto._id));
|
||||
let hasMixedModalidades = $derived(
|
||||
new Set(selectedItems.map((i) => i.modalidade)).size > 1
|
||||
);
|
||||
|
||||
// Item configuration modal
|
||||
let showItemModal = $state(false);
|
||||
@@ -153,6 +156,36 @@
|
||||
}
|
||||
}
|
||||
|
||||
function formatModalidade(modalidade: SelectedItem['modalidade']) {
|
||||
switch (modalidade) {
|
||||
case 'consumo':
|
||||
return 'Consumo';
|
||||
case 'dispensa':
|
||||
return 'Dispensa';
|
||||
case 'inexgibilidade':
|
||||
return 'Inexigibilidade';
|
||||
case 'adesao':
|
||||
return 'Adesão';
|
||||
default:
|
||||
return modalidade;
|
||||
}
|
||||
}
|
||||
|
||||
function getModalidadeBadgeClasses(modalidade: SelectedItem['modalidade']) {
|
||||
switch (modalidade) {
|
||||
case 'consumo':
|
||||
return 'bg-blue-100 text-blue-800';
|
||||
case 'dispensa':
|
||||
return 'bg-yellow-100 text-yellow-800';
|
||||
case 'inexgibilidade':
|
||||
return 'bg-purple-100 text-purple-800';
|
||||
case 'adesao':
|
||||
return 'bg-green-100 text-green-800';
|
||||
default:
|
||||
return 'bg-gray-100 text-gray-800';
|
||||
}
|
||||
}
|
||||
|
||||
function getAcaoNome(acaoId: Id<'acoes'> | undefined) {
|
||||
if (!acaoId) return '-';
|
||||
const acao = acoes.find((a) => a._id === acaoId);
|
||||
@@ -172,7 +205,8 @@
|
||||
.map((match) => {
|
||||
// Find name from selected items (might be multiple with same object, just pick one name)
|
||||
const item = selectedItems.find((p) => p.objeto._id === match.objetoId);
|
||||
return `${item?.objeto.nome}: ${match.quantidade} un.`;
|
||||
const modalidadeLabel = formatModalidade(match.modalidade);
|
||||
return `${item?.objeto.nome} (${modalidadeLabel}): ${match.quantidade} un.`;
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
@@ -226,6 +260,8 @@
|
||||
|
||||
checking = true;
|
||||
try {
|
||||
// Importante: ação (acaoId) NÃO entra no filtro de similaridade.
|
||||
// O filtro considera apenas combinação de objeto + modalidade.
|
||||
const itensFiltro =
|
||||
selectedItems.length > 0
|
||||
? selectedItems.map((item) => ({
|
||||
@@ -255,6 +291,11 @@
|
||||
|
||||
async function handleSubmit(e: Event) {
|
||||
e.preventDefault();
|
||||
if (hasMixedModalidades) {
|
||||
error =
|
||||
'Não é possível criar o pedido com itens de modalidades diferentes. Ajuste os itens antes de continuar.';
|
||||
return;
|
||||
}
|
||||
creating = true;
|
||||
error = null;
|
||||
try {
|
||||
@@ -369,32 +410,44 @@
|
||||
<div class="space-y-3">
|
||||
{#each selectedItems as item, index (index)}
|
||||
<div
|
||||
class="rounded-lg border border-gray-200 bg-gray-50 p-4 transition hover:shadow-md"
|
||||
class="rounded-xl border border-gray-200 bg-gray-50 p-4 transition hover:shadow-md"
|
||||
>
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="flex-1 space-y-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<p class="font-semibold text-gray-900">{item.objeto.nome}</p>
|
||||
<span
|
||||
class={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold ${getModalidadeBadgeClasses(
|
||||
item.modalidade
|
||||
)}`}
|
||||
>
|
||||
{formatModalidade(item.modalidade)}
|
||||
</span>
|
||||
{#if item.ataNumero}
|
||||
<span
|
||||
class="rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800"
|
||||
class="inline-flex items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800"
|
||||
>
|
||||
Ata {item.ataNumero}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="mt-2 flex flex-wrap gap-x-4 gap-y-1 text-sm text-gray-600">
|
||||
<span><strong>Qtd:</strong> {item.quantidade} {item.objeto.unidade}</span>
|
||||
<span><strong>Modalidade:</strong> {item.modalidade}</span>
|
||||
{#if item.acaoId}
|
||||
<span><strong>Ação:</strong> {getAcaoNome(item.acaoId)}</span>
|
||||
<span
|
||||
class="inline-flex items-center rounded-full bg-indigo-100 px-2.5 py-0.5 text-xs font-medium text-indigo-800"
|
||||
>
|
||||
Ação: {getAcaoNome(item.acaoId)}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-x-4 gap-y-1 text-sm text-gray-600">
|
||||
<span>
|
||||
<strong>Qtd:</strong> {item.quantidade} {item.objeto.unidade}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-4 flex items-center gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded p-2 text-blue-600 transition hover:bg-blue-50"
|
||||
class="rounded-lg p-2 text-blue-600 transition hover:bg-blue-50"
|
||||
onclick={() => openDetails(item)}
|
||||
aria-label="Ver detalhes"
|
||||
>
|
||||
@@ -402,7 +455,7 @@
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded p-2 text-red-600 transition hover:bg-red-50"
|
||||
class="rounded-lg p-2 text-red-600 transition hover:bg-red-50"
|
||||
onclick={() => removeItem(index)}
|
||||
aria-label="Remover item"
|
||||
>
|
||||
@@ -424,6 +477,18 @@
|
||||
</div>
|
||||
|
||||
<!-- Warnings Section -->
|
||||
{#if hasMixedModalidades}
|
||||
<div
|
||||
class="mb-3 rounded-lg border border-red-400 bg-red-50 px-4 py-3 text-sm text-red-800"
|
||||
>
|
||||
<p class="font-semibold">Modalidades diferentes detectadas</p>
|
||||
<p>
|
||||
Não é possível criar o pedido com itens de modalidades diferentes. Ajuste os itens para
|
||||
usar uma única modalidade.
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if warning}
|
||||
<div
|
||||
class="rounded-lg border border-yellow-400 bg-yellow-50 px-4 py-3 text-sm text-yellow-800"
|
||||
@@ -443,11 +508,25 @@
|
||||
<ul class="space-y-2">
|
||||
{#each existingPedidos as pedido (pedido._id)}
|
||||
<li class="flex flex-col rounded-lg bg-white px-4 py-3 shadow-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="space-y-1">
|
||||
<p class="text-sm font-medium text-gray-900">
|
||||
Pedido {pedido.numeroSei || 'sem número SEI'} — {formatStatus(pedido.status)}
|
||||
</p>
|
||||
{#if getFirstMatchingSelectedItem(pedido)}
|
||||
{#key pedido._id}
|
||||
{#if getFirstMatchingSelectedItem(pedido)}
|
||||
<span
|
||||
class={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold ${getModalidadeBadgeClasses(
|
||||
getFirstMatchingSelectedItem(pedido).modalidade
|
||||
)}`}
|
||||
>
|
||||
Modalidade:{' '}
|
||||
{formatModalidade(getFirstMatchingSelectedItem(pedido).modalidade)}
|
||||
</span>
|
||||
{/if}
|
||||
{/key}
|
||||
{/if}
|
||||
{#if getMatchingInfo(pedido)}
|
||||
<p class="mt-1 text-xs text-blue-700">
|
||||
{getMatchingInfo(pedido)}
|
||||
@@ -477,7 +556,7 @@
|
||||
</a>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={creating || selectedItems.length === 0}
|
||||
disabled={creating || selectedItems.length === 0 || hasMixedModalidades}
|
||||
class="rounded-lg bg-blue-600 px-6 py-2.5 font-semibold text-white shadow-md transition hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{creating ? 'Criando...' : 'Criar Pedido'}
|
||||
|
||||
Reference in New Issue
Block a user