feat: Add 'atas' (minutes/records) management feature, and implement various improvements across UI, backend logic, and authentication.

This commit is contained in:
2025-12-02 16:37:48 -03:00
parent 05e7f1181d
commit 4bd9e21748
265 changed files with 29156 additions and 26460 deletions

View File

@@ -1,19 +1,18 @@
<script lang="ts">
import { useQuery, useConvexClient } from 'convex-svelte';
import { api } from '@sgse-app/backend/convex/_generated/api';
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
import ActionGuard from '$lib/components/ActionGuard.svelte';
import { useConvexClient, useQuery } from 'convex-svelte';
import { goto } from '$app/navigation';
import ActionGuard from '$lib/components/ActionGuard.svelte';
const client = useConvexClient();
// Estado do filtro
let statusFilter = $state<'draft' | 'published' | 'archived' | undefined>(undefined);
const statusFilter = $state<'draft' | 'published' | 'archived' | undefined>(undefined);
// Query de templates
const templatesQuery = useQuery(
api.flows.listTemplates,
() => (statusFilter ? { status: statusFilter } : {})
const templatesQuery = useQuery(api.flows.listTemplates, () =>
statusFilter ? { status: statusFilter } : {}
);
// Modal de criação
@@ -25,7 +24,10 @@
// Modal de confirmação de exclusão
let showDeleteModal = $state(false);
let templateToDelete = $state<{ _id: Id<'flowTemplates'>; name: string } | null>(null);
let templateToDelete = $state<{
_id: Id<'flowTemplates'>;
name: string;
} | null>(null);
let isDeleting = $state(false);
let deleteError = $state<string | null>(null);
@@ -86,7 +88,9 @@
deleteError = null;
try {
await client.mutation(api.flows.deleteTemplate, { id: templateToDelete._id });
await client.mutation(api.flows.deleteTemplate, {
id: templateToDelete._id
});
closeDeleteModal();
} catch (e) {
deleteError = e instanceof Error ? e.message : 'Erro ao excluir template';
@@ -95,7 +99,10 @@
}
}
async function handleStatusChange(templateId: Id<'flowTemplates'>, newStatus: 'draft' | 'published' | 'archived') {
async function handleStatusChange(
templateId: Id<'flowTemplates'>,
newStatus: 'draft' | 'published' | 'archived'
) {
try {
await client.mutation(api.flows.updateTemplate, {
id: templateId,
@@ -150,10 +157,7 @@
</div>
<div class="flex flex-col gap-4 sm:flex-row sm:items-center">
<!-- Filtro de status -->
<select
class="select select-bordered"
bind:value={statusFilter}
>
<select class="select select-bordered" bind:value={statusFilter}>
<option value={undefined}>Todos os status</option>
<option value="draft">Rascunho</option>
<option value="published">Publicado</option>
@@ -209,7 +213,9 @@
</svg>
<h3 class="text-base-content/70 mt-4 text-lg font-semibold">Nenhum template encontrado</h3>
<p class="text-base-content/50 mt-2">
{statusFilter ? 'Não há templates com este status.' : 'Clique em "Novo Template" para criar o primeiro.'}
{statusFilter
? 'Não há templates com este status.'
: 'Clique em "Novo Template" para criar o primeiro.'}
</p>
</div>
{:else}
@@ -226,21 +232,45 @@
</div>
{#if template.description}
<p class="text-base-content/60 text-sm line-clamp-2">
<p class="text-base-content/60 line-clamp-2 text-sm">
{template.description}
</p>
{/if}
<div class="text-base-content/50 mt-2 flex flex-wrap gap-4 text-xs">
<span class="flex items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
/>
</svg>
{template.stepsCount} passos
</span>
<span class="flex items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
{formatDate(template.createdAt)}
</span>
@@ -249,11 +279,26 @@
<div class="card-actions mt-4 justify-between">
<div class="dropdown">
<button class="btn btn-ghost btn-sm" aria-label="Alterar status">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z" />
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"
/>
</svg>
</button>
<ul class="dropdown-content menu bg-base-100 rounded-box z-10 w-52 p-2 shadow" role="menu">
<ul
class="dropdown-content menu bg-base-100 rounded-box z-10 w-52 p-2 shadow"
role="menu"
>
{#if template.status !== 'draft'}
<li>
<button onclick={() => handleStatusChange(template._id, 'draft')}>
@@ -283,12 +328,21 @@
</ul>
</div>
<a
href="/fluxos/{template._id}/editor"
class="btn btn-secondary btn-sm"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
<a href="/fluxos/{template._id}/editor" class="btn btn-secondary btn-sm">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
/>
</svg>
Editar
</a>
@@ -303,8 +357,20 @@
<!-- Link para Instâncias -->
<section class="flex justify-center">
<a href="/licitacoes/fluxos" class="btn btn-outline btn-lg">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" />
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"
/>
</svg>
Ver Fluxos de Trabalho
</a>
@@ -337,7 +403,13 @@
</div>
{/if}
<form onsubmit={(e) => { e.preventDefault(); handleCreate(); }} class="mt-4 space-y-4">
<form
onsubmit={(e) => {
e.preventDefault();
handleCreate();
}}
class="mt-4 space-y-4"
>
<div class="form-control">
<label class="label" for="template-name">
<span class="label-text">Nome do Template</span>
@@ -378,7 +450,12 @@
</div>
</form>
</div>
<button type="button" class="modal-backdrop" onclick={closeCreateModal} aria-label="Fechar modal"></button>
<button
type="button"
class="modal-backdrop"
onclick={closeCreateModal}
aria-label="Fechar modal"
></button>
</div>
{/if}
@@ -386,7 +463,7 @@
{#if showDeleteModal && templateToDelete}
<div class="modal modal-open">
<div class="modal-box">
<h3 class="text-lg font-bold text-error">Confirmar Exclusão</h3>
<h3 class="text-error text-lg font-bold">Confirmar Exclusão</h3>
{#if deleteError}
<div class="alert alert-error mt-4">
@@ -412,13 +489,12 @@
Tem certeza que deseja excluir o template <strong>{templateToDelete.name}</strong>?
</p>
<p class="text-base-content/60 text-sm">
Esta ação não pode ser desfeita. Templates com instâncias vinculadas não podem ser excluídos.
Esta ação não pode ser desfeita. Templates com instâncias vinculadas não podem ser
excluídos.
</p>
<div class="modal-action">
<button class="btn" onclick={closeDeleteModal} disabled={isDeleting}>
Cancelar
</button>
<button class="btn" onclick={closeDeleteModal} disabled={isDeleting}> Cancelar </button>
<button class="btn btn-error" onclick={handleDelete} disabled={isDeleting}>
{#if isDeleting}
<span class="loading loading-spinner loading-sm"></span>
@@ -427,7 +503,11 @@
</button>
</div>
</div>
<button type="button" class="modal-backdrop" onclick={closeDeleteModal} aria-label="Fechar modal"></button>
<button
type="button"
class="modal-backdrop"
onclick={closeDeleteModal}
aria-label="Fechar modal"
></button>
</div>
{/if}