feat: Add 'atas' (minutes/records) management feature, and implement various improvements across UI, backend logic, and authentication.
This commit is contained in:
@@ -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}
|
||||
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { resolve } from '$app/paths';
|
||||
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 { useConvexClient, useQuery } from 'convex-svelte';
|
||||
import { resolve } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import ActionGuard from '$lib/components/ActionGuard.svelte';
|
||||
|
||||
const client = useConvexClient();
|
||||
const instanceId = $derived($page.params.id as Id<'flowInstances'>);
|
||||
|
||||
// Query da instância com passos
|
||||
const instanceQuery = useQuery(api.flows.getInstanceWithSteps, () => ({ id: instanceId }));
|
||||
const instanceQuery = useQuery(api.flows.getInstanceWithSteps, () => ({
|
||||
id: instanceId
|
||||
}));
|
||||
|
||||
// Query de usuários (para reatribuição) - será filtrado por setor no modal
|
||||
const usuariosQuery = useQuery(api.usuarios.listar, {});
|
||||
|
||||
|
||||
// Query de usuários por setor para atribuição
|
||||
let usuariosPorSetorQuery = $state<ReturnType<typeof useQuery<typeof api.flows.getUsuariosBySetorForAssignment>> | null>(null);
|
||||
const usuariosPorSetorQuery = $state<ReturnType<
|
||||
typeof useQuery<typeof api.flows.getUsuariosBySetorForAssignment>
|
||||
> | null>(null);
|
||||
|
||||
// Estado de operações
|
||||
let isProcessing = $state(false);
|
||||
@@ -24,17 +28,27 @@
|
||||
|
||||
// Modal de reatribuição
|
||||
let showReassignModal = $state(false);
|
||||
let stepToReassign = $state<{ _id: Id<'flowInstanceSteps'>; stepName: string } | null>(null);
|
||||
let stepToReassign = $state<{
|
||||
_id: Id<'flowInstanceSteps'>;
|
||||
stepName: string;
|
||||
} | null>(null);
|
||||
let newAssigneeId = $state<Id<'usuarios'> | ''>('');
|
||||
|
||||
// Modal de notas
|
||||
let showNotesModal = $state(false);
|
||||
let stepForNotes = $state<{ _id: Id<'flowInstanceSteps'>; stepName: string; notes: string } | null>(null);
|
||||
let stepForNotes = $state<{
|
||||
_id: Id<'flowInstanceSteps'>;
|
||||
stepName: string;
|
||||
notes: string;
|
||||
} | null>(null);
|
||||
let editedNotes = $state('');
|
||||
|
||||
// Modal de upload
|
||||
let showUploadModal = $state(false);
|
||||
let stepForUpload = $state<{ _id: Id<'flowInstanceSteps'>; stepName: string } | null>(null);
|
||||
let stepForUpload = $state<{
|
||||
_id: Id<'flowInstanceSteps'>;
|
||||
stepName: string;
|
||||
} | null>(null);
|
||||
let uploadFile = $state<File | null>(null);
|
||||
let isUploading = $state(false);
|
||||
|
||||
@@ -88,7 +102,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
function openReassignModal(step: { _id: Id<'flowInstanceSteps'>; stepName: string; assignedToId?: Id<'usuarios'> }) {
|
||||
function openReassignModal(step: {
|
||||
_id: Id<'flowInstanceSteps'>;
|
||||
stepName: string;
|
||||
assignedToId?: Id<'usuarios'>;
|
||||
}) {
|
||||
stepToReassign = step;
|
||||
newAssigneeId = step.assignedToId ?? '';
|
||||
showReassignModal = true;
|
||||
@@ -119,7 +137,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
function openNotesModal(step: { _id: Id<'flowInstanceSteps'>; stepName: string; notes?: string }) {
|
||||
function openNotesModal(step: {
|
||||
_id: Id<'flowInstanceSteps'>;
|
||||
stepName: string;
|
||||
notes?: string;
|
||||
}) {
|
||||
stepForNotes = { ...step, notes: step.notes ?? '' };
|
||||
editedNotes = step.notes ?? '';
|
||||
showNotesModal = true;
|
||||
@@ -286,11 +308,25 @@
|
||||
</div>
|
||||
{:else if !instanceQuery.data}
|
||||
<div class="flex flex-col items-center justify-center py-24 text-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="text-base-content/30 h-16 w-16" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="text-base-content/30 h-16 w-16"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<h3 class="text-base-content/70 mt-4 text-lg font-semibold">Fluxo não encontrado</h3>
|
||||
<a href={resolve('/(dashboard)/licitacoes/fluxos')} class="btn btn-ghost mt-4">Voltar para lista</a>
|
||||
<a href={resolve('/(dashboard)/licitacoes/fluxos')} class="btn btn-ghost mt-4"
|
||||
>Voltar para lista</a
|
||||
>
|
||||
</div>
|
||||
{:else}
|
||||
{@const instance = instanceQuery.data.instance}
|
||||
@@ -302,12 +338,26 @@
|
||||
class="border-info/25 from-info/10 via-base-100 to-secondary/20 relative overflow-hidden rounded-3xl border bg-linear-to-br p-8 shadow-2xl"
|
||||
>
|
||||
<div class="bg-info/20 absolute top-10 -left-10 h-40 w-40 rounded-full blur-3xl"></div>
|
||||
<div class="bg-secondary/20 absolute right-0 -bottom-16 h-56 w-56 rounded-full blur-3xl"></div>
|
||||
<div
|
||||
class="bg-secondary/20 absolute right-0 -bottom-16 h-56 w-56 rounded-full blur-3xl"
|
||||
></div>
|
||||
<div class="relative z-10">
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<div class="mb-6 flex items-center gap-4">
|
||||
<a href={resolve('/(dashboard)/licitacoes/fluxos')} class="btn btn-ghost btn-sm">
|
||||
<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="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||
<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="M10 19l-7-7m0 0l7-7m-7 7h18"
|
||||
/>
|
||||
</svg>
|
||||
Voltar
|
||||
</a>
|
||||
@@ -326,15 +376,39 @@
|
||||
<span class="text-base-content/70 font-medium">{instance.contratoId}</span>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex items-center gap-2 text-base-content/60">
|
||||
<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="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||
<div class="text-base-content/60 flex items-center gap-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="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
Gerente: {instance.managerName ?? '-'}
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-base-content/60">
|
||||
<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" />
|
||||
<div class="text-base-content/60 flex items-center gap-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="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>
|
||||
Iniciado: {formatDate(instance.startedAt)}
|
||||
</div>
|
||||
@@ -343,9 +417,21 @@
|
||||
|
||||
{#if instance.status === 'active'}
|
||||
<ActionGuard recurso="fluxos_instancias" acao="cancelar">
|
||||
<button class="btn btn-error btn-outline" onclick={() => showCancelModal = true}>
|
||||
<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="M6 18L18 6M6 6l12 12" />
|
||||
<button class="btn btn-error btn-outline" onclick={() => (showCancelModal = true)}>
|
||||
<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="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
Cancelar Fluxo
|
||||
</button>
|
||||
@@ -358,11 +444,23 @@
|
||||
<!-- Erro global -->
|
||||
{#if processingError}
|
||||
<div class="alert alert-error">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 shrink-0 stroke-current"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<span>{processingError}</span>
|
||||
<button class="btn btn-ghost btn-sm" onclick={() => processingError = null}>Fechar</button>
|
||||
<button class="btn btn-ghost btn-sm" onclick={() => (processingError = null)}>Fechar</button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -379,18 +477,55 @@
|
||||
<div class="relative flex gap-6 {index < steps.length - 1 ? 'pb-6' : ''}">
|
||||
<!-- Linha conectora -->
|
||||
{#if index < steps.length - 1}
|
||||
<div class="absolute left-5 top-10 bottom-0 w-0.5 {step.status === 'completed' ? 'bg-success' : 'bg-base-300'}"></div>
|
||||
<div
|
||||
class="absolute top-10 bottom-0 left-5 w-0.5 {step.status === 'completed'
|
||||
? 'bg-success'
|
||||
: 'bg-base-300'}"
|
||||
></div>
|
||||
{/if}
|
||||
|
||||
<!-- Indicador de status -->
|
||||
<div class="z-10 flex h-10 w-10 shrink-0 items-center justify-center rounded-full {step.status === 'completed' ? 'bg-success text-success-content' : isCurrent ? 'bg-info text-info-content' : step.status === 'blocked' ? 'bg-error text-error-content' : 'bg-base-300 text-base-content'}">
|
||||
<div
|
||||
class="z-10 flex h-10 w-10 shrink-0 items-center justify-center rounded-full {step.status ===
|
||||
'completed'
|
||||
? 'bg-success text-success-content'
|
||||
: isCurrent
|
||||
? 'bg-info text-info-content'
|
||||
: step.status === 'blocked'
|
||||
? 'bg-error text-error-content'
|
||||
: 'bg-base-300 text-base-content'}"
|
||||
>
|
||||
{#if step.status === 'completed'}
|
||||
<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="M5 13l4 4L19 7" />
|
||||
<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="M5 13l4 4L19 7"
|
||||
/>
|
||||
</svg>
|
||||
{:else if step.status === 'blocked'}
|
||||
<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="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
<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="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/>
|
||||
</svg>
|
||||
{:else}
|
||||
<span class="text-sm font-bold">{index + 1}</span>
|
||||
@@ -398,7 +533,11 @@
|
||||
</div>
|
||||
|
||||
<!-- Conteúdo do passo -->
|
||||
<div class="flex-1 rounded-xl border {isCurrent ? 'border-info bg-info/5' : 'bg-base-200/50'} p-4">
|
||||
<div
|
||||
class="flex-1 rounded-xl border {isCurrent
|
||||
? 'border-info bg-info/5'
|
||||
: 'bg-base-200/50'} p-4"
|
||||
>
|
||||
<div class="flex flex-wrap items-start justify-between gap-4">
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -413,23 +552,59 @@
|
||||
{/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-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-3 w-3"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5"
|
||||
/>
|
||||
</svg>
|
||||
{step.setorNome ?? 'Setor não definido'}
|
||||
</span>
|
||||
{#if step.assignedToName}
|
||||
<span class="flex items-center gap-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-3 w-3"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
{step.assignedToName}
|
||||
</span>
|
||||
{/if}
|
||||
{#if step.dueDate}
|
||||
<span class="flex items-center gap-1 {overdue ? 'text-warning' : ''}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-3 w-3"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
Prazo: {formatDate(step.dueDate)}
|
||||
</span>
|
||||
@@ -485,8 +660,20 @@
|
||||
onclick={() => openReassignModal(step)}
|
||||
aria-label="Reatribuir responsável"
|
||||
>
|
||||
<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="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||
<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="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</ActionGuard>
|
||||
@@ -496,8 +683,20 @@
|
||||
onclick={() => openNotesModal(step)}
|
||||
aria-label="Editar notas"
|
||||
>
|
||||
<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
|
||||
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>
|
||||
</button>
|
||||
|
||||
@@ -507,8 +706,20 @@
|
||||
onclick={() => openUploadModal(step)}
|
||||
aria-label="Upload de documento"
|
||||
>
|
||||
<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="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
|
||||
<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="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</ActionGuard>
|
||||
@@ -526,12 +737,26 @@
|
||||
<!-- Documentos -->
|
||||
{#if step.documents && step.documents.length > 0}
|
||||
<div class="mt-4">
|
||||
<h4 class="text-base-content/70 mb-2 text-xs font-semibold uppercase">Documentos</h4>
|
||||
<h4 class="text-base-content/70 mb-2 text-xs font-semibold uppercase">
|
||||
Documentos
|
||||
</h4>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#each step.documents as doc (doc._id)}
|
||||
<div class="badge badge-outline gap-2 py-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-3 w-3"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||
/>
|
||||
</svg>
|
||||
{doc.name}
|
||||
<ActionGuard recurso="fluxos_documentos" acao="excluir">
|
||||
@@ -596,10 +821,12 @@
|
||||
</div>
|
||||
|
||||
<div class="modal-action">
|
||||
<button class="btn" onclick={closeReassignModal} disabled={isProcessing}>
|
||||
Cancelar
|
||||
</button>
|
||||
<button class="btn btn-primary" onclick={handleReassign} disabled={isProcessing || !newAssigneeId}>
|
||||
<button class="btn" onclick={closeReassignModal} disabled={isProcessing}> Cancelar </button>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
onclick={handleReassign}
|
||||
disabled={isProcessing || !newAssigneeId}
|
||||
>
|
||||
{#if isProcessing}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
{/if}
|
||||
@@ -607,7 +834,12 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="modal-backdrop" onclick={closeReassignModal} aria-label="Fechar modal"></button>
|
||||
<button
|
||||
type="button"
|
||||
class="modal-backdrop"
|
||||
onclick={closeReassignModal}
|
||||
aria-label="Fechar modal"
|
||||
></button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -634,9 +866,7 @@
|
||||
</div>
|
||||
|
||||
<div class="modal-action">
|
||||
<button class="btn" onclick={closeNotesModal} disabled={isProcessing}>
|
||||
Cancelar
|
||||
</button>
|
||||
<button class="btn" onclick={closeNotesModal} disabled={isProcessing}> Cancelar </button>
|
||||
<button class="btn btn-primary" onclick={handleSaveNotes} disabled={isProcessing}>
|
||||
{#if isProcessing}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
@@ -645,7 +875,8 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="modal-backdrop" onclick={closeNotesModal} aria-label="Fechar modal"></button>
|
||||
<button type="button" class="modal-backdrop" onclick={closeNotesModal} aria-label="Fechar modal"
|
||||
></button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -677,10 +908,12 @@
|
||||
{/if}
|
||||
|
||||
<div class="modal-action">
|
||||
<button class="btn" onclick={closeUploadModal} disabled={isUploading}>
|
||||
Cancelar
|
||||
</button>
|
||||
<button class="btn btn-primary" onclick={handleUpload} disabled={isUploading || !uploadFile}>
|
||||
<button class="btn" onclick={closeUploadModal} disabled={isUploading}> Cancelar </button>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
onclick={handleUpload}
|
||||
disabled={isUploading || !uploadFile}
|
||||
>
|
||||
{#if isUploading}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
{/if}
|
||||
@@ -688,7 +921,12 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="modal-backdrop" onclick={closeUploadModal} aria-label="Fechar modal"></button>
|
||||
<button
|
||||
type="button"
|
||||
class="modal-backdrop"
|
||||
onclick={closeUploadModal}
|
||||
aria-label="Fechar modal"
|
||||
></button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -696,16 +934,14 @@
|
||||
{#if showCancelModal}
|
||||
<div class="modal modal-open">
|
||||
<div class="modal-box">
|
||||
<h3 class="text-lg font-bold text-error">Cancelar Fluxo</h3>
|
||||
<p class="py-4">
|
||||
Tem certeza que deseja cancelar este fluxo?
|
||||
</p>
|
||||
<h3 class="text-error text-lg font-bold">Cancelar Fluxo</h3>
|
||||
<p class="py-4">Tem certeza que deseja cancelar este fluxo?</p>
|
||||
<p class="text-base-content/60 text-sm">
|
||||
Esta ação não pode ser desfeita. Todos os passos pendentes serão marcados como cancelados.
|
||||
</p>
|
||||
|
||||
<div class="modal-action">
|
||||
<button class="btn" onclick={() => showCancelModal = false} disabled={isProcessing}>
|
||||
<button class="btn" onclick={() => (showCancelModal = false)} disabled={isProcessing}>
|
||||
Voltar
|
||||
</button>
|
||||
<button class="btn btn-error" onclick={handleCancelInstance} disabled={isProcessing}>
|
||||
@@ -716,7 +952,11 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="modal-backdrop" onclick={() => showCancelModal = false} aria-label="Fechar modal"></button>
|
||||
<button
|
||||
type="button"
|
||||
class="modal-backdrop"
|
||||
onclick={() => (showCancelModal = false)}
|
||||
aria-label="Fechar modal"
|
||||
></button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { resolve } from '$app/paths';
|
||||
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 { useConvexClient, useQuery } from 'convex-svelte';
|
||||
import { flip } from 'svelte/animate';
|
||||
import { resolve } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
const client = useConvexClient();
|
||||
const templateId = $derived($page.params.id as Id<'flowTemplates'>);
|
||||
|
||||
// Queries
|
||||
const templateQuery = useQuery(api.flows.getTemplate, () => ({ id: templateId }));
|
||||
const stepsQuery = useQuery(api.flows.listStepsByTemplate, () => ({ flowTemplateId: templateId }));
|
||||
const templateQuery = useQuery(api.flows.getTemplate, () => ({
|
||||
id: templateId
|
||||
}));
|
||||
const stepsQuery = useQuery(api.flows.listStepsByTemplate, () => ({
|
||||
flowTemplateId: templateId
|
||||
}));
|
||||
const setoresQuery = useQuery(api.setores.list, {});
|
||||
|
||||
|
||||
// Query de sub-etapas (reativa baseada no step selecionado)
|
||||
const subEtapasQuery = useQuery(
|
||||
api.flows.listarSubEtapas,
|
||||
() => selectedStepId ? { flowStepId: selectedStepId } : 'skip'
|
||||
const subEtapasQuery = useQuery(api.flows.listarSubEtapas, () =>
|
||||
selectedStepId ? { flowStepId: selectedStepId } : 'skip'
|
||||
);
|
||||
|
||||
// Estado local para drag and drop
|
||||
@@ -130,7 +133,8 @@
|
||||
description: editingStep.description || undefined,
|
||||
expectedDuration: editingStep.expectedDuration,
|
||||
setorId: editingStep.setorId,
|
||||
requiredDocuments: editingStep.requiredDocuments.length > 0 ? editingStep.requiredDocuments : undefined
|
||||
requiredDocuments:
|
||||
editingStep.requiredDocuments.length > 0 ? editingStep.requiredDocuments : undefined
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Erro ao salvar passo:', e);
|
||||
@@ -152,7 +156,7 @@
|
||||
|
||||
async function moveStepUp(index: number) {
|
||||
if (index === 0 || !localSteps) return;
|
||||
|
||||
|
||||
const previousSteps = [...localSteps];
|
||||
const newSteps = [...localSteps];
|
||||
[newSteps[index - 1], newSteps[index]] = [newSteps[index], newSteps[index - 1]];
|
||||
@@ -176,7 +180,7 @@
|
||||
|
||||
async function moveStepDown(index: number) {
|
||||
if (!localSteps || index === localSteps.length - 1) return;
|
||||
|
||||
|
||||
const previousSteps = [...localSteps];
|
||||
const newSteps = [...localSteps];
|
||||
[newSteps[index], newSteps[index + 1]] = [newSteps[index + 1], newSteps[index]];
|
||||
@@ -279,7 +283,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function handleAtualizarStatusSubEtapa(subEtapaId: Id<'flowSubSteps'>, novoStatus: 'pending' | 'in_progress' | 'completed' | 'blocked') {
|
||||
async function handleAtualizarStatusSubEtapa(
|
||||
subEtapaId: Id<'flowSubSteps'>,
|
||||
novoStatus: 'pending' | 'in_progress' | 'completed' | 'blocked'
|
||||
) {
|
||||
try {
|
||||
await client.mutation(api.flows.atualizarSubEtapa, {
|
||||
subEtapaId,
|
||||
@@ -297,14 +304,26 @@
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<a href={resolve('/(dashboard)/fluxos')} class="btn btn-ghost btn-sm">
|
||||
<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="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||
<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="M10 19l-7-7m0 0l7-7m-7 7h18"
|
||||
/>
|
||||
</svg>
|
||||
Voltar
|
||||
</a>
|
||||
<div>
|
||||
{#if templateQuery.isLoading}
|
||||
<div class="h-6 w-48 animate-pulse rounded bg-base-300"></div>
|
||||
<div class="bg-base-300 h-6 w-48 animate-pulse rounded"></div>
|
||||
{:else if templateQuery.data}
|
||||
<h1 class="text-xl font-bold">{templateQuery.data.name}</h1>
|
||||
<p class="text-base-content/60 text-sm">
|
||||
@@ -321,8 +340,20 @@
|
||||
onclick={handlePublish}
|
||||
disabled={!localSteps || localSteps.length === 0}
|
||||
>
|
||||
<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="M5 13l4 4L19 7" />
|
||||
<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="M5 13l4 4L19 7"
|
||||
/>
|
||||
</svg>
|
||||
Publicar
|
||||
</button>
|
||||
@@ -342,8 +373,20 @@
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold">Passos do Fluxo</h2>
|
||||
<button class="btn btn-secondary btn-sm" onclick={openNewStepModal}>
|
||||
<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 4v16m8-8H4" />
|
||||
<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 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Novo Passo
|
||||
</button>
|
||||
@@ -354,28 +397,49 @@
|
||||
<span class="loading loading-spinner loading-lg text-secondary"></span>
|
||||
</div>
|
||||
{:else if !localSteps || localSteps.length === 0}
|
||||
<div class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed py-12 text-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="text-base-content/30 h-12 w-12" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" 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" />
|
||||
<div
|
||||
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed py-12 text-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="text-base-content/30 h-12 w-12"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
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>
|
||||
<p class="text-base-content/60 mt-4">Nenhum passo definido</p>
|
||||
<p class="text-base-content/40 text-sm">Clique em "Novo Passo" para adicionar o primeiro passo</p>
|
||||
<p class="text-base-content/40 text-sm">
|
||||
Clique em "Novo Passo" para adicionar o primeiro passo
|
||||
</p>
|
||||
</div>
|
||||
{:else if localSteps && localSteps.length > 0}
|
||||
<div class="space-y-3">
|
||||
{#each localSteps as step, index (step._id)}
|
||||
<div
|
||||
class="card w-full border text-left transition-all duration-200 {selectedStepId === step._id ? 'border-secondary bg-secondary/10 ring-2 ring-secondary' : 'bg-base-100 hover:bg-base-200'}"
|
||||
class="card w-full border text-left transition-all duration-200 {selectedStepId ===
|
||||
step._id
|
||||
? 'border-secondary bg-secondary/10 ring-secondary ring-2'
|
||||
: 'bg-base-100 hover:bg-base-200'}"
|
||||
animate:flip={{ duration: 200 }}
|
||||
>
|
||||
<div class="card-body p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="bg-secondary/20 text-secondary flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-sm font-bold">
|
||||
<div
|
||||
class="bg-secondary/20 text-secondary flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-sm font-bold"
|
||||
>
|
||||
{index + 1}
|
||||
</div>
|
||||
<div
|
||||
<div
|
||||
class="min-w-0 flex-1 cursor-pointer"
|
||||
onclick={() => selectedStepId = step._id}
|
||||
onclick={() => (selectedStepId = step._id)}
|
||||
onkeydown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
@@ -391,14 +455,38 @@
|
||||
{/if}
|
||||
<div class="text-base-content/50 mt-2 flex flex-wrap gap-3 text-xs">
|
||||
<span class="flex items-center gap-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-3 w-3"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5"
|
||||
/>
|
||||
</svg>
|
||||
{step.setorNome ?? 'Setor não definido'}
|
||||
</span>
|
||||
<span class="flex items-center gap-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-3 w-3"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
{step.expectedDuration} dia{step.expectedDuration > 1 ? 's' : ''}
|
||||
</span>
|
||||
@@ -412,8 +500,20 @@
|
||||
disabled={index === 0 || isDragging}
|
||||
aria-label="Mover passo para cima"
|
||||
>
|
||||
<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="M5 15l7-7 7 7" />
|
||||
<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="M5 15l7-7 7 7"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
@@ -423,8 +523,20 @@
|
||||
disabled={index === localSteps.length - 1 || isDragging}
|
||||
aria-label="Mover passo para baixo"
|
||||
>
|
||||
<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="M19 9l-7 7-7-7" />
|
||||
<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="M19 9l-7 7-7-7"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -444,11 +556,23 @@
|
||||
<h3 class="text-lg font-semibold">Editar Passo</h3>
|
||||
<button
|
||||
class="btn btn-ghost btn-sm"
|
||||
onclick={() => selectedStepId = null}
|
||||
onclick={() => (selectedStepId = null)}
|
||||
aria-label="Fechar edição"
|
||||
>
|
||||
<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="M6 18L18 6M6 6l12 12" />
|
||||
<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="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -527,8 +651,20 @@
|
||||
onclick={() => removeRequiredDocument(index)}
|
||||
aria-label="Remover documento"
|
||||
>
|
||||
<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="M6 18L18 6M6 6l12 12" />
|
||||
<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="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -538,8 +674,20 @@
|
||||
class="btn btn-ghost btn-sm w-full"
|
||||
onclick={addRequiredDocument}
|
||||
>
|
||||
<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 4v16m8-8H4" />
|
||||
<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 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Adicionar Documento
|
||||
</button>
|
||||
@@ -556,8 +704,20 @@
|
||||
onclick={openSubEtapaModal}
|
||||
aria-label="Adicionar sub-etapa"
|
||||
>
|
||||
<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 4v16m8-8H4" />
|
||||
<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 4v16m8-8H4"
|
||||
/>
|
||||
</svg>
|
||||
Adicionar
|
||||
</button>
|
||||
@@ -569,15 +729,31 @@
|
||||
</div>
|
||||
{:else if subEtapasQuery.data && subEtapasQuery.data.length > 0}
|
||||
{#each subEtapasQuery.data as subEtapa (subEtapa._id)}
|
||||
<div class="flex items-center gap-2 rounded-lg border border-base-300 bg-base-100 p-2">
|
||||
<div
|
||||
class="border-base-300 bg-base-100 flex items-center gap-2 rounded-lg border p-2"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-sm">{subEtapa.name}</div>
|
||||
<div class="text-sm font-medium">{subEtapa.name}</div>
|
||||
{#if subEtapa.description}
|
||||
<div class="text-base-content/60 text-xs">{subEtapa.description}</div>
|
||||
{/if}
|
||||
<div class="mt-1">
|
||||
<span class="badge badge-xs {subEtapa.status === 'completed' ? 'badge-success' : subEtapa.status === 'in_progress' ? 'badge-info' : subEtapa.status === 'blocked' ? 'badge-error' : 'badge-ghost'}">
|
||||
{subEtapa.status === 'completed' ? 'Concluída' : subEtapa.status === 'in_progress' ? 'Em Andamento' : subEtapa.status === 'blocked' ? 'Bloqueada' : 'Pendente'}
|
||||
<span
|
||||
class="badge badge-xs {subEtapa.status === 'completed'
|
||||
? 'badge-success'
|
||||
: subEtapa.status === 'in_progress'
|
||||
? 'badge-info'
|
||||
: subEtapa.status === 'blocked'
|
||||
? 'badge-error'
|
||||
: 'badge-ghost'}"
|
||||
>
|
||||
{subEtapa.status === 'completed'
|
||||
? 'Concluída'
|
||||
: subEtapa.status === 'in_progress'
|
||||
? 'Em Andamento'
|
||||
: subEtapa.status === 'blocked'
|
||||
? 'Bloqueada'
|
||||
: 'Pendente'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -585,7 +761,15 @@
|
||||
<select
|
||||
class="select select-xs select-bordered"
|
||||
value={subEtapa.status}
|
||||
onchange={(e) => handleAtualizarStatusSubEtapa(subEtapa._id, e.currentTarget.value as 'pending' | 'in_progress' | 'completed' | 'blocked')}
|
||||
onchange={(e) =>
|
||||
handleAtualizarStatusSubEtapa(
|
||||
subEtapa._id,
|
||||
e.currentTarget.value as
|
||||
| 'pending'
|
||||
| 'in_progress'
|
||||
| 'completed'
|
||||
| 'blocked'
|
||||
)}
|
||||
>
|
||||
<option value="pending">Pendente</option>
|
||||
<option value="in_progress">Em Andamento</option>
|
||||
@@ -598,15 +782,29 @@
|
||||
onclick={() => handleDeletarSubEtapa(subEtapa._id)}
|
||||
aria-label="Deletar sub-etapa"
|
||||
>
|
||||
<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="M6 18L18 6M6 6l12 12" />
|
||||
<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="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="text-base-content/40 rounded-lg border border-dashed border-base-300 bg-base-200/50 p-4 text-center text-sm">
|
||||
<div
|
||||
class="text-base-content/40 border-base-300 bg-base-200/50 rounded-lg border border-dashed p-4 text-center text-sm"
|
||||
>
|
||||
Nenhuma sub-etapa adicionada
|
||||
</div>
|
||||
{/if}
|
||||
@@ -614,10 +812,7 @@
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 pt-4">
|
||||
<button
|
||||
class="btn btn-error btn-outline flex-1"
|
||||
onclick={handleDeleteStep}
|
||||
>
|
||||
<button class="btn btn-error btn-outline flex-1" onclick={handleDeleteStep}>
|
||||
Excluir
|
||||
</button>
|
||||
<button
|
||||
@@ -634,8 +829,20 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col items-center justify-center py-12 text-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="text-base-content/30 h-12 w-12" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="text-base-content/30 h-12 w-12"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"
|
||||
/>
|
||||
</svg>
|
||||
<p class="text-base-content/60 mt-4">Selecione um passo</p>
|
||||
<p class="text-base-content/40 text-sm">Clique em um passo para editar seus detalhes</p>
|
||||
@@ -653,14 +860,31 @@
|
||||
|
||||
{#if stepError}
|
||||
<div class="alert alert-error mt-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 shrink-0 stroke-current"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<span>{stepError}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<form onsubmit={(e) => { e.preventDefault(); handleCreateStep(); }} class="mt-4 space-y-4">
|
||||
<form
|
||||
onsubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleCreateStep();
|
||||
}}
|
||||
class="mt-4 space-y-4"
|
||||
>
|
||||
<div class="form-control">
|
||||
<label class="label" for="new-step-name">
|
||||
<span class="label-text">Nome do Passo</span>
|
||||
@@ -734,7 +958,12 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<button type="button" class="modal-backdrop" onclick={closeNewStepModal} aria-label="Fechar modal"></button>
|
||||
<button
|
||||
type="button"
|
||||
class="modal-backdrop"
|
||||
onclick={closeNewStepModal}
|
||||
aria-label="Fechar modal"
|
||||
></button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -746,14 +975,31 @@
|
||||
|
||||
{#if subEtapaError}
|
||||
<div class="alert alert-error mt-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-6 w-6 shrink-0 stroke-current"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<span>{subEtapaError}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<form onsubmit={(e) => { e.preventDefault(); handleCriarSubEtapa(); }} class="mt-4 space-y-4">
|
||||
<form
|
||||
onsubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleCriarSubEtapa();
|
||||
}}
|
||||
class="mt-4 space-y-4"
|
||||
>
|
||||
<div class="form-control">
|
||||
<label class="label" for="sub-etapa-nome">
|
||||
<span class="label-text">Nome da Sub-etapa</span>
|
||||
@@ -782,7 +1028,12 @@
|
||||
</div>
|
||||
|
||||
<div class="modal-action">
|
||||
<button type="button" class="btn" onclick={closeSubEtapaModal} disabled={isCriandoSubEtapa}>
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
onclick={closeSubEtapaModal}
|
||||
disabled={isCriandoSubEtapa}
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
<button type="submit" class="btn btn-secondary" disabled={isCriandoSubEtapa}>
|
||||
@@ -794,7 +1045,11 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<button type="button" class="modal-backdrop" onclick={closeSubEtapaModal} aria-label="Fechar modal"></button>
|
||||
<button
|
||||
type="button"
|
||||
class="modal-backdrop"
|
||||
onclick={closeSubEtapaModal}
|
||||
aria-label="Fechar modal"
|
||||
></button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
<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 dos filtros
|
||||
let statusFilter = $state<'active' | 'completed' | 'cancelled' | undefined>(undefined);
|
||||
const statusFilter = $state<'active' | 'completed' | 'cancelled' | undefined>(undefined);
|
||||
|
||||
// Query de instâncias
|
||||
const instancesQuery = useQuery(
|
||||
api.flows.listInstances,
|
||||
() => (statusFilter ? { status: statusFilter } : {})
|
||||
const instancesQuery = useQuery(api.flows.listInstances, () =>
|
||||
statusFilter ? { status: statusFilter } : {}
|
||||
);
|
||||
|
||||
// Query de templates publicados (para o modal de criação)
|
||||
const publishedTemplatesQuery = useQuery(api.flows.listTemplates, { status: 'published' });
|
||||
const publishedTemplatesQuery = useQuery(api.flows.listTemplates, {
|
||||
status: 'published'
|
||||
});
|
||||
|
||||
// Modal de criação
|
||||
let showCreateModal = $state(false);
|
||||
@@ -107,8 +108,20 @@
|
||||
<div class="max-w-3xl space-y-4">
|
||||
<div class="flex items-center gap-4">
|
||||
<a href="/fluxos" class="btn btn-ghost btn-sm">
|
||||
<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="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||
<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="M10 19l-7-7m0 0l7-7m-7 7h18"
|
||||
/>
|
||||
</svg>
|
||||
Templates
|
||||
</a>
|
||||
@@ -122,16 +135,13 @@
|
||||
Instâncias de Fluxo
|
||||
</h1>
|
||||
<p class="text-base-content/70 text-base leading-relaxed sm:text-lg">
|
||||
Acompanhe e gerencie as execuções de fluxos de trabalho. Visualize o progresso,
|
||||
documentos e responsáveis de cada etapa.
|
||||
Acompanhe e gerencie as execuções de fluxos de trabalho. Visualize o progresso, documentos
|
||||
e responsáveis de cada etapa.
|
||||
</p>
|
||||
</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="active">Em Andamento</option>
|
||||
<option value="completed">Concluído</option>
|
||||
@@ -185,9 +195,13 @@
|
||||
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>
|
||||
<h3 class="text-base-content/70 mt-4 text-lg font-semibold">Nenhuma instância encontrada</h3>
|
||||
<h3 class="text-base-content/70 mt-4 text-lg font-semibold">
|
||||
Nenhuma instância encontrada
|
||||
</h3>
|
||||
<p class="text-base-content/50 mt-2">
|
||||
{statusFilter ? 'Não há instâncias com este status.' : 'Clique em "Nova Instância" para iniciar um fluxo.'}
|
||||
{statusFilter
|
||||
? 'Não há instâncias com este status.'
|
||||
: 'Clique em "Nova Instância" para iniciar um fluxo.'}
|
||||
</p>
|
||||
</div>
|
||||
{:else}
|
||||
@@ -207,7 +221,10 @@
|
||||
<tbody>
|
||||
{#each instancesQuery.data as instance (instance._id)}
|
||||
{@const statusBadge = getStatusBadge(instance.status)}
|
||||
{@const progressPercent = getProgressPercentage(instance.progress.completed, instance.progress.total)}
|
||||
{@const progressPercent = getProgressPercentage(
|
||||
instance.progress.completed,
|
||||
instance.progress.total
|
||||
)}
|
||||
<tr class="hover">
|
||||
<td>
|
||||
<div class="font-medium">{instance.templateName ?? 'Template desconhecido'}</div>
|
||||
@@ -221,12 +238,9 @@
|
||||
<td class="text-base-content/70">{instance.managerName ?? '-'}</td>
|
||||
<td>
|
||||
<div class="flex items-center gap-2">
|
||||
<progress
|
||||
class="progress progress-info w-20"
|
||||
value={progressPercent}
|
||||
max="100"
|
||||
<progress class="progress progress-info w-20" value={progressPercent} max="100"
|
||||
></progress>
|
||||
<span class="text-xs text-base-content/60">
|
||||
<span class="text-base-content/60 text-xs">
|
||||
{instance.progress.completed}/{instance.progress.total}
|
||||
</span>
|
||||
</div>
|
||||
@@ -236,13 +250,27 @@
|
||||
</td>
|
||||
<td class="text-base-content/60 text-sm">{formatDate(instance.startedAt)}</td>
|
||||
<td class="text-right">
|
||||
<a
|
||||
href="/licitacoes/fluxos/{instance._id}"
|
||||
class="btn btn-ghost 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="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
<a href="/licitacoes/fluxos/{instance._id}" class="btn btn-ghost 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="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
/>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||
/>
|
||||
</svg>
|
||||
Ver
|
||||
</a>
|
||||
@@ -282,7 +310,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-select">
|
||||
<span class="label-text">Template de Fluxo</span>
|
||||
@@ -301,7 +335,9 @@
|
||||
{/if}
|
||||
</select>
|
||||
<p class="label">
|
||||
<span class="label-text-alt text-base-content/60">Apenas templates publicados podem ser instanciados</span>
|
||||
<span class="label-text-alt text-base-content/60"
|
||||
>Apenas templates publicados podem ser instanciados</span
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -367,7 +403,11 @@
|
||||
</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}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user