feat: Implement initial pedido (order) management, product catalog, and TI configuration features.
This commit is contained in:
157
apps/web/src/routes/(dashboard)/pedidos/+page.svelte
Normal file
157
apps/web/src/routes/(dashboard)/pedidos/+page.svelte
Normal file
@@ -0,0 +1,157 @@
|
||||
<script lang="ts">
|
||||
import { useQuery } from 'convex-svelte';
|
||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||
import { Plus, Eye } from 'lucide-svelte';
|
||||
import { resolve } from '$app/paths';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
// Reactive queries
|
||||
const pedidosQuery = useQuery(api.pedidos.list, {});
|
||||
const acoesQuery = useQuery(api.acoes.list, {});
|
||||
|
||||
const pedidos = $derived(pedidosQuery.data || []);
|
||||
const acoes = $derived(acoesQuery.data || []);
|
||||
const loading = $derived(pedidosQuery.isLoading || acoesQuery.isLoading);
|
||||
const error = $derived(pedidosQuery.error?.message || acoesQuery.error?.message || null);
|
||||
|
||||
function getAcaoNome(acaoId: Id<'acoes'> | undefined) {
|
||||
if (!acaoId) return '-';
|
||||
const acao = acoes.find((a) => a._id === acaoId);
|
||||
return acao ? acao.nome : '-';
|
||||
}
|
||||
|
||||
function formatStatus(status: string) {
|
||||
switch (status) {
|
||||
case 'em_rascunho':
|
||||
return 'Rascunho';
|
||||
case 'aguardando_aceite':
|
||||
return 'Aguardando Aceite';
|
||||
case 'em_analise':
|
||||
return 'Em Análise';
|
||||
case 'precisa_ajustes':
|
||||
return 'Precisa de Ajustes';
|
||||
case 'concluido':
|
||||
return 'Concluído';
|
||||
case 'cancelado':
|
||||
return 'Cancelado';
|
||||
default:
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
function getStatusColor(status: string) {
|
||||
switch (status) {
|
||||
case 'em_rascunho':
|
||||
return 'bg-gray-100 text-gray-800';
|
||||
case 'aguardando_aceite':
|
||||
return 'bg-yellow-100 text-yellow-800';
|
||||
case 'em_analise':
|
||||
return 'bg-blue-100 text-blue-800';
|
||||
case 'precisa_ajustes':
|
||||
return 'bg-orange-100 text-orange-800';
|
||||
case 'concluido':
|
||||
return 'bg-green-100 text-green-800';
|
||||
case 'cancelado':
|
||||
return 'bg-red-100 text-red-800';
|
||||
default:
|
||||
return 'bg-gray-100 text-gray-800';
|
||||
}
|
||||
}
|
||||
|
||||
function formatDate(timestamp: number) {
|
||||
return new Date(timestamp).toLocaleString('pt-BR');
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container mx-auto p-6">
|
||||
<div class="mb-6 flex items-center justify-between">
|
||||
<h1 class="text-2xl font-bold">Pedidos</h1>
|
||||
<a
|
||||
href={resolve('/pedidos/novo')}
|
||||
class="flex items-center gap-2 rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700"
|
||||
>
|
||||
<Plus size={20} />
|
||||
Novo Pedido
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{#if loading}
|
||||
<p>Carregando...</p>
|
||||
{:else if error}
|
||||
<p class="text-red-600">{error}</p>
|
||||
{:else}
|
||||
<div class="overflow-hidden rounded-lg bg-white shadow-md">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th
|
||||
class="px-6 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase"
|
||||
>Número SEI</th
|
||||
>
|
||||
<th
|
||||
class="px-6 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase"
|
||||
>Status</th
|
||||
>
|
||||
<th
|
||||
class="px-6 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase"
|
||||
>Ação</th
|
||||
>
|
||||
<th
|
||||
class="px-6 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase"
|
||||
>Data de Criação</th
|
||||
>
|
||||
<th
|
||||
class="px-6 py-3 text-right text-xs font-medium tracking-wider text-gray-500 uppercase"
|
||||
>Ações</th
|
||||
>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 bg-white">
|
||||
{#each pedidos as pedido (pedido._id)}
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="px-6 py-4 font-medium whitespace-nowrap">
|
||||
{#if pedido.numeroSei}
|
||||
{pedido.numeroSei}
|
||||
{:else}
|
||||
<span class="text-amber-600">Sem número SEI</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<span
|
||||
class="inline-flex rounded-full px-2 py-1 text-xs font-semibold {getStatusColor(
|
||||
pedido.status
|
||||
)}"
|
||||
>
|
||||
{formatStatus(pedido.status)}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-gray-500">
|
||||
{getAcaoNome(pedido.acaoId)}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-gray-500">
|
||||
{formatDate(pedido.criadoEm)}
|
||||
</td>
|
||||
<td class="px-6 py-4 text-right text-sm font-medium whitespace-nowrap">
|
||||
<a
|
||||
href={resolve(`/pedidos/${pedido._id}`)}
|
||||
class="inline-flex items-center gap-1 text-indigo-600 hover:text-indigo-900"
|
||||
>
|
||||
<Eye size={18} />
|
||||
Visualizar
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
{#if pedidos.length === 0}
|
||||
<tr>
|
||||
<td colspan="5" class="px-6 py-4 text-center text-gray-500"
|
||||
>Nenhum pedido cadastrado.</td
|
||||
>
|
||||
</tr>
|
||||
{/if}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
Reference in New Issue
Block a user