Ajuste chat #9

Merged
deyvisonwanderley merged 7 commits from ajuste_chat into master 2025-11-05 18:44:43 +00:00
36 changed files with 3245 additions and 2811 deletions
Showing only changes of commit c459297968 - Show all commits

View File

@@ -1,83 +1,71 @@
<script lang="ts"> <script lang="ts">
import { useQuery } from "convex-svelte"; import { useQuery } from "convex-svelte";
import { api } from "@sgse-app/backend/convex/_generated/api"; import { api } from "@sgse-app/backend/convex/_generated/api";
import type { Id } from "@sgse-app/backend/convex/_generated/dataModel"; import type { Id } from "@sgse-app/backend/convex/_generated/dataModel";
import { authStore } from "$lib/stores/auth.svelte"; import { authStore } from "$lib/stores/auth.svelte";
import { loginModalStore } from "$lib/stores/loginModal.svelte"; import { loginModalStore } from "$lib/stores/loginModal.svelte";
import { AlertTriangle } from "lucide-svelte";
interface Props {
recurso: string; interface Props {
acao: string; recurso: string;
children?: any; acao: string;
} children?: any;
}
let { recurso, acao, children }: Props = $props();
let { recurso, acao, children }: Props = $props();
let verificando = $state(true);
let permitido = $state(false); let verificando = $state(true);
let permitido = $state(false);
const permissaoQuery = $derived(
authStore.usuario const permissaoQuery = $derived(
? useQuery(api.permissoesAcoes.verificarAcao, { authStore.usuario
usuarioId: authStore.usuario._id as Id<"usuarios">, ? useQuery(api.permissoesAcoes.verificarAcao, {
recurso, usuarioId: authStore.usuario._id as Id<"usuarios">,
acao, recurso,
}) acao,
: null })
); : null
);
$effect(() => {
if (!authStore.autenticado) { $effect(() => {
verificando = false; if (!authStore.autenticado) {
permitido = false; verificando = false;
const currentPath = window.location.pathname; permitido = false;
loginModalStore.open(currentPath); const currentPath = window.location.pathname;
return; loginModalStore.open(currentPath);
} return;
}
if (permissaoQuery?.error) {
verificando = false; if (permissaoQuery?.error) {
permitido = false; verificando = false;
} else if (permissaoQuery && !permissaoQuery.isLoading) { permitido = false;
// Backend retorna null quando permitido } else if (permissaoQuery && !permissaoQuery.isLoading) {
verificando = false; // Backend retorna null quando permitido
permitido = true; verificando = false;
} permitido = true;
}); }
</script> });
</script>
{#if verificando}
<div class="flex items-center justify-center min-h-screen"> {#if verificando}
<div class="text-center"> <div class="flex items-center justify-center min-h-screen">
<span class="loading loading-spinner loading-lg text-primary"></span> <div class="text-center">
<p class="mt-4 text-base-content/70">Verificando permissões...</p> <span class="loading loading-spinner loading-lg text-primary"></span>
</div> <p class="mt-4 text-base-content/70">Verificando permissões...</p>
</div> </div>
{:else if permitido} </div>
{@render children?.()} {:else if permitido}
{:else} {@render children?.()}
<div class="flex items-center justify-center min-h-screen"> {:else}
<div class="text-center"> <div class="flex items-center justify-center min-h-screen">
<div class="p-4 bg-error/10 rounded-full inline-block mb-4"> <div class="text-center">
<svg <div class="p-4 bg-error/10 rounded-full inline-block mb-4">
xmlns="http://www.w3.org/2000/svg" <AlertTriangle class="h-16 w-16 text-error" strokeWidth={2} />
class="h-16 w-16 text-error" </div>
fill="none" <h2 class="text-2xl font-bold text-base-content mb-2">Acesso Negado</h2>
viewBox="0 0 24 24" <p class="text-base-content/70">
stroke="currentColor" Você não tem permissão para acessar esta ação.
> </p>
<path </div>
stroke-linecap="round" </div>
stroke-linejoin="round" {/if}
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>
</div>
<h2 class="text-2xl font-bold text-base-content mb-2">Acesso Negado</h2>
<p class="text-base-content/70">
Você não tem permissão para acessar esta ação.
</p>
</div>
</div>
{/if}

View File

@@ -1,4 +1,6 @@
<script lang="ts"> <script lang="ts">
import { AlertCircle } from "lucide-svelte";
interface Props { interface Props {
open: boolean; open: boolean;
title?: string; title?: string;
@@ -20,20 +22,7 @@
<div class="modal modal-open"> <div class="modal modal-open">
<div class="modal-box"> <div class="modal-box">
<h3 class="font-bold text-lg text-error mb-4 flex items-center gap-2"> <h3 class="font-bold text-lg text-error mb-4 flex items-center gap-2">
<svg <AlertCircle class="h-6 w-6" strokeWidth={2} />
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
{title} {title}
</h3> </h3>
<p class="py-4 text-base-content">{message}</p> <p class="py-4 text-base-content">{message}</p>

View File

@@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import { useConvexClient } from "convex-svelte"; import { useConvexClient } from "convex-svelte";
import { ExternalLink, FileText, File, Upload, Trash2, Eye, RefreshCw } from "lucide-svelte";
interface Props { interface Props {
label: string; label: string;
@@ -159,9 +160,7 @@
class="text-primary hover:text-primary-focus transition-colors" class="text-primary hover:text-primary-focus transition-colors"
aria-label="Acessar link" aria-label="Acessar link"
> >
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <ExternalLink class="h-4 w-4" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
</a> </a>
</div> </div>
{/if} {/if}
@@ -186,15 +185,11 @@
<img src={previewUrl} alt="Preview" class="w-12 h-12 object-cover rounded" /> <img src={previewUrl} alt="Preview" class="w-12 h-12 object-cover rounded" />
{:else if fileType === "application/pdf" || fileName.endsWith(".pdf")} {:else if fileType === "application/pdf" || fileName.endsWith(".pdf")}
<div class="w-12 h-12 bg-error/10 rounded flex items-center justify-center"> <div class="w-12 h-12 bg-error/10 rounded flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-error" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <FileText class="h-6 w-6 text-error" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
</svg>
</div> </div>
{:else} {:else}
<div class="w-12 h-12 bg-success/10 rounded flex items-center justify-center"> <div class="w-12 h-12 bg-success/10 rounded flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-success" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <File class="h-6 w-6 text-success" strokeWidth={2} />
<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>
</div> </div>
{/if} {/if}
</div> </div>
@@ -221,10 +216,7 @@
disabled={uploading || disabled} disabled={uploading || disabled}
title="Visualizar arquivo" title="Visualizar arquivo"
> >
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <Eye class="h-4 w-4" strokeWidth={2} />
<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>
</button> </button>
{/if} {/if}
<button <button
@@ -234,9 +226,7 @@
disabled={uploading || disabled} disabled={uploading || disabled}
title="Substituir arquivo" title="Substituir arquivo"
> >
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <RefreshCw class="h-4 w-4" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
</svg>
</button> </button>
<button <button
type="button" type="button"
@@ -245,9 +235,7 @@
disabled={uploading || disabled} disabled={uploading || disabled}
title="Remover arquivo" title="Remover arquivo"
> >
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <Trash2 class="h-4 w-4" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button> </button>
</div> </div>
</div> </div>
@@ -262,9 +250,7 @@
<span class="loading loading-spinner loading-sm"></span> <span class="loading loading-spinner loading-sm"></span>
Carregando... Carregando...
{:else} {:else}
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <Upload class="h-5 w-5" strokeWidth={2} />
<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>
Selecionar arquivo (PDF ou imagem, máx. 10MB) Selecionar arquivo (PDF ou imagem, máx. 10MB)
{/if} {/if}
</button> </button>

View File

@@ -1,162 +1,159 @@
<script lang="ts"> <script lang="ts">
import { modelosDeclaracoes } from "$lib/utils/modelosDeclaracoes"; import { modelosDeclaracoes } from "$lib/utils/modelosDeclaracoes";
import { import {
gerarDeclaracaoAcumulacaoCargo, gerarDeclaracaoAcumulacaoCargo,
gerarDeclaracaoDependentesIR, gerarDeclaracaoDependentesIR,
gerarDeclaracaoIdoneidade, gerarDeclaracaoIdoneidade,
gerarTermoNepotismo, gerarTermoNepotismo,
gerarTermoOpcaoRemuneracao, gerarTermoOpcaoRemuneracao,
downloadBlob downloadBlob
} from "$lib/utils/declaracoesGenerator"; } from "$lib/utils/declaracoesGenerator";
import { FileText, Info } from "lucide-svelte";
interface Props {
funcionario?: any; interface Props {
showPreencherButton?: boolean; funcionario?: any;
} showPreencherButton?: boolean;
}
let { funcionario, showPreencherButton = false }: Props = $props();
let generating = $state(false); let { funcionario, showPreencherButton = false }: Props = $props();
let generating = $state(false);
function baixarModelo(arquivoUrl: string, nomeModelo: string) {
const link = document.createElement('a'); function baixarModelo(arquivoUrl: string, nomeModelo: string) {
link.href = arquivoUrl; const link = document.createElement('a');
link.download = nomeModelo + '.pdf'; link.href = arquivoUrl;
link.target = '_blank'; link.download = nomeModelo + '.pdf';
document.body.appendChild(link); link.target = '_blank';
link.click(); document.body.appendChild(link);
document.body.removeChild(link); link.click();
} document.body.removeChild(link);
}
async function gerarPreenchido(modeloId: string) {
if (!funcionario) { async function gerarPreenchido(modeloId: string) {
alert('Dados do funcionário não disponíveis'); if (!funcionario) {
return; alert('Dados do funcionário não disponíveis');
} return;
}
try {
generating = true; try {
let blob: Blob; generating = true;
let nomeArquivo: string; let blob: Blob;
let nomeArquivo: string;
switch (modeloId) {
case 'acumulacao_cargo': switch (modeloId) {
blob = await gerarDeclaracaoAcumulacaoCargo(funcionario); case 'acumulacao_cargo':
nomeArquivo = `Declaracao_Acumulacao_Cargo_${funcionario.nome.replace(/ /g, '_')}_${Date.now()}.pdf`; blob = await gerarDeclaracaoAcumulacaoCargo(funcionario);
break; nomeArquivo = `Declaracao_Acumulacao_Cargo_${funcionario.nome.replace(/ /g, '_')}_${Date.now()}.pdf`;
break;
case 'dependentes_ir':
blob = await gerarDeclaracaoDependentesIR(funcionario); case 'dependentes_ir':
nomeArquivo = `Declaracao_Dependentes_IR_${funcionario.nome.replace(/ /g, '_')}_${Date.now()}.pdf`; blob = await gerarDeclaracaoDependentesIR(funcionario);
break; nomeArquivo = `Declaracao_Dependentes_IR_${funcionario.nome.replace(/ /g, '_')}_${Date.now()}.pdf`;
break;
case 'idoneidade':
blob = await gerarDeclaracaoIdoneidade(funcionario); case 'idoneidade':
nomeArquivo = `Declaracao_Idoneidade_${funcionario.nome.replace(/ /g, '_')}_${Date.now()}.pdf`; blob = await gerarDeclaracaoIdoneidade(funcionario);
break; nomeArquivo = `Declaracao_Idoneidade_${funcionario.nome.replace(/ /g, '_')}_${Date.now()}.pdf`;
break;
case 'nepotismo':
blob = await gerarTermoNepotismo(funcionario); case 'nepotismo':
nomeArquivo = `Termo_Nepotismo_${funcionario.nome.replace(/ /g, '_')}_${Date.now()}.pdf`; blob = await gerarTermoNepotismo(funcionario);
break; nomeArquivo = `Termo_Nepotismo_${funcionario.nome.replace(/ /g, '_')}_${Date.now()}.pdf`;
break;
case 'opcao_remuneracao':
blob = await gerarTermoOpcaoRemuneracao(funcionario); case 'opcao_remuneracao':
nomeArquivo = `Termo_Opcao_Remuneracao_${funcionario.nome.replace(/ /g, '_')}_${Date.now()}.pdf`; blob = await gerarTermoOpcaoRemuneracao(funcionario);
break; nomeArquivo = `Termo_Opcao_Remuneracao_${funcionario.nome.replace(/ /g, '_')}_${Date.now()}.pdf`;
break;
default:
alert('Modelo não encontrado'); default:
return; alert('Modelo não encontrado');
} return;
}
downloadBlob(blob, nomeArquivo);
} catch (error) { downloadBlob(blob, nomeArquivo);
console.error('Erro ao gerar declaração:', error); } catch (error) {
alert('Erro ao gerar declaração preenchida'); console.error('Erro ao gerar declaração:', error);
} finally { alert('Erro ao gerar declaração preenchida');
generating = false; } finally {
} generating = false;
} }
</script> }
</script>
<div class="card bg-base-100 shadow-xl">
<div class="card-body"> <div class="card bg-base-100 shadow-xl">
<h2 class="card-title text-xl border-b pb-3"> <div class="card-body">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <h2 class="card-title text-xl border-b pb-3">
<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" /> <FileText class="h-5 w-5" strokeWidth={2} />
</svg> Modelos de Declarações
Modelos de Declarações </h2>
</h2>
<div class="alert alert-info shadow-sm mb-4">
<div class="alert alert-info shadow-sm mb-4"> <Info class="stroke-current shrink-0 h-5 w-5" strokeWidth={2} />
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-5 w-5" fill="none" viewBox="0 0 24 24"> <div class="text-sm">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> <p class="font-semibold">Baixe os modelos, preencha, assine e faça upload no sistema</p>
</svg> <p class="text-xs opacity-80 mt-1">Estes documentos são necessários para completar o cadastro do funcionário</p>
<div class="text-sm"> </div>
<p class="font-semibold">Baixe os modelos, preencha, assine e faça upload no sistema</p> </div>
<p class="text-xs opacity-80 mt-1">Estes documentos são necessários para completar o cadastro do funcionário</p>
</div> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
</div> {#each modelosDeclaracoes as modelo}
<div class="card bg-base-200 shadow-sm hover:shadow-md transition-shadow">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div class="card-body p-4">
{#each modelosDeclaracoes as modelo} <div class="flex items-start gap-3">
<div class="card bg-base-200 shadow-sm hover:shadow-md transition-shadow"> <!-- Ícone PDF -->
<div class="card-body p-4"> <div class="flex-shrink-0 w-12 h-12 bg-error/10 rounded-lg flex items-center justify-center">
<div class="flex items-start gap-3"> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-error" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<!-- Ícone PDF --> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
<div class="flex-shrink-0 w-12 h-12 bg-error/10 rounded-lg flex items-center justify-center"> </svg>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-error" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
</svg> <!-- Conteúdo -->
</div> <div class="flex-1 min-w-0">
<h3 class="font-semibold text-sm mb-1 line-clamp-2">{modelo.nome}</h3>
<!-- Conteúdo --> <p class="text-xs text-base-content/70 mb-3 line-clamp-2">{modelo.descricao}</p>
<div class="flex-1 min-w-0">
<h3 class="font-semibold text-sm mb-1 line-clamp-2">{modelo.nome}</h3> <!-- Ações -->
<p class="text-xs text-base-content/70 mb-3 line-clamp-2">{modelo.descricao}</p> <div class="flex flex-col gap-2">
<button
<!-- Ações --> type="button"
<div class="flex flex-col gap-2"> class="btn btn-primary btn-xs gap-1"
<button onclick={() => baixarModelo(modelo.arquivo, modelo.nome)}
type="button" >
class="btn btn-primary btn-xs gap-1" <svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
onclick={() => baixarModelo(modelo.arquivo, modelo.nome)} <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
> </svg>
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"> Baixar Modelo
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /> </button>
</svg>
Baixar Modelo {#if showPreencherButton && modelo.podePreencherAutomaticamente && funcionario}
</button> <button
type="button"
{#if showPreencherButton && modelo.podePreencherAutomaticamente && funcionario} class="btn btn-outline btn-xs gap-1"
<button onclick={() => gerarPreenchido(modelo.id)}
type="button" disabled={generating}
class="btn btn-outline btn-xs gap-1" >
onclick={() => gerarPreenchido(modelo.id)} {#if generating}
disabled={generating} <span class="loading loading-spinner loading-xs"></span>
> Gerando...
{#if generating} {:else}
<span class="loading loading-spinner loading-xs"></span> <svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
Gerando... <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" />
{:else} </svg>
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"> Gerar Preenchido
<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" /> {/if}
</svg> </button>
Gerar Preenchido {/if}
{/if} </div>
</button> </div>
{/if} </div>
</div> </div>
</div> </div>
</div> {/each}
</div> </div>
</div>
{/each} <div class="mt-4 text-xs text-base-content/60 text-center">
</div> <p>💡 Dica: Após preencher e assinar os documentos, faça upload na seção "Documentação Anexa"</p>
</div>
<div class="mt-4 text-xs text-base-content/60 text-center"> </div>
<p>💡 Dica: Após preencher e assinar os documentos, faça upload na seção "Documentação Anexa"</p> </div>
</div>
</div>
</div>

View File

@@ -1,463 +1,458 @@
<script lang="ts"> <script lang="ts">
import jsPDF from 'jspdf'; import jsPDF from 'jspdf';
import autoTable from 'jspdf-autotable'; import autoTable from 'jspdf-autotable';
import { maskCPF, maskCEP, maskPhone } from "$lib/utils/masks"; import { maskCPF, maskCEP, maskPhone } from "$lib/utils/masks";
import { import {
SEXO_OPTIONS, ESTADO_CIVIL_OPTIONS, GRAU_INSTRUCAO_OPTIONS, SEXO_OPTIONS, ESTADO_CIVIL_OPTIONS, GRAU_INSTRUCAO_OPTIONS,
GRUPO_SANGUINEO_OPTIONS, FATOR_RH_OPTIONS, APOSENTADO_OPTIONS GRUPO_SANGUINEO_OPTIONS, FATOR_RH_OPTIONS, APOSENTADO_OPTIONS
} from "$lib/utils/constants"; } from "$lib/utils/constants";
import logoGovPE from "$lib/assets/logo_governo_PE.png"; import logoGovPE from "$lib/assets/logo_governo_PE.png";
import { CheckCircle2, X, Printer } from "lucide-svelte";
interface Props {
funcionario: any; interface Props {
onClose: () => void; funcionario: any;
} onClose: () => void;
}
let { funcionario, onClose }: Props = $props();
let { funcionario, onClose }: Props = $props();
let modalRef: HTMLDialogElement;
let generating = $state(false); let modalRef: HTMLDialogElement;
let generating = $state(false);
// Seções selecionáveis
let sections = $state({ // Seções selecionáveis
dadosPessoais: true, let sections = $state({
filiacao: true, dadosPessoais: true,
naturalidade: true, filiacao: true,
documentos: true, naturalidade: true,
formacao: true, documentos: true,
saude: true, formacao: true,
endereco: true, saude: true,
contato: true, endereco: true,
cargo: true, contato: true,
bancario: true, cargo: true,
}); bancario: true,
});
function getLabelFromOptions(value: string | undefined, options: Array<{value: string, label: string}>): string {
if (!value) return "-"; function getLabelFromOptions(value: string | undefined, options: Array<{value: string, label: string}>): string {
return options.find(opt => opt.value === value)?.label || value; if (!value) return "-";
} return options.find(opt => opt.value === value)?.label || value;
}
function selectAll() {
Object.keys(sections).forEach(key => { function selectAll() {
sections[key as keyof typeof sections] = true; Object.keys(sections).forEach(key => {
}); sections[key as keyof typeof sections] = true;
} });
}
function deselectAll() {
Object.keys(sections).forEach(key => { function deselectAll() {
sections[key as keyof typeof sections] = false; Object.keys(sections).forEach(key => {
}); sections[key as keyof typeof sections] = false;
} });
}
async function gerarPDF() {
try { async function gerarPDF() {
generating = true; try {
generating = true;
const doc = new jsPDF();
const doc = new jsPDF();
// Logo no canto superior esquerdo (proporcional)
let yPosition = 20; // Logo no canto superior esquerdo (proporcional)
try { let yPosition = 20;
const logoImg = new Image(); try {
logoImg.src = logoGovPE; const logoImg = new Image();
await new Promise<void>((resolve, reject) => { logoImg.src = logoGovPE;
logoImg.onload = () => resolve(); await new Promise<void>((resolve, reject) => {
logoImg.onerror = () => reject(); logoImg.onload = () => resolve();
setTimeout(() => reject(), 3000); // timeout após 3s logoImg.onerror = () => reject();
}); setTimeout(() => reject(), 3000); // timeout após 3s
});
// Logo proporcional: largura 25mm, altura ajustada automaticamente
const logoWidth = 25; // Logo proporcional: largura 25mm, altura ajustada automaticamente
const aspectRatio = logoImg.height / logoImg.width; const logoWidth = 25;
const logoHeight = logoWidth * aspectRatio; const aspectRatio = logoImg.height / logoImg.width;
const logoHeight = logoWidth * aspectRatio;
doc.addImage(logoImg, 'PNG', 15, 10, logoWidth, logoHeight);
doc.addImage(logoImg, 'PNG', 15, 10, logoWidth, logoHeight);
// Ajustar posição inicial do texto para ficar ao lado da logo
yPosition = Math.max(20, 10 + logoHeight / 2); // Ajustar posição inicial do texto para ficar ao lado da logo
} catch (err) { yPosition = Math.max(20, 10 + logoHeight / 2);
console.warn('Não foi possível carregar a logo:', err); } catch (err) {
} console.warn('Não foi possível carregar a logo:', err);
}
// Cabeçalho (alinhado com a logo)
doc.setFontSize(16); // Cabeçalho (alinhado com a logo)
doc.setFont('helvetica', 'bold'); doc.setFontSize(16);
doc.text('Secretaria de Esportes', 50, yPosition); doc.setFont('helvetica', 'bold');
doc.setFontSize(12); doc.text('Secretaria de Esportes', 50, yPosition);
doc.setFont('helvetica', 'normal'); doc.setFontSize(12);
doc.text('Governo de Pernambuco', 50, yPosition + 7); doc.setFont('helvetica', 'normal');
doc.text('Governo de Pernambuco', 50, yPosition + 7);
yPosition = Math.max(45, yPosition + 25);
yPosition = Math.max(45, yPosition + 25);
// Título da ficha
doc.setFontSize(18); // Título da ficha
doc.setFont('helvetica', 'bold'); doc.setFontSize(18);
doc.text('FICHA CADASTRAL DE FUNCIONÁRIO', 105, yPosition, { align: 'center' }); doc.setFont('helvetica', 'bold');
doc.text('FICHA CADASTRAL DE FUNCIONÁRIO', 105, yPosition, { align: 'center' });
yPosition += 8;
doc.setFontSize(10); yPosition += 8;
doc.setFont('helvetica', 'normal'); doc.setFontSize(10);
doc.text(`Gerado em: ${new Date().toLocaleString('pt-BR')}`, 105, yPosition, { align: 'center' }); doc.setFont('helvetica', 'normal');
doc.text(`Gerado em: ${new Date().toLocaleString('pt-BR')}`, 105, yPosition, { align: 'center' });
yPosition += 12;
yPosition += 12;
// Dados Pessoais
if (sections.dadosPessoais) { // Dados Pessoais
const dadosPessoais: any[] = [ if (sections.dadosPessoais) {
['Nome', funcionario.nome], const dadosPessoais: any[] = [
['Matrícula', funcionario.matricula], ['Nome', funcionario.nome],
['CPF', maskCPF(funcionario.cpf)], ['Matrícula', funcionario.matricula],
['RG', funcionario.rg], ['CPF', maskCPF(funcionario.cpf)],
['Data Nascimento', funcionario.nascimento], ['RG', funcionario.rg],
]; ['Data Nascimento', funcionario.nascimento],
];
if (funcionario.rgOrgaoExpedidor) dadosPessoais.push(['Órgão Expedidor RG', funcionario.rgOrgaoExpedidor]);
if (funcionario.rgDataEmissao) dadosPessoais.push(['Data Emissão RG', funcionario.rgDataEmissao]); if (funcionario.rgOrgaoExpedidor) dadosPessoais.push(['Órgão Expedidor RG', funcionario.rgOrgaoExpedidor]);
if (funcionario.sexo) dadosPessoais.push(['Sexo', getLabelFromOptions(funcionario.sexo, SEXO_OPTIONS)]); if (funcionario.rgDataEmissao) dadosPessoais.push(['Data Emissão RG', funcionario.rgDataEmissao]);
if (funcionario.estadoCivil) dadosPessoais.push(['Estado Civil', getLabelFromOptions(funcionario.estadoCivil, ESTADO_CIVIL_OPTIONS)]); if (funcionario.sexo) dadosPessoais.push(['Sexo', getLabelFromOptions(funcionario.sexo, SEXO_OPTIONS)]);
if (funcionario.nacionalidade) dadosPessoais.push(['Nacionalidade', funcionario.nacionalidade]); if (funcionario.estadoCivil) dadosPessoais.push(['Estado Civil', getLabelFromOptions(funcionario.estadoCivil, ESTADO_CIVIL_OPTIONS)]);
if (funcionario.nacionalidade) dadosPessoais.push(['Nacionalidade', funcionario.nacionalidade]);
autoTable(doc, {
startY: yPosition, autoTable(doc, {
head: [['DADOS PESSOAIS', '']], startY: yPosition,
body: dadosPessoais, head: [['DADOS PESSOAIS', '']],
theme: 'grid', body: dadosPessoais,
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' }, theme: 'grid',
styles: { fontSize: 9 }, headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
}); styles: { fontSize: 9 },
});
yPosition = (doc as any).lastAutoTable.finalY + 10;
} yPosition = (doc as any).lastAutoTable.finalY + 10;
}
// Filiação
if (sections.filiacao && (funcionario.nomePai || funcionario.nomeMae)) { // Filiação
const filiacao: any[] = []; if (sections.filiacao && (funcionario.nomePai || funcionario.nomeMae)) {
if (funcionario.nomePai) filiacao.push(['Nome do Pai', funcionario.nomePai]); const filiacao: any[] = [];
if (funcionario.nomeMae) filiacao.push(['Nome da Mãe', funcionario.nomeMae]); if (funcionario.nomePai) filiacao.push(['Nome do Pai', funcionario.nomePai]);
if (funcionario.nomeMae) filiacao.push(['Nome da Mãe', funcionario.nomeMae]);
autoTable(doc, {
startY: yPosition, autoTable(doc, {
head: [['FILIAÇÃO', '']], startY: yPosition,
body: filiacao, head: [['FILIAÇÃO', '']],
theme: 'grid', body: filiacao,
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' }, theme: 'grid',
styles: { fontSize: 9 }, headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
}); styles: { fontSize: 9 },
});
yPosition = (doc as any).lastAutoTable.finalY + 10;
} yPosition = (doc as any).lastAutoTable.finalY + 10;
}
// Naturalidade
if (sections.naturalidade && (funcionario.naturalidade || funcionario.naturalidadeUF)) { // Naturalidade
const naturalidade: any[] = []; if (sections.naturalidade && (funcionario.naturalidade || funcionario.naturalidadeUF)) {
if (funcionario.naturalidade) naturalidade.push(['Cidade', funcionario.naturalidade]); const naturalidade: any[] = [];
if (funcionario.naturalidadeUF) naturalidade.push(['UF', funcionario.naturalidadeUF]); if (funcionario.naturalidade) naturalidade.push(['Cidade', funcionario.naturalidade]);
if (funcionario.naturalidadeUF) naturalidade.push(['UF', funcionario.naturalidadeUF]);
autoTable(doc, {
startY: yPosition, autoTable(doc, {
head: [['NATURALIDADE', '']], startY: yPosition,
body: naturalidade, head: [['NATURALIDADE', '']],
theme: 'grid', body: naturalidade,
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' }, theme: 'grid',
styles: { fontSize: 9 }, headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
}); styles: { fontSize: 9 },
});
yPosition = (doc as any).lastAutoTable.finalY + 10;
} yPosition = (doc as any).lastAutoTable.finalY + 10;
}
// Documentos
if (sections.documentos) { // Documentos
const documentosData: any[] = []; if (sections.documentos) {
const documentosData: any[] = [];
if (funcionario.carteiraProfissionalNumero) {
documentosData.push(['Cart. Profissional', `Nº ${funcionario.carteiraProfissionalNumero}${funcionario.carteiraProfissionalSerie ? ' - Série: ' + funcionario.carteiraProfissionalSerie : ''}`]); if (funcionario.carteiraProfissionalNumero) {
} documentosData.push(['Cart. Profissional', `Nº ${funcionario.carteiraProfissionalNumero}${funcionario.carteiraProfissionalSerie ? ' - Série: ' + funcionario.carteiraProfissionalSerie : ''}`]);
if (funcionario.reservistaNumero) { }
documentosData.push(['Reservista', `Nº ${funcionario.reservistaNumero}${funcionario.reservistaSerie ? ' - Série: ' + funcionario.reservistaSerie : ''}`]); if (funcionario.reservistaNumero) {
} documentosData.push(['Reservista', `Nº ${funcionario.reservistaNumero}${funcionario.reservistaSerie ? ' - Série: ' + funcionario.reservistaSerie : ''}`]);
if (funcionario.tituloEleitorNumero) { }
let titulo = `Nº ${funcionario.tituloEleitorNumero}`; if (funcionario.tituloEleitorNumero) {
if (funcionario.tituloEleitorZona) titulo += ` - Zona: ${funcionario.tituloEleitorZona}`; let titulo = ` ${funcionario.tituloEleitorNumero}`;
if (funcionario.tituloEleitorSecao) titulo += ` - Seção: ${funcionario.tituloEleitorSecao}`; if (funcionario.tituloEleitorZona) titulo += ` - Zona: ${funcionario.tituloEleitorZona}`;
documentosData.push(['Título Eleitor', titulo]); if (funcionario.tituloEleitorSecao) titulo += ` - Seção: ${funcionario.tituloEleitorSecao}`;
} documentosData.push(['Título Eleitor', titulo]);
if (funcionario.pisNumero) documentosData.push(['PIS/PASEP', funcionario.pisNumero]); }
if (funcionario.pisNumero) documentosData.push(['PIS/PASEP', funcionario.pisNumero]);
if (documentosData.length > 0) {
autoTable(doc, { if (documentosData.length > 0) {
startY: yPosition, autoTable(doc, {
head: [['DOCUMENTOS', '']], startY: yPosition,
body: documentosData, head: [['DOCUMENTOS', '']],
theme: 'grid', body: documentosData,
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' }, theme: 'grid',
styles: { fontSize: 9 }, headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
}); styles: { fontSize: 9 },
});
yPosition = (doc as any).lastAutoTable.finalY + 10;
} yPosition = (doc as any).lastAutoTable.finalY + 10;
} }
}
// Formação
if (sections.formacao && (funcionario.grauInstrucao || funcionario.formacao)) { // Formação
const formacaoData: any[] = []; if (sections.formacao && (funcionario.grauInstrucao || funcionario.formacao)) {
if (funcionario.grauInstrucao) formacaoData.push(['Grau Instrução', getLabelFromOptions(funcionario.grauInstrucao, GRAU_INSTRUCAO_OPTIONS)]); const formacaoData: any[] = [];
if (funcionario.formacao) formacaoData.push(['Formação', funcionario.formacao]); if (funcionario.grauInstrucao) formacaoData.push(['Grau Instrução', getLabelFromOptions(funcionario.grauInstrucao, GRAU_INSTRUCAO_OPTIONS)]);
if (funcionario.formacaoRegistro) formacaoData.push(['Registro Nº', funcionario.formacaoRegistro]); if (funcionario.formacao) formacaoData.push(['Formação', funcionario.formacao]);
if (funcionario.formacaoRegistro) formacaoData.push(['Registro Nº', funcionario.formacaoRegistro]);
autoTable(doc, {
startY: yPosition, autoTable(doc, {
head: [['FORMAÇÃO', '']], startY: yPosition,
body: formacaoData, head: [['FORMAÇÃO', '']],
theme: 'grid', body: formacaoData,
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' }, theme: 'grid',
styles: { fontSize: 9 }, headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
}); styles: { fontSize: 9 },
});
yPosition = (doc as any).lastAutoTable.finalY + 10;
} yPosition = (doc as any).lastAutoTable.finalY + 10;
}
// Saúde
if (sections.saude && (funcionario.grupoSanguineo || funcionario.fatorRH)) { // Saúde
const saudeData: any[] = []; if (sections.saude && (funcionario.grupoSanguineo || funcionario.fatorRH)) {
if (funcionario.grupoSanguineo) saudeData.push(['Grupo Sanguíneo', funcionario.grupoSanguineo]); const saudeData: any[] = [];
if (funcionario.fatorRH) saudeData.push(['Fator RH', getLabelFromOptions(funcionario.fatorRH, FATOR_RH_OPTIONS)]); if (funcionario.grupoSanguineo) saudeData.push(['Grupo Sanguíneo', funcionario.grupoSanguineo]);
if (funcionario.fatorRH) saudeData.push(['Fator RH', getLabelFromOptions(funcionario.fatorRH, FATOR_RH_OPTIONS)]);
autoTable(doc, {
startY: yPosition, autoTable(doc, {
head: [['SAÚDE', '']], startY: yPosition,
body: saudeData, head: [['SAÚDE', '']],
theme: 'grid', body: saudeData,
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' }, theme: 'grid',
styles: { fontSize: 9 }, headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
}); styles: { fontSize: 9 },
});
yPosition = (doc as any).lastAutoTable.finalY + 10;
} yPosition = (doc as any).lastAutoTable.finalY + 10;
}
// Endereço
if (sections.endereco) { // Endereço
const enderecoData: any[] = [ if (sections.endereco) {
['Endereço', funcionario.endereco], const enderecoData: any[] = [
['Cidade', funcionario.cidade], ['Endereço', funcionario.endereco],
['UF', funcionario.uf], ['Cidade', funcionario.cidade],
['CEP', maskCEP(funcionario.cep)], ['UF', funcionario.uf],
]; ['CEP', maskCEP(funcionario.cep)],
];
autoTable(doc, {
startY: yPosition, autoTable(doc, {
head: [['ENDEREÇO', '']], startY: yPosition,
body: enderecoData, head: [['ENDEREÇO', '']],
theme: 'grid', body: enderecoData,
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' }, theme: 'grid',
styles: { fontSize: 9 }, headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
}); styles: { fontSize: 9 },
});
yPosition = (doc as any).lastAutoTable.finalY + 10;
} yPosition = (doc as any).lastAutoTable.finalY + 10;
}
// Contato
if (sections.contato) { // Contato
const contatoData: any[] = [ if (sections.contato) {
['E-mail', funcionario.email], const contatoData: any[] = [
['Telefone', maskPhone(funcionario.telefone)], ['E-mail', funcionario.email],
]; ['Telefone', maskPhone(funcionario.telefone)],
];
autoTable(doc, {
startY: yPosition, autoTable(doc, {
head: [['CONTATO', '']], startY: yPosition,
body: contatoData, head: [['CONTATO', '']],
theme: 'grid', body: contatoData,
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' }, theme: 'grid',
styles: { fontSize: 9 }, headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
}); styles: { fontSize: 9 },
});
yPosition = (doc as any).lastAutoTable.finalY + 10;
} yPosition = (doc as any).lastAutoTable.finalY + 10;
}
// Nova página para cargo
if (yPosition > 200) { // Nova página para cargo
doc.addPage(); if (yPosition > 200) {
yPosition = 20; doc.addPage();
} yPosition = 20;
}
// Cargo e Vínculo
if (sections.cargo) { // Cargo e Vínculo
const cargoData: any[] = [ if (sections.cargo) {
['Tipo', funcionario.simboloTipo === 'cargo_comissionado' ? 'Cargo Comissionado' : 'Função Gratificada'], const cargoData: any[] = [
]; ['Tipo', funcionario.simboloTipo === 'cargo_comissionado' ? 'Cargo Comissionado' : 'Função Gratificada'],
];
if (funcionario.simbolo) {
cargoData.push(['Símbolo', funcionario.simbolo.nome]); if (funcionario.simbolo) {
} cargoData.push(['Símbolo', funcionario.simbolo.nome]);
if (funcionario.descricaoCargo) cargoData.push(['Descrição', funcionario.descricaoCargo]); }
if (funcionario.admissaoData) cargoData.push(['Data Admissão', funcionario.admissaoData]); if (funcionario.descricaoCargo) cargoData.push(['Descrição', funcionario.descricaoCargo]);
if (funcionario.nomeacaoPortaria) cargoData.push(['Portaria', funcionario.nomeacaoPortaria]); if (funcionario.admissaoData) cargoData.push(['Data Admissão', funcionario.admissaoData]);
if (funcionario.nomeacaoData) cargoData.push(['Data Nomeação', funcionario.nomeacaoData]); if (funcionario.nomeacaoPortaria) cargoData.push(['Portaria', funcionario.nomeacaoPortaria]);
if (funcionario.nomeacaoDOE) cargoData.push(['DOE', funcionario.nomeacaoDOE]); if (funcionario.nomeacaoData) cargoData.push(['Data Nomeação', funcionario.nomeacaoData]);
if (funcionario.pertenceOrgaoPublico) { if (funcionario.nomeacaoDOE) cargoData.push(['DOE', funcionario.nomeacaoDOE]);
cargoData.push(['Pertence Órgão Público', 'Sim']); if (funcionario.pertenceOrgaoPublico) {
if (funcionario.orgaoOrigem) cargoData.push(['Órgão Origem', funcionario.orgaoOrigem]); cargoData.push(['Pertence Órgão Público', 'Sim']);
} if (funcionario.orgaoOrigem) cargoData.push(['Órgão Origem', funcionario.orgaoOrigem]);
if (funcionario.aposentado && funcionario.aposentado !== 'nao') { }
cargoData.push(['Aposentado', getLabelFromOptions(funcionario.aposentado, APOSENTADO_OPTIONS)]); if (funcionario.aposentado && funcionario.aposentado !== 'nao') {
} cargoData.push(['Aposentado', getLabelFromOptions(funcionario.aposentado, APOSENTADO_OPTIONS)]);
}
autoTable(doc, {
startY: yPosition, autoTable(doc, {
head: [['CARGO E VÍNCULO', '']], startY: yPosition,
body: cargoData, head: [['CARGO E VÍNCULO', '']],
theme: 'grid', body: cargoData,
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' }, theme: 'grid',
styles: { fontSize: 9 }, headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
}); styles: { fontSize: 9 },
});
yPosition = (doc as any).lastAutoTable.finalY + 10;
} yPosition = (doc as any).lastAutoTable.finalY + 10;
}
// Dados Bancários
if (sections.bancario && funcionario.contaBradescoNumero) { // Dados Bancários
const bancarioData: any[] = [ if (sections.bancario && funcionario.contaBradescoNumero) {
['Conta', `${funcionario.contaBradescoNumero}${funcionario.contaBradescoDV ? '-' + funcionario.contaBradescoDV : ''}`], const bancarioData: any[] = [
]; ['Conta', `${funcionario.contaBradescoNumero}${funcionario.contaBradescoDV ? '-' + funcionario.contaBradescoDV : ''}`],
if (funcionario.contaBradescoAgencia) bancarioData.push(['Agência', funcionario.contaBradescoAgencia]); ];
if (funcionario.contaBradescoAgencia) bancarioData.push(['Agência', funcionario.contaBradescoAgencia]);
autoTable(doc, {
startY: yPosition, autoTable(doc, {
head: [['DADOS BANCÁRIOS - BRADESCO', '']], startY: yPosition,
body: bancarioData, head: [['DADOS BANCÁRIOS - BRADESCO', '']],
theme: 'grid', body: bancarioData,
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' }, theme: 'grid',
styles: { fontSize: 9 }, headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
}); styles: { fontSize: 9 },
});
yPosition = (doc as any).lastAutoTable.finalY + 10;
} yPosition = (doc as any).lastAutoTable.finalY + 10;
}
// Adicionar rodapé em todas as páginas
const pageCount = (doc as any).internal.getNumberOfPages(); // Adicionar rodapé em todas as páginas
for (let i = 1; i <= pageCount; i++) { const pageCount = (doc as any).internal.getNumberOfPages();
doc.setPage(i); for (let i = 1; i <= pageCount; i++) {
doc.setFontSize(9); doc.setPage(i);
doc.setFont('helvetica', 'normal'); doc.setFontSize(9);
doc.setTextColor(128, 128, 128); doc.setFont('helvetica', 'normal');
doc.text('SGSE - Sistema de Gerenciamento da Secretaria de Esportes', 105, 285, { align: 'center' }); doc.setTextColor(128, 128, 128);
doc.text(`Página ${i} de ${pageCount}`, 195, 285, { align: 'right' }); doc.text('SGSE - Sistema de Gerenciamento da Secretaria de Esportes', 105, 285, { align: 'center' });
} doc.text(`Página ${i} de ${pageCount}`, 195, 285, { align: 'right' });
}
// Salvar PDF
doc.save(`Ficha_${funcionario.nome.replace(/ /g, '_')}_${new Date().getTime()}.pdf`); // Salvar PDF
doc.save(`Ficha_${funcionario.nome.replace(/ /g, '_')}_${new Date().getTime()}.pdf`);
onClose();
} catch (error) { onClose();
console.error('Erro ao gerar PDF:', error); } catch (error) {
alert('Erro ao gerar PDF. Verifique o console para mais detalhes.'); console.error('Erro ao gerar PDF:', error);
} finally { alert('Erro ao gerar PDF. Verifique o console para mais detalhes.');
generating = false; } finally {
} generating = false;
} }
}
$effect(() => {
if (modalRef) { $effect(() => {
modalRef.showModal(); if (modalRef) {
} modalRef.showModal();
}); }
</script> });
</script>
<dialog bind:this={modalRef} class="modal">
<div class="modal-box max-w-4xl"> <dialog bind:this={modalRef} class="modal">
<h3 class="font-bold text-2xl mb-4">Imprimir Ficha Cadastral</h3> <div class="modal-box max-w-4xl">
<p class="text-sm text-base-content/70 mb-6">Selecione as seções que deseja incluir no PDF</p> <h3 class="font-bold text-2xl mb-4">Imprimir Ficha Cadastral</h3>
<p class="text-sm text-base-content/70 mb-6">Selecione as seções que deseja incluir no PDF</p>
<!-- Botões de seleção -->
<div class="flex gap-2 mb-6"> <!-- Botões de seleção -->
<button type="button" class="btn btn-sm btn-outline" onclick={selectAll}> <div class="flex gap-2 mb-6">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <button type="button" class="btn btn-sm btn-outline" onclick={selectAll}>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> <CheckCircle2 class="h-4 w-4" strokeWidth={2} />
</svg> Selecionar Todos
Selecionar Todos </button>
</button> <button type="button" class="btn btn-sm btn-outline" onclick={deselectAll}>
<button type="button" class="btn btn-sm btn-outline" onclick={deselectAll}> <X class="h-4 w-4" strokeWidth={2} />
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> Desmarcar Todos
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> </button>
</svg> </div>
Desmarcar Todos
</button> <!-- Grid de checkboxes -->
</div> <div class="grid grid-cols-2 md:grid-cols-3 gap-4 mb-6 max-h-96 overflow-y-auto p-2 border rounded-lg bg-base-200">
<label class="label cursor-pointer justify-start gap-3">
<!-- Grid de checkboxes --> <input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.dadosPessoais} />
<div class="grid grid-cols-2 md:grid-cols-3 gap-4 mb-6 max-h-96 overflow-y-auto p-2 border rounded-lg bg-base-200"> <span class="label-text">Dados Pessoais</span>
<label class="label cursor-pointer justify-start gap-3"> </label>
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.dadosPessoais} />
<span class="label-text">Dados Pessoais</span> <label class="label cursor-pointer justify-start gap-3">
</label> <input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.filiacao} />
<span class="label-text">Filiação</span>
<label class="label cursor-pointer justify-start gap-3"> </label>
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.filiacao} />
<span class="label-text">Filiação</span> <label class="label cursor-pointer justify-start gap-3">
</label> <input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.naturalidade} />
<span class="label-text">Naturalidade</span>
<label class="label cursor-pointer justify-start gap-3"> </label>
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.naturalidade} />
<span class="label-text">Naturalidade</span> <label class="label cursor-pointer justify-start gap-3">
</label> <input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.documentos} />
<span class="label-text">Documentos</span>
<label class="label cursor-pointer justify-start gap-3"> </label>
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.documentos} />
<span class="label-text">Documentos</span> <label class="label cursor-pointer justify-start gap-3">
</label> <input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.formacao} />
<span class="label-text">Formação</span>
<label class="label cursor-pointer justify-start gap-3"> </label>
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.formacao} />
<span class="label-text">Formação</span> <label class="label cursor-pointer justify-start gap-3">
</label> <input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.saude} />
<span class="label-text">Saúde</span>
<label class="label cursor-pointer justify-start gap-3"> </label>
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.saude} />
<span class="label-text">Saúde</span> <label class="label cursor-pointer justify-start gap-3">
</label> <input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.endereco} />
<span class="label-text">Endereço</span>
<label class="label cursor-pointer justify-start gap-3"> </label>
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.endereco} />
<span class="label-text">Endereço</span> <label class="label cursor-pointer justify-start gap-3">
</label> <input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.contato} />
<span class="label-text">Contato</span>
<label class="label cursor-pointer justify-start gap-3"> </label>
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.contato} />
<span class="label-text">Contato</span> <label class="label cursor-pointer justify-start gap-3">
</label> <input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.cargo} />
<span class="label-text">Cargo e Vínculo</span>
<label class="label cursor-pointer justify-start gap-3"> </label>
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.cargo} />
<span class="label-text">Cargo e Vínculo</span> <label class="label cursor-pointer justify-start gap-3">
</label> <input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.bancario} />
<span class="label-text">Dados Bancários</span>
<label class="label cursor-pointer justify-start gap-3"> </label>
<input type="checkbox" class="checkbox checkbox-primary" bind:checked={sections.bancario} /> </div>
<span class="label-text">Dados Bancários</span>
</label> <!-- Ações -->
</div> <div class="modal-action">
<button type="button" class="btn btn-ghost" onclick={onClose} disabled={generating}>
<!-- Ações --> Cancelar
<div class="modal-action"> </button>
<button type="button" class="btn btn-ghost" onclick={onClose} disabled={generating}> <button type="button" class="btn btn-primary gap-2" onclick={gerarPDF} disabled={generating}>
Cancelar {#if generating}
</button> <span class="loading loading-spinner loading-sm"></span>
<button type="button" class="btn btn-primary gap-2" onclick={gerarPDF} disabled={generating}> Gerando PDF...
{#if generating} {:else}
<span class="loading loading-spinner loading-sm"></span> <Printer class="h-5 w-5" strokeWidth={2} />
Gerando PDF... Gerar PDF
{:else} {/if}
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </button>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" /> </div>
</svg> </div>
Gerar PDF
{/if} <form method="dialog" class="modal-backdrop">
</button> <button type="button" onclick={onClose}>fechar</button>
</div> </form>
</div> </dialog>
<form method="dialog" class="modal-backdrop">
<button type="button" onclick={onClose}>fechar</button>
</form>
</dialog>

View File

@@ -12,6 +12,7 @@
import PresenceManager from "$lib/components/chat/PresenceManager.svelte"; import PresenceManager from "$lib/components/chat/PresenceManager.svelte";
import { getBrowserInfo } from "$lib/utils/browserInfo"; import { getBrowserInfo } from "$lib/utils/browserInfo";
import { getAvatarUrl } from "$lib/utils/avatarGenerator"; import { getAvatarUrl } from "$lib/utils/avatarGenerator";
import { Menu, User, Home, UserPlus, XCircle, LogIn, Tag, Plus, Check } from "lucide-svelte";
let { children }: { children: Snippet } = $props(); let { children }: { children: Snippet } = $props();
@@ -180,21 +181,11 @@
<div class="absolute inset-0 bg-gradient-to-br from-white/0 to-white/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div> <div class="absolute inset-0 bg-gradient-to-br from-white/0 to-white/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<!-- Ícone de menu hambúrguer --> <!-- Ícone de menu hambúrguer -->
<svg <Menu
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
class="w-7 h-7 text-white relative z-10 group-hover:scale-110 transition-transform duration-300" class="w-7 h-7 text-white relative z-10 group-hover:scale-110 transition-transform duration-300"
style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3));" style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3));"
> strokeWidth={2.5}
<path />
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2.5"
d="M4 6h16M4 12h16M4 18h16"
stroke="currentColor"
></path>
</svg>
</label> </label>
</div> </div>
<div class="flex-1 flex items-center gap-4 lg:gap-6"> <div class="flex-1 flex items-center gap-4 lg:gap-6">
@@ -261,15 +252,10 @@
/> />
{:else} {:else}
<!-- Ícone de usuário moderno (fallback) --> <!-- Ícone de usuário moderno (fallback) -->
<svg <User
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="w-7 h-7 text-white relative z-10 group-hover:scale-110 transition-transform duration-300" class="w-7 h-7 text-white relative z-10 group-hover:scale-110 transition-transform duration-300"
style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3));" style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3));"
> />
<path fill-rule="evenodd" d="M18.685 19.097A9.723 9.723 0 0021.75 12c0-5.385-4.365-9.75-9.75-9.75S2.25 6.615 2.25 12a9.723 9.723 0 003.065 7.097A9.716 9.716 0 0012 21.75a9.716 9.716 0 006.685-2.653zm-12.54-1.285A7.486 7.486 0 0112 15a7.486 7.486 0 015.855 2.812A8.224 8.224 0 0112 20.25a8.224 8.224 0 01-5.855-2.438zM15.75 9a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z" clip-rule="evenodd" />
</svg>
{/if} {/if}
<!-- Badge de status online --> <!-- Badge de status online -->
@@ -301,21 +287,11 @@
<div class="absolute inset-0 rounded-full bg-white/10 group-hover:animate-ping"></div> <div class="absolute inset-0 rounded-full bg-white/10 group-hover:animate-ping"></div>
<!-- Ícone de login premium --> <!-- Ícone de login premium -->
<svg <User
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8 relative z-10 text-white group-hover:scale-110 transition-all duration-500" class="h-8 w-8 relative z-10 text-white group-hover:scale-110 transition-all duration-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2.5"
style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3));" style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3));"
> strokeWidth={2.5}
<path />
stroke-linecap="round"
stroke-linejoin="round"
d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
/>
</svg>
</button> </button>
{/if} {/if}
</div> </div>
@@ -365,20 +341,10 @@
href="/" href="/"
class={getMenuClasses(currentPath === "/")} class={getMenuClasses(currentPath === "/")}
> >
<svg <Home
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 group-hover:scale-110 transition-transform" class="h-5 w-5 group-hover:scale-110 transition-transform"
fill="none" strokeWidth={2}
viewBox="0 0 24 24" />
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
/>
</svg>
<span>Dashboard</span> <span>Dashboard</span>
</a> </a>
</li> </li>
@@ -399,20 +365,10 @@
href="/solicitar-acesso" href="/solicitar-acesso"
class={getSolicitarClasses(currentPath === "/solicitar-acesso")} class={getSolicitarClasses(currentPath === "/solicitar-acesso")}
> >
<svg <UserPlus
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5" class="h-5 w-5"
fill="none" strokeWidth={2}
viewBox="0 0 24 24" />
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"
/>
</svg>
<span>Solicitar acesso</span> <span>Solicitar acesso</span>
</a> </a>
</li> </li>
@@ -446,9 +402,7 @@
{#if erroLogin} {#if erroLogin}
<div class="alert alert-error mb-4"> <div class="alert alert-error mb-4">
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"> <XCircle class="stroke-current shrink-0 h-6 w-6" strokeWidth={2} />
<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>{erroLogin}</span> <span>{erroLogin}</span>
</div> </div>
{/if} {/if}
@@ -492,9 +446,7 @@
<span class="loading loading-spinner loading-sm"></span> <span class="loading loading-spinner loading-sm"></span>
Entrando... Entrando...
{:else} {:else}
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <LogIn class="h-5 w-5" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1" />
</svg>
Entrar Entrar
{/if} {/if}
</button> </button>
@@ -559,16 +511,12 @@
<!-- Informações de Versão --> <!-- Informações de Versão -->
<div class="bg-primary/10 rounded-xl p-6 space-y-3"> <div class="bg-primary/10 rounded-xl p-6 space-y-3">
<div class="flex items-center justify-center gap-2"> <div class="flex items-center justify-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <Tag class="h-5 w-5 text-primary" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
</svg>
<p class="text-sm font-medium text-base-content/70">Versão</p> <p class="text-sm font-medium text-base-content/70">Versão</p>
</div> </div>
<p class="text-2xl font-bold text-primary">1.0 26_2025</p> <p class="text-2xl font-bold text-primary">1.0 26_2025</p>
<div class="badge badge-warning badge-lg gap-2"> <div class="badge badge-warning badge-lg gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <Plus class="h-4 w-4" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
Em Desenvolvimento Em Desenvolvimento
</div> </div>
</div> </div>
@@ -603,9 +551,7 @@
class="btn btn-primary btn-lg w-full max-w-xs mx-auto shadow-lg hover:shadow-xl transition-all duration-300" class="btn btn-primary btn-lg w-full max-w-xs mx-auto shadow-lg hover:shadow-xl transition-all duration-300"
onclick={closeAboutModal} onclick={closeAboutModal}
> >
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <Check class="h-6 w-6" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
OK OK
</button> </button>
</div> </div>

View File

@@ -11,7 +11,7 @@
import SalaReuniaoManager from "./SalaReuniaoManager.svelte"; import SalaReuniaoManager from "./SalaReuniaoManager.svelte";
import { getAvatarUrl } from "$lib/utils/avatarGenerator"; import { getAvatarUrl } from "$lib/utils/avatarGenerator";
import { authStore } from "$lib/stores/auth.svelte"; import { authStore } from "$lib/stores/auth.svelte";
import { Bell, X } from "lucide-svelte"; import { Bell, X, ArrowLeft, LogOut, MoreVertical, Users, Clock, XCircle } from "lucide-svelte";
interface Props { interface Props {
conversaId: string; conversaId: string;
@@ -117,18 +117,10 @@
aria-label="Voltar" aria-label="Voltar"
title="Voltar para lista de conversas" title="Voltar para lista de conversas"
> >
<svg <ArrowLeft
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2.5"
stroke-linecap="round"
stroke-linejoin="round"
class="w-6 h-6 text-primary" class="w-6 h-6 text-primary"
> strokeWidth={2.5}
<path d="M19 12H5M12 19l-7-7 7-7"/> />
</svg>
</button> </button>
<!-- Avatar e Info --> <!-- Avatar e Info -->
@@ -215,20 +207,10 @@
title="Sair da conversa" title="Sair da conversa"
> >
<div class="absolute inset-0 bg-red-500/0 group-hover:bg-red-500/10 transition-colors duration-300"></div> <div class="absolute inset-0 bg-red-500/0 group-hover:bg-red-500/10 transition-colors duration-300"></div>
<svg <LogOut
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-5 h-5 text-red-500 relative z-10 group-hover:scale-110 transition-transform" class="w-5 h-5 text-red-500 relative z-10 group-hover:scale-110 transition-transform"
> strokeWidth={2}
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/> />
<polyline points="16 17 21 12 16 7"/>
<line x1="21" y1="12" x2="9" y2="12"/>
</svg>
</button> </button>
{/if} {/if}
@@ -247,20 +229,10 @@
title="Recursos administrativos" title="Recursos administrativos"
> >
<div class="absolute inset-0 bg-blue-500/0 group-hover:bg-blue-500/10 transition-colors duration-300"></div> <div class="absolute inset-0 bg-blue-500/0 group-hover:bg-blue-500/10 transition-colors duration-300"></div>
<svg <MoreVertical
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-5 h-5 text-blue-500 relative z-10 group-hover:scale-110 transition-transform" class="w-5 h-5 text-blue-500 relative z-10 group-hover:scale-110 transition-transform"
> strokeWidth={2}
<circle cx="12" cy="12" r="1"/> />
<circle cx="12" cy="5" r="1"/>
<circle cx="12" cy="19" r="1"/>
</svg>
</button> </button>
{#if showAdminMenu} {#if showAdminMenu}
<ul <ul
@@ -277,9 +249,7 @@
showAdminMenu = false; showAdminMenu = false;
}} }}
> >
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4"> <Users class="w-4 h-4" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" />
</svg>
Gerenciar Participantes Gerenciar Participantes
</button> </button>
</li> </li>
@@ -293,9 +263,7 @@
showAdminMenu = false; showAdminMenu = false;
}} }}
> >
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4"> <Bell class="w-4 h-4" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 0 0 5.454-1.31A8.967 8.967 0 0 1 18 9.75V9A6 6 0 0 0 6 9v.75a8.967 8.967 0 0 1-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 0 1-5.714 0m5.714 0a3 3 0 1 1-5.714 0" />
</svg>
Enviar Notificação Enviar Notificação
</button> </button>
</li> </li>
@@ -324,9 +292,7 @@
})(); })();
}} }}
> >
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4"> <XCircle class="w-4 h-4" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>
Encerrar Reunião Encerrar Reunião
</button> </button>
</li> </li>
@@ -345,19 +311,10 @@
title="Agendar mensagem" title="Agendar mensagem"
> >
<div class="absolute inset-0 bg-purple-500/0 group-hover:bg-purple-500/10 transition-colors duration-300"></div> <div class="absolute inset-0 bg-purple-500/0 group-hover:bg-purple-500/10 transition-colors duration-300"></div>
<svg <Clock
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-5 h-5 text-purple-500 relative z-10 group-hover:scale-110 transition-transform" class="w-5 h-5 text-purple-500 relative z-10 group-hover:scale-110 transition-transform"
> strokeWidth={2}
<circle cx="12" cy="12" r="10"/> />
<polyline points="12 6 12 12 16 14"/>
</svg>
</button> </button>
</div> </div>
</div> </div>

View File

@@ -4,6 +4,7 @@
import type { Id } from "@sgse-app/backend/convex/_generated/dataModel"; import type { Id } from "@sgse-app/backend/convex/_generated/dataModel";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { authStore } from "$lib/stores/auth.svelte"; import { authStore } from "$lib/stores/auth.svelte";
import { Paperclip, Smile, Send } from "lucide-svelte";
interface Props { interface Props {
conversaId: Id<"conversas">; conversaId: Id<"conversas">;
@@ -340,18 +341,10 @@
<span class="loading loading-spinner loading-sm relative z-10"></span> <span class="loading loading-spinner loading-sm relative z-10"></span>
{:else} {:else}
<!-- Ícone de clipe moderno --> <!-- Ícone de clipe moderno -->
<svg <Paperclip
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-5 h-5 text-primary relative z-10 group-hover:scale-110 transition-transform" class="w-5 h-5 text-primary relative z-10 group-hover:scale-110 transition-transform"
> strokeWidth={2}
<path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/> />
</svg>
{/if} {/if}
</label> </label>
@@ -367,21 +360,10 @@
title="Adicionar emoji" title="Adicionar emoji"
> >
<div class="absolute inset-0 bg-warning/0 group-hover:bg-warning/10 transition-colors duration-300"></div> <div class="absolute inset-0 bg-warning/0 group-hover:bg-warning/10 transition-colors duration-300"></div>
<svg <Smile
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-5 h-5 text-warning relative z-10 group-hover:scale-110 transition-transform" class="w-5 h-5 text-warning relative z-10 group-hover:scale-110 transition-transform"
> strokeWidth={2}
<circle cx="12" cy="12" r="10"/> />
<path d="M8 14s1.5 2 4 2 4-2 4-2"/>
<line x1="9" y1="9" x2="9.01" y2="9"/>
<line x1="15" y1="9" x2="15.01" y2="9"/>
</svg>
</button> </button>
<!-- Picker de Emojis --> <!-- Picker de Emojis -->
@@ -458,14 +440,9 @@
<span class="loading loading-spinner loading-sm relative z-10 text-white"></span> <span class="loading loading-spinner loading-sm relative z-10 text-white"></span>
{:else} {:else}
<!-- Ícone de avião de papel moderno --> <!-- Ícone de avião de papel moderno -->
<svg <Send
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="w-5 h-5 text-white relative z-10 group-hover:scale-110 group-hover:translate-x-1 transition-all" class="w-5 h-5 text-white relative z-10 group-hover:scale-110 group-hover:translate-x-1 transition-all"
> />
<path d="M3.478 2.405a.75.75 0 00-.926.94l2.432 7.905H13.5a.75.75 0 010 1.5H4.984l-2.432 7.905a.75.75 0 00.926.94 60.519 60.519 0 0018.445-8.986.75.75 0 000-1.218A60.517 60.517 0 003.478 2.405z"/>
</svg>
{/if} {/if}
</button> </button>
</div> </div>

View File

@@ -6,6 +6,7 @@
import { ptBR } from "date-fns/locale"; import { ptBR } from "date-fns/locale";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { authStore } from "$lib/stores/auth.svelte"; import { authStore } from "$lib/stores/auth.svelte";
import { Bell, Mail, AtSign, Users, Calendar, Clock, BellOff } from "lucide-svelte";
// Queries e Client // Queries e Client
const client = useConvexClient(); const client = useConvexClient();
@@ -200,22 +201,14 @@
{/if} {/if}
<!-- Ícone do sino PREENCHIDO moderno --> <!-- Ícone do sino PREENCHIDO moderno -->
<svg <Bell
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="w-7 h-7 text-white relative z-10 transition-all duration-300 group-hover:scale-110" class="w-7 h-7 text-white relative z-10 transition-all duration-300 group-hover:scale-110"
style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3)); animation: {count && style="filter: drop-shadow(0 2px 8px rgba(0,0,0,0.3)); animation: {count &&
count > 0 count > 0
? 'bell-ring 2s ease-in-out infinite' ? 'bell-ring 2s ease-in-out infinite'
: 'none'};" : 'none'};"
> fill="currentColor"
<path />
fill-rule="evenodd"
d="M5.25 9a6.75 6.75 0 0113.5 0v.75c0 2.123.8 4.057 2.118 5.52a.75.75 0 01-.297 1.206c-1.544.57-3.16.99-4.831 1.243a3.75 3.75 0 11-7.48 0 24.585 24.585 0 01-4.831-1.244.75.75 0 01-.298-1.205A8.217 8.217 0 005.25 9.75V9zm4.502 8.9a2.25 2.25 0 104.496 0 25.057 25.057 0 01-4.496 0z"
clip-rule="evenodd"
/>
</svg>
<!-- Badge premium MODERNO com gradiente --> <!-- Badge premium MODERNO com gradiente -->
{#if count + (notificacoesFerias?.length || 0) > 0} {#if count + (notificacoesFerias?.length || 0) > 0}
@@ -264,50 +257,11 @@
<!-- Ícone --> <!-- Ícone -->
<div class="flex-shrink-0 mt-1"> <div class="flex-shrink-0 mt-1">
{#if notificacao.tipo === "nova_mensagem"} {#if notificacao.tipo === "nova_mensagem"}
<svg <Mail class="w-5 h-5 text-primary" strokeWidth={1.5} />
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-5 h-5 text-primary"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75"
/>
</svg>
{:else if notificacao.tipo === "mencao"} {:else if notificacao.tipo === "mencao"}
<svg <AtSign class="w-5 h-5 text-warning" strokeWidth={1.5} />
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-5 h-5 text-warning"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M16.5 12a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0Zm0 0c0 1.657 1.007 3 2.25 3S21 13.657 21 12a9 9 0 1 0-2.636 6.364M16.5 12V8.25"
/>
</svg>
{:else} {:else}
<svg <Users class="w-5 h-5 text-info" strokeWidth={1.5} />
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-5 h-5 text-info"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z"
/>
</svg>
{/if} {/if}
</div> </div>
@@ -349,20 +303,7 @@
<div class="flex items-start gap-3"> <div class="flex items-start gap-3">
<!-- Ícone --> <!-- Ícone -->
<div class="flex-shrink-0 mt-1"> <div class="flex-shrink-0 mt-1">
<svg <Calendar class="w-5 h-5 text-purple-600" strokeWidth={2} />
xmlns="http://www.w3.org/2000/svg"
class="w-5 h-5 text-purple-600"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<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>
</div> </div>
<!-- Conteúdo --> <!-- Conteúdo -->
@@ -398,20 +339,7 @@
<div class="flex items-start gap-3"> <div class="flex items-start gap-3">
<!-- Ícone --> <!-- Ícone -->
<div class="flex-shrink-0 mt-1"> <div class="flex-shrink-0 mt-1">
<svg <Clock class="w-5 h-5 text-orange-600" strokeWidth={2} />
xmlns="http://www.w3.org/2000/svg"
class="w-5 h-5 text-orange-600"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<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>
</div> </div>
<!-- Conteúdo --> <!-- Conteúdo -->
@@ -436,20 +364,7 @@
<!-- Sem notificações --> <!-- Sem notificações -->
{#if notificacoes.length === 0 && notificacoesFerias.length === 0 && notificacoesAusencias.length === 0} {#if notificacoes.length === 0 && notificacoesFerias.length === 0 && notificacoesAusencias.length === 0}
<div class="px-4 py-8 text-center text-base-content/50"> <div class="px-4 py-8 text-center text-base-content/50">
<svg <BellOff class="w-12 h-12 mx-auto mb-2 opacity-50" strokeWidth={1.5} />
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-12 h-12 mx-auto mb-2 opacity-50"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9.143 17.082a24.248 24.248 0 0 0 3.844.148m-3.844-.148a23.856 23.856 0 0 1-5.455-1.31 8.964 8.964 0 0 0 2.3-5.542m3.155 6.852a3 3 0 0 0 5.667 1.97m1.965-2.277L21 21m-4.225-4.225a23.81 23.81 0 0 0 3.536-1.003A8.967 8.967 0 0 1 18 9.75V9A6 6 0 0 0 6.53 6.53m10.245 10.245L6.53 6.53M3 3l3.53 3.53"
/>
</svg>
<p class="text-sm">Nenhuma notificação</p> <p class="text-sm">Nenhuma notificação</p>
</div> </div>
{/if} {/if}

View File

@@ -1,39 +1,42 @@
<script lang="ts"> <script lang="ts">
interface Props { import type { Component } from "svelte";
title: string;
value: string | number; interface Props {
icon?: string; title: string;
trend?: { value: string | number;
value: number; Icon?: Component;
isPositive: boolean; icon?: string; // Mantido para compatibilidade retroativa
}; trend?: {
description?: string; value: number;
color?: "primary" | "secondary" | "accent" | "success" | "warning" | "error"; isPositive: boolean;
} };
description?: string;
let { title, value, icon, trend, description, color = "primary" }: Props = $props(); color?: "primary" | "secondary" | "accent" | "success" | "warning" | "error";
</script> }
<div class="stats shadow bg-base-100"> let { title, value, Icon, icon, trend, description, color = "primary" }: Props = $props();
<div class="stat"> </script>
<div class="stat-figure text-{color}">
{#if icon} <div class="stats shadow bg-base-100">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-8 h-8 stroke-current"> <div class="stat">
{@html icon} <div class="stat-figure text-{color}">
</svg> {#if Icon}
{/if} <svelte:component this={Icon} class="inline-block w-8 h-8 stroke-current" strokeWidth={2} />
</div> {:else if icon}
<div class="stat-title">{title}</div> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-8 h-8 stroke-current">
<div class="stat-value text-{color}">{value}</div> {@html icon}
{#if description} </svg>
<div class="stat-desc">{description}</div> {/if}
{/if} </div>
{#if trend} <div class="stat-title">{title}</div>
<div class="stat-desc {trend.isPositive ? 'text-success' : 'text-error'}"> <div class="stat-value text-{color}">{value}</div>
{trend.isPositive ? '↗︎' : '↘︎'} {Math.abs(trend.value)}% {#if description}
</div> <div class="stat-desc">{description}</div>
{/if} {/if}
</div> {#if trend}
</div> <div class="stat-desc {trend.isPositive ? 'text-success' : 'text-error'}">
{trend.isPositive ? '↗︎' : '↘︎'} {Math.abs(trend.value)}%
</div>
{/if}
</div>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -1,48 +1,43 @@
<script lang="ts"> <script lang="ts">
</script> import { ShoppingCart, ShoppingBag, Plus } from "lucide-svelte";
</script>
<main class="container mx-auto px-4 py-4">
<div class="text-sm breadcrumbs mb-4"> <main class="container mx-auto px-4 py-4">
<ul> <div class="text-sm breadcrumbs mb-4">
<li><a href="/" class="text-primary hover:underline">Dashboard</a></li> <ul>
<li>Compras</li> <li><a href="/" class="text-primary hover:underline">Dashboard</a></li>
</ul> <li>Compras</li>
</div> </ul>
</div>
<div class="mb-6">
<div class="flex items-center gap-4 mb-2"> <div class="mb-6">
<div class="p-3 bg-cyan-500/20 rounded-xl"> <div class="flex items-center gap-4 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-cyan-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <div class="p-3 bg-cyan-500/20 rounded-xl">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" /> <ShoppingCart class="h-8 w-8 text-cyan-600" strokeWidth={2} />
</svg> </div>
</div> <div>
<div> <h1 class="text-3xl font-bold text-primary">Compras</h1>
<h1 class="text-3xl font-bold text-primary">Compras</h1> <p class="text-base-content/70">Gestão de compras e aquisições</p>
<p class="text-base-content/70">Gestão de compras e aquisições</p> </div>
</div> </div>
</div> </div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card bg-base-100 shadow-xl"> <div class="card-body">
<div class="card-body"> <div class="flex flex-col items-center justify-center py-12 text-center">
<div class="flex flex-col items-center justify-center py-12 text-center"> <div class="mb-6">
<div class="mb-6"> <ShoppingBag class="h-24 w-24 text-base-content/20" strokeWidth={1.5} />
<svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24 text-base-content/20" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" /> <h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2>
</svg> <p class="text-base-content/70 max-w-md mb-6">
</div> O módulo de Compras está sendo desenvolvido e em breve estará disponível com funcionalidades completas para gestão de compras e aquisições.
<h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2> </p>
<p class="text-base-content/70 max-w-md mb-6"> <div class="badge badge-warning badge-lg gap-2">
O módulo de Compras está sendo desenvolvido e em breve estará disponível com funcionalidades completas para gestão de compras e aquisições. <Plus class="h-4 w-4" strokeWidth={2} />
</p> Em Desenvolvimento
<div class="badge badge-warning badge-lg gap-2"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /> </div>
</svg> </div>
Em Desenvolvimento </main>
</div>
</div>
</div>
</div>
</main>

View File

@@ -1,48 +1,43 @@
<script lang="ts"> <script lang="ts">
</script> import { Megaphone, Edit, Plus } from "lucide-svelte";
</script>
<main class="container mx-auto px-4 py-4">
<div class="text-sm breadcrumbs mb-4"> <main class="container mx-auto px-4 py-4">
<ul> <div class="text-sm breadcrumbs mb-4">
<li><a href="/" class="text-primary hover:underline">Dashboard</a></li> <ul>
<li>Comunicação</li> <li><a href="/" class="text-primary hover:underline">Dashboard</a></li>
</ul> <li>Comunicação</li>
</div> </ul>
</div>
<div class="mb-6">
<div class="flex items-center gap-4 mb-2"> <div class="mb-6">
<div class="p-3 bg-pink-500/20 rounded-xl"> <div class="flex items-center gap-4 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-pink-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <div class="p-3 bg-pink-500/20 rounded-xl">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5.882V19.24a1.76 1.76 0 01-3.417.592l-2.147-6.15M18 13a3 3 0 100-6M5.436 13.683A4.001 4.001 0 017 6h1.832c4.1 0 7.625-1.234 9.168-3v14c-1.543-1.766-5.067-3-9.168-3H7a3.988 3.988 0 01-1.564-.317z" /> <Megaphone class="h-8 w-8 text-pink-600" strokeWidth={2} />
</svg> </div>
</div> <div>
<div> <h1 class="text-3xl font-bold text-primary">Comunicação</h1>
<h1 class="text-3xl font-bold text-primary">Comunicação</h1> <p class="text-base-content/70">Gestão de comunicação institucional</p>
<p class="text-base-content/70">Gestão de comunicação institucional</p> </div>
</div> </div>
</div> </div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card bg-base-100 shadow-xl"> <div class="card-body">
<div class="card-body"> <div class="flex flex-col items-center justify-center py-12 text-center">
<div class="flex flex-col items-center justify-center py-12 text-center"> <div class="mb-6">
<div class="mb-6"> <Edit class="h-24 w-24 text-base-content/20" strokeWidth={1.5} />
<svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24 text-base-content/20" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" /> <h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2>
</svg> <p class="text-base-content/70 max-w-md mb-6">
</div> O módulo de Comunicação está sendo desenvolvido e em breve estará disponível com funcionalidades completas de gestão de comunicação institucional.
<h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2> </p>
<p class="text-base-content/70 max-w-md mb-6"> <div class="badge badge-warning badge-lg gap-2">
O módulo de Comunicação está sendo desenvolvido e em breve estará disponível com funcionalidades completas de gestão de comunicação institucional. <Plus class="h-4 w-4" strokeWidth={2} />
</p> Em Desenvolvimento
<div class="badge badge-warning badge-lg gap-2"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /> </div>
</svg> </div>
Em Desenvolvimento </main>
</div>
</div>
</div>
</div>
</main>

View File

@@ -1,99 +1,88 @@
<script lang="ts"> <script lang="ts">
</script> import { BarChart3, ClipboardCheck, Plus, CheckCircle2, Clock, TrendingUp } from "lucide-svelte";
</script>
<main class="container mx-auto px-4 py-4">
<!-- Breadcrumb --> <main class="container mx-auto px-4 py-4">
<div class="text-sm breadcrumbs mb-4"> <!-- Breadcrumb -->
<ul> <div class="text-sm breadcrumbs mb-4">
<li><a href="/" class="text-primary hover:underline">Dashboard</a></li> <ul>
<li>Controladoria</li> <li><a href="/" class="text-primary hover:underline">Dashboard</a></li>
</ul> <li>Controladoria</li>
</div> </ul>
</div>
<!-- Cabeçalho -->
<div class="mb-6"> <!-- Cabeçalho -->
<div class="flex items-center gap-4 mb-2"> <div class="mb-6">
<div class="p-3 bg-purple-500/20 rounded-xl"> <div class="flex items-center gap-4 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-purple-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <div class="p-3 bg-purple-500/20 rounded-xl">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /> <BarChart3 class="h-8 w-8 text-purple-600" strokeWidth={2} />
</svg> </div>
</div> <div>
<div> <h1 class="text-3xl font-bold text-primary">Controladoria</h1>
<h1 class="text-3xl font-bold text-primary">Controladoria</h1> <p class="text-base-content/70">Controle e auditoria interna da secretaria</p>
<p class="text-base-content/70">Controle e auditoria interna da secretaria</p> </div>
</div> </div>
</div> </div>
</div>
<!-- Card de Aviso -->
<!-- Card de Aviso --> <div class="card bg-base-100 shadow-xl">
<div class="card bg-base-100 shadow-xl"> <div class="card-body">
<div class="card-body"> <div class="flex flex-col items-center justify-center py-12 text-center">
<div class="flex flex-col items-center justify-center py-12 text-center"> <div class="mb-6">
<div class="mb-6"> <ClipboardCheck class="h-24 w-24 text-base-content/20" strokeWidth={1.5} />
<svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24 text-base-content/20" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<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 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01" /> <h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2>
</svg> <p class="text-base-content/70 max-w-md mb-6">
</div> O módulo de Controladoria está sendo desenvolvido e em breve estará disponível com funcionalidades completas de controle e auditoria.
<h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2> </p>
<p class="text-base-content/70 max-w-md mb-6"> <div class="badge badge-warning badge-lg gap-2">
O módulo de Controladoria está sendo desenvolvido e em breve estará disponível com funcionalidades completas de controle e auditoria. <Plus class="h-4 w-4" strokeWidth={2} />
</p> Em Desenvolvimento
<div class="badge badge-warning badge-lg gap-2"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /> </div>
</svg> </div>
Em Desenvolvimento
</div> <!-- Funcionalidades Previstas -->
</div> <div class="mt-6">
</div> <h3 class="text-xl font-bold mb-4">Funcionalidades Previstas</h3>
</div> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow">
<!-- Funcionalidades Previstas --> <div class="card-body">
<div class="mt-6"> <div class="flex items-center gap-3 mb-2">
<h3 class="text-xl font-bold mb-4">Funcionalidades Previstas</h3> <div class="p-2 bg-primary/10 rounded-lg">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <CheckCircle2 class="h-6 w-6 text-primary" strokeWidth={2} />
<div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow"> </div>
<div class="card-body"> <h4 class="font-semibold">Auditoria Interna</h4>
<div class="flex items-center gap-3 mb-2"> </div>
<div class="p-2 bg-primary/10 rounded-lg"> <p class="text-sm text-base-content/70">Controle e verificação de processos internos</p>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> </div>
</svg>
</div> <div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow">
<h4 class="font-semibold">Auditoria Interna</h4> <div class="card-body">
</div> <div class="flex items-center gap-3 mb-2">
<p class="text-sm text-base-content/70">Controle e verificação de processos internos</p> <div class="p-2 bg-primary/10 rounded-lg">
</div> <Clock class="h-6 w-6 text-primary" strokeWidth={2} />
</div> </div>
<h4 class="font-semibold">Compliance</h4>
<div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow"> </div>
<div class="card-body"> <p class="text-sm text-base-content/70">Conformidade com normas e regulamentos</p>
<div class="flex items-center gap-3 mb-2"> </div>
<div class="p-2 bg-primary/10 rounded-lg"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<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" /> <div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow">
</svg> <div class="card-body">
</div> <div class="flex items-center gap-3 mb-2">
<h4 class="font-semibold">Compliance</h4> <div class="p-2 bg-primary/10 rounded-lg">
</div> <TrendingUp class="h-6 w-6 text-primary" strokeWidth={2} />
<p class="text-sm text-base-content/70">Conformidade com normas e regulamentos</p> </div>
</div> <h4 class="font-semibold">Indicadores de Gestão</h4>
</div> </div>
<p class="text-sm text-base-content/70">Monitoramento de KPIs e métricas</p>
<div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow"> </div>
<div class="card-body"> </div>
<div class="flex items-center gap-3 mb-2"> </div>
<div class="p-2 bg-primary/10 rounded-lg"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </main>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 12l3-3 3 3 4-4M8 21l4-4 4 4M3 4h18M4 4h16v12a1 1 0 01-1 1H5a1 1 0 01-1-1V4z" />
</svg>
</div>
<h4 class="font-semibold">Indicadores de Gestão</h4>
</div>
<p class="text-sm text-base-content/70">Monitoramento de KPIs e métricas</p>
</div>
</div>
</div>
</div>
</main>

View File

@@ -1,99 +1,88 @@
<script lang="ts"> <script lang="ts">
</script> import { DollarSign, Building2, Plus, Calculator, TrendingUp, FileText } from "lucide-svelte";
</script>
<main class="container mx-auto px-4 py-4">
<!-- Breadcrumb --> <main class="container mx-auto px-4 py-4">
<div class="text-sm breadcrumbs mb-4"> <!-- Breadcrumb -->
<ul> <div class="text-sm breadcrumbs mb-4">
<li><a href="/" class="text-primary hover:underline">Dashboard</a></li> <ul>
<li>Financeiro</li> <li><a href="/" class="text-primary hover:underline">Dashboard</a></li>
</ul> <li>Financeiro</li>
</div> </ul>
</div>
<!-- Cabeçalho -->
<div class="mb-6"> <!-- Cabeçalho -->
<div class="flex items-center gap-4 mb-2"> <div class="mb-6">
<div class="p-3 bg-green-500/20 rounded-xl"> <div class="flex items-center gap-4 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <div class="p-3 bg-green-500/20 rounded-xl">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> <DollarSign class="h-8 w-8 text-green-600" strokeWidth={2} />
</svg> </div>
</div> <div>
<div> <h1 class="text-3xl font-bold text-primary">Financeiro</h1>
<h1 class="text-3xl font-bold text-primary">Financeiro</h1> <p class="text-base-content/70">Gestão financeira e orçamentária da secretaria</p>
<p class="text-base-content/70">Gestão financeira e orçamentária da secretaria</p> </div>
</div> </div>
</div> </div>
</div>
<!-- Card de Aviso -->
<!-- Card de Aviso --> <div class="card bg-base-100 shadow-xl">
<div class="card bg-base-100 shadow-xl"> <div class="card-body">
<div class="card-body"> <div class="flex flex-col items-center justify-center py-12 text-center">
<div class="flex flex-col items-center justify-center py-12 text-center"> <div class="mb-6">
<div class="mb-6"> <Building2 class="h-24 w-24 text-base-content/20" strokeWidth={1.5} />
<svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24 text-base-content/20" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" /> <h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2>
</svg> <p class="text-base-content/70 max-w-md mb-6">
</div> O módulo Financeiro está sendo desenvolvido e em breve estará disponível com funcionalidades completas de gestão financeira e orçamentária.
<h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2> </p>
<p class="text-base-content/70 max-w-md mb-6"> <div class="badge badge-warning badge-lg gap-2">
O módulo Financeiro está sendo desenvolvido e em breve estará disponível com funcionalidades completas de gestão financeira e orçamentária. <Plus class="h-4 w-4" strokeWidth={2} />
</p> Em Desenvolvimento
<div class="badge badge-warning badge-lg gap-2"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /> </div>
</svg> </div>
Em Desenvolvimento
</div> <!-- Funcionalidades Previstas -->
</div> <div class="mt-6">
</div> <h3 class="text-xl font-bold mb-4">Funcionalidades Previstas</h3>
</div> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow">
<!-- Funcionalidades Previstas --> <div class="card-body">
<div class="mt-6"> <div class="flex items-center gap-3 mb-2">
<h3 class="text-xl font-bold mb-4">Funcionalidades Previstas</h3> <div class="p-2 bg-primary/10 rounded-lg">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <Calculator class="h-6 w-6 text-primary" strokeWidth={2} />
<div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow"> </div>
<div class="card-body"> <h4 class="font-semibold">Controle Orçamentário</h4>
<div class="flex items-center gap-3 mb-2"> </div>
<div class="p-2 bg-primary/10 rounded-lg"> <p class="text-sm text-base-content/70">Gestão e acompanhamento do orçamento anual</p>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z" /> </div>
</svg>
</div> <div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow">
<h4 class="font-semibold">Controle Orçamentário</h4> <div class="card-body">
</div> <div class="flex items-center gap-3 mb-2">
<p class="text-sm text-base-content/70">Gestão e acompanhamento do orçamento anual</p> <div class="p-2 bg-primary/10 rounded-lg">
</div> <TrendingUp class="h-6 w-6 text-primary" strokeWidth={2} />
</div> </div>
<h4 class="font-semibold">Fluxo de Caixa</h4>
<div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow"> </div>
<div class="card-body"> <p class="text-sm text-base-content/70">Controle de entradas e saídas financeiras</p>
<div class="flex items-center gap-3 mb-2"> </div>
<div class="p-2 bg-primary/10 rounded-lg"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z" /> <div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow">
</svg> <div class="card-body">
</div> <div class="flex items-center gap-3 mb-2">
<h4 class="font-semibold">Fluxo de Caixa</h4> <div class="p-2 bg-primary/10 rounded-lg">
</div> <FileText class="h-6 w-6 text-primary" strokeWidth={2} />
<p class="text-sm text-base-content/70">Controle de entradas e saídas financeiras</p> </div>
</div> <h4 class="font-semibold">Relatórios Financeiros</h4>
</div> </div>
<p class="text-sm text-base-content/70">Geração de relatórios e demonstrativos</p>
<div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow"> </div>
<div class="card-body"> </div>
<div class="flex items-center gap-3 mb-2"> </div>
<div class="p-2 bg-primary/10 rounded-lg"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </main>
<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>
</div>
<h4 class="font-semibold">Relatórios Financeiros</h4>
</div>
<p class="text-sm text-base-content/70">Geração de relatórios e demonstrativos</p>
</div>
</div>
</div>
</div>
</main>

View File

@@ -1,48 +1,43 @@
<script lang="ts"> <script lang="ts">
</script> import { Scale, BookOpen, Plus } from "lucide-svelte";
</script>
<main class="container mx-auto px-4 py-4">
<div class="text-sm breadcrumbs mb-4"> <main class="container mx-auto px-4 py-4">
<ul> <div class="text-sm breadcrumbs mb-4">
<li><a href="/" class="text-primary hover:underline">Dashboard</a></li> <ul>
<li>Jurídico</li> <li><a href="/" class="text-primary hover:underline">Dashboard</a></li>
</ul> <li>Jurídico</li>
</div> </ul>
</div>
<div class="mb-6">
<div class="flex items-center gap-4 mb-2"> <div class="mb-6">
<div class="p-3 bg-red-500/20 rounded-xl"> <div class="flex items-center gap-4 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-red-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <div class="p-3 bg-red-500/20 rounded-xl">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3" /> <Scale class="h-8 w-8 text-red-600" strokeWidth={2} />
</svg> </div>
</div> <div>
<div> <h1 class="text-3xl font-bold text-primary">Jurídico</h1>
<h1 class="text-3xl font-bold text-primary">Jurídico</h1> <p class="text-base-content/70">Assessoria jurídica e gestão de processos</p>
<p class="text-base-content/70">Assessoria jurídica e gestão de processos</p> </div>
</div> </div>
</div> </div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card bg-base-100 shadow-xl"> <div class="card-body">
<div class="card-body"> <div class="flex flex-col items-center justify-center py-12 text-center">
<div class="flex flex-col items-center justify-center py-12 text-center"> <div class="mb-6">
<div class="mb-6"> <BookOpen class="h-24 w-24 text-base-content/20" strokeWidth={1.5} />
<svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24 text-base-content/20" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" /> <h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2>
</svg> <p class="text-base-content/70 max-w-md mb-6">
</div> O módulo Jurídico está sendo desenvolvido e em breve estará disponível com funcionalidades completas de assessoria jurídica e gestão de processos.
<h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2> </p>
<p class="text-base-content/70 max-w-md mb-6"> <div class="badge badge-warning badge-lg gap-2">
O módulo Jurídico está sendo desenvolvido e em breve estará disponível com funcionalidades completas de assessoria jurídica e gestão de processos. <Plus class="h-4 w-4" strokeWidth={2} />
</p> Em Desenvolvimento
<div class="badge badge-warning badge-lg gap-2"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /> </div>
</svg> </div>
Em Desenvolvimento </main>
</div>
</div>
</div>
</div>
</main>

View File

@@ -1,99 +1,88 @@
<script lang="ts"> <script lang="ts">
</script> import { FileText, ClipboardCopy, Plus, Users, FileDoc } from "lucide-svelte";
</script>
<main class="container mx-auto px-4 py-4">
<!-- Breadcrumb --> <main class="container mx-auto px-4 py-4">
<div class="text-sm breadcrumbs mb-4"> <!-- Breadcrumb -->
<ul> <div class="text-sm breadcrumbs mb-4">
<li><a href="/" class="text-primary hover:underline">Dashboard</a></li> <ul>
<li>Licitações</li> <li><a href="/" class="text-primary hover:underline">Dashboard</a></li>
</ul> <li>Licitações</li>
</div> </ul>
</div>
<!-- Cabeçalho -->
<div class="mb-6"> <!-- Cabeçalho -->
<div class="flex items-center gap-4 mb-2"> <div class="mb-6">
<div class="p-3 bg-orange-500/20 rounded-xl"> <div class="flex items-center gap-4 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-orange-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <div class="p-3 bg-orange-500/20 rounded-xl">
<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" /> <FileText class="h-8 w-8 text-orange-600" strokeWidth={2} />
</svg> </div>
</div> <div>
<div> <h1 class="text-3xl font-bold text-primary">Licitações</h1>
<h1 class="text-3xl font-bold text-primary">Licitações</h1> <p class="text-base-content/70">Gestão de processos licitatórios</p>
<p class="text-base-content/70">Gestão de processos licitatórios</p> </div>
</div> </div>
</div> </div>
</div>
<!-- Card de Aviso -->
<!-- Card de Aviso --> <div class="card bg-base-100 shadow-xl">
<div class="card bg-base-100 shadow-xl"> <div class="card-body">
<div class="card-body"> <div class="flex flex-col items-center justify-center py-12 text-center">
<div class="flex flex-col items-center justify-center py-12 text-center"> <div class="mb-6">
<div class="mb-6"> <FileDoc class="h-24 w-24 text-base-content/20" strokeWidth={1.5} />
<svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24 text-base-content/20" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" 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" /> <h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2>
</svg> <p class="text-base-content/70 max-w-md mb-6">
</div> O módulo de Licitações está sendo desenvolvido e em breve estará disponível com funcionalidades completas para gestão de processos licitatórios.
<h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2> </p>
<p class="text-base-content/70 max-w-md mb-6"> <div class="badge badge-warning badge-lg gap-2">
O módulo de Licitações está sendo desenvolvido e em breve estará disponível com funcionalidades completas para gestão de processos licitatórios. <Plus class="h-4 w-4" strokeWidth={2} />
</p> Em Desenvolvimento
<div class="badge badge-warning badge-lg gap-2"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /> </div>
</svg> </div>
Em Desenvolvimento
</div> <!-- Funcionalidades Previstas -->
</div> <div class="mt-6">
</div> <h3 class="text-xl font-bold mb-4">Funcionalidades Previstas</h3>
</div> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow">
<!-- Funcionalidades Previstas --> <div class="card-body">
<div class="mt-6"> <div class="flex items-center gap-3 mb-2">
<h3 class="text-xl font-bold mb-4">Funcionalidades Previstas</h3> <div class="p-2 bg-primary/10 rounded-lg">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <ClipboardCopy class="h-6 w-6 text-primary" strokeWidth={2} />
<div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow"> </div>
<div class="card-body"> <h4 class="font-semibold">Processos Licitatórios</h4>
<div class="flex items-center gap-3 mb-2"> </div>
<div class="p-2 bg-primary/10 rounded-lg"> <p class="text-sm text-base-content/70">Cadastro e acompanhamento de licitações</p>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<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" /> </div>
</svg>
</div> <div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow">
<h4 class="font-semibold">Processos Licitatórios</h4> <div class="card-body">
</div> <div class="flex items-center gap-3 mb-2">
<p class="text-sm text-base-content/70">Cadastro e acompanhamento de licitações</p> <div class="p-2 bg-primary/10 rounded-lg">
</div> <Users class="h-6 w-6 text-primary" strokeWidth={2} />
</div> </div>
<h4 class="font-semibold">Fornecedores</h4>
<div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow"> </div>
<div class="card-body"> <p class="text-sm text-base-content/70">Cadastro e gestão de fornecedores</p>
<div class="flex items-center gap-3 mb-2"> </div>
<div class="p-2 bg-primary/10 rounded-lg"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" /> <div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow">
</svg> <div class="card-body">
</div> <div class="flex items-center gap-3 mb-2">
<h4 class="font-semibold">Fornecedores</h4> <div class="p-2 bg-primary/10 rounded-lg">
</div> <FileDoc class="h-6 w-6 text-primary" strokeWidth={2} />
<p class="text-sm text-base-content/70">Cadastro e gestão de fornecedores</p> </div>
</div> <h4 class="font-semibold">Documentação</h4>
</div> </div>
<p class="text-sm text-base-content/70">Gestão de documentos e editais</p>
<div class="card bg-base-100 shadow-md hover:shadow-lg transition-shadow"> </div>
<div class="card-body"> </div>
<div class="flex items-center gap-3 mb-2"> </div>
<div class="p-2 bg-primary/10 rounded-lg"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </main>
<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>
</div>
<h4 class="font-semibold">Documentação</h4>
</div>
<p class="text-sm text-base-content/70">Gestão de documentos e editais</p>
</div>
</div>
</div>
</div>
</main>

View File

@@ -1,48 +1,43 @@
<script lang="ts"> <script lang="ts">
</script> import { Trophy, Award, Plus } from "lucide-svelte";
</script>
<main class="container mx-auto px-4 py-4">
<div class="text-sm breadcrumbs mb-4"> <main class="container mx-auto px-4 py-4">
<ul> <div class="text-sm breadcrumbs mb-4">
<li><a href="/" class="text-primary hover:underline">Dashboard</a></li> <ul>
<li>Programas Esportivos</li> <li><a href="/" class="text-primary hover:underline">Dashboard</a></li>
</ul> <li>Programas Esportivos</li>
</div> </ul>
</div>
<div class="mb-6">
<div class="flex items-center gap-4 mb-2"> <div class="mb-6">
<div class="p-3 bg-yellow-500/20 rounded-xl"> <div class="flex items-center gap-4 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-yellow-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <div class="p-3 bg-yellow-500/20 rounded-xl">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> <Trophy class="h-8 w-8 text-yellow-600" strokeWidth={2} />
</svg> </div>
</div> <div>
<div> <h1 class="text-3xl font-bold text-primary">Programas Esportivos</h1>
<h1 class="text-3xl font-bold text-primary">Programas Esportivos</h1> <p class="text-base-content/70">Gestão de programas e projetos esportivos</p>
<p class="text-base-content/70">Gestão de programas e projetos esportivos</p> </div>
</div> </div>
</div> </div>
</div>
<div class="card bg-base-100 shadow-xl">
<div class="card bg-base-100 shadow-xl"> <div class="card-body">
<div class="card-body"> <div class="flex flex-col items-center justify-center py-12 text-center">
<div class="flex flex-col items-center justify-center py-12 text-center"> <div class="mb-6">
<div class="mb-6"> <Award class="h-24 w-24 text-base-content/20" strokeWidth={1.5} />
<svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24 text-base-content/20" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V5.5A2.5 2.5 0 109.5 8H12zm-7 4h14M5 12a2 2 0 110-4h14a2 2 0 110 4M5 12v7a2 2 0 002 2h10a2 2 0 002-2v-7" /> <h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2>
</svg> <p class="text-base-content/70 max-w-md mb-6">
</div> O módulo de Programas Esportivos está sendo desenvolvido e em breve estará disponível com funcionalidades completas para gestão de programas e projetos esportivos.
<h2 class="text-2xl font-bold mb-2">Módulo em Desenvolvimento</h2> </p>
<p class="text-base-content/70 max-w-md mb-6"> <div class="badge badge-warning badge-lg gap-2">
O módulo de Programas Esportivos está sendo desenvolvido e em breve estará disponível com funcionalidades completas para gestão de programas e projetos esportivos. <Plus class="h-4 w-4" strokeWidth={2} />
</p> Em Desenvolvimento
<div class="badge badge-warning badge-lg gap-2"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /> </div>
</svg> </div>
Em Desenvolvimento </main>
</div>
</div>
</div>
</div>
</main>

View File

@@ -1,281 +1,253 @@
<script lang="ts"> <script lang="ts">
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import { useQuery } from "convex-svelte"; import { useQuery } from "convex-svelte";
import { api } from "@sgse-app/backend/convex/_generated/api"; import { api } from "@sgse-app/backend/convex/_generated/api";
import { Users, UserPlus, ClipboardList, Trash2, BarChart3, Calendar, FileText, BadgeCheck, Plus, List, ChevronRight, CheckCircle2, Info, ArrowRight } from "lucide-svelte";
// Buscar estatísticas para exibir nos cards import type { Component } from "svelte";
const statsQuery = useQuery(api.dashboard.getStats, {});
// Buscar estatísticas para exibir nos cards
const menuItems = [ const statsQuery = useQuery(api.dashboard.getStats, {});
{
categoria: "Gestão de Funcionários", interface MenuOpcao {
descricao: "Gerencie o cadastro e informações dos funcionários", nome: string;
icon: `<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12" fill="none" viewBox="0 0 24 24" stroke="currentColor"> descricao: string;
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" /> href: string;
</svg>`, Icon: Component;
gradient: "from-blue-500/10 to-blue-600/20", }
accentColor: "text-blue-600",
bgIcon: "bg-blue-500/20", interface MenuCategoria {
opcoes: [ categoria: string;
{ descricao: string;
nome: "Cadastrar Funcionário", Icon: Component;
descricao: "Adicionar novo funcionário ao sistema", gradient: string;
href: "/recursos-humanos/funcionarios/cadastro", accentColor: string;
icon: `<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> bgIcon: string;
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z" /> opcoes: MenuOpcao[];
</svg>`, }
},
{ const menuItems: MenuCategoria[] = [
nome: "Listar Funcionários", {
descricao: "Visualizar e editar cadastros", categoria: "Gestão de Funcionários",
href: "/recursos-humanos/funcionarios", descricao: "Gerencie o cadastro e informações dos funcionários",
icon: `<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> Icon: Users,
<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-3 7h3m-3 4h3m-6-4h.01M9 16h.01" /> gradient: "from-blue-500/10 to-blue-600/20",
</svg>`, accentColor: "text-blue-600",
}, bgIcon: "bg-blue-500/20",
{ opcoes: [
nome: "Excluir Cadastro", {
descricao: "Remover funcionário do sistema", nome: "Cadastrar Funcionário",
href: "/recursos-humanos/funcionarios/excluir", descricao: "Adicionar novo funcionário ao sistema",
icon: `<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> href: "/recursos-humanos/funcionarios/cadastro",
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> Icon: UserPlus,
</svg>`, },
}, {
{ nome: "Listar Funcionários",
nome: "Relatórios", descricao: "Visualizar e editar cadastros",
descricao: "Visualizar estatísticas e gráficos", href: "/recursos-humanos/funcionarios",
href: "/recursos-humanos/funcionarios/relatorios", Icon: ClipboardList,
icon: `<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> },
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /> {
</svg>`, nome: "Excluir Cadastro",
}, descricao: "Remover funcionário do sistema",
], href: "/recursos-humanos/funcionarios/excluir",
}, Icon: Trash2,
{ },
categoria: "Gestão de Férias e Licenças", {
descricao: "Controle de férias, atestados e licenças", nome: "Relatórios",
icon: `<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12" fill="none" viewBox="0 0 24 24" stroke="currentColor"> descricao: "Visualizar estatísticas e gráficos",
<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" /> href: "/recursos-humanos/funcionarios/relatorios",
</svg>`, Icon: BarChart3,
gradient: "from-purple-500/10 to-purple-600/20", },
accentColor: "text-purple-600", ],
bgIcon: "bg-purple-500/20", },
opcoes: [ {
{ categoria: "Gestão de Férias e Licenças",
nome: "Gestão de Férias", descricao: "Controle de férias, atestados e licenças",
descricao: "Controlar períodos de férias", Icon: Calendar,
href: "/recursos-humanos/ferias", gradient: "from-purple-500/10 to-purple-600/20",
icon: `<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> accentColor: "text-purple-600",
<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" /> bgIcon: "bg-purple-500/20",
</svg>`, opcoes: [
}, {
{ nome: "Gestão de Férias",
nome: "Atestados & Licenças", descricao: "Controlar períodos de férias",
descricao: "Registrar atestados e licenças", href: "/recursos-humanos/ferias",
href: "/recursos-humanos/atestados-licencas", Icon: Calendar,
icon: `<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> },
<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>`, nome: "Atestados & Licenças",
}, descricao: "Registrar atestados e licenças",
], href: "/recursos-humanos/atestados-licencas",
}, Icon: FileText,
{ },
categoria: "Gestão de Símbolos", ],
descricao: "Gerencie cargos comissionados e funções gratificadas", },
icon: `<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12" fill="none" viewBox="0 0 24 24" stroke="currentColor"> {
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z" /> categoria: "Gestão de Símbolos",
</svg>`, descricao: "Gerencie cargos comissionados e funções gratificadas",
gradient: "from-green-500/10 to-green-600/20", Icon: BadgeCheck,
accentColor: "text-green-600", gradient: "from-green-500/10 to-green-600/20",
bgIcon: "bg-green-500/20", accentColor: "text-green-600",
opcoes: [ bgIcon: "bg-green-500/20",
{ opcoes: [
nome: "Cadastrar Símbolo", {
descricao: "Adicionar novo cargo ou função", nome: "Cadastrar Símbolo",
href: "/recursos-humanos/simbolos/cadastro", descricao: "Adicionar novo cargo ou função",
icon: `<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> href: "/recursos-humanos/simbolos/cadastro",
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v3m0 0v3m0-3h3m-3 0H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" /> Icon: Plus,
</svg>`, },
}, {
{ nome: "Listar Símbolos",
nome: "Listar Símbolos", descricao: "Visualizar e editar símbolos",
descricao: "Visualizar e editar símbolos", href: "/recursos-humanos/simbolos",
href: "/recursos-humanos/simbolos", Icon: List,
icon: `<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> },
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" /> ],
</svg>`, },
}, ];
], </script>
},
]; <main class="container mx-auto px-4 py-4">
</script> <!-- Cabeçalho -->
<div class="mb-8">
<main class="container mx-auto px-4 py-4"> <h1 class="text-4xl font-bold text-primary mb-2">Recursos Humanos</h1>
<!-- Cabeçalho --> <p class="text-lg text-base-content/70">
<div class="mb-8"> Gerencie funcionários, símbolos e visualize relatórios do departamento
<h1 class="text-4xl font-bold text-primary mb-2">Recursos Humanos</h1> </p>
<p class="text-lg text-base-content/70"> </div>
Gerencie funcionários, símbolos e visualize relatórios do departamento
</p> <!-- Estatísticas Rápidas -->
</div> {#if statsQuery.data}
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
<!-- Estatísticas Rápidas --> <div class="stats shadow-lg bg-gradient-to-br from-primary/10 to-primary/20">
{#if statsQuery.data} <div class="stat">
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8"> <div class="stat-figure text-primary">
<div class="stats shadow-lg bg-gradient-to-br from-primary/10 to-primary/20"> <Users class="h-8 w-8" strokeWidth={2} />
<div class="stat"> </div>
<div class="stat-figure text-primary"> <div class="stat-title">Total</div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <div class="stat-value text-primary">{statsQuery.data.totalFuncionarios}</div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" /> <div class="stat-desc">Funcionários cadastrados</div>
</svg> </div>
</div> </div>
<div class="stat-title">Total</div>
<div class="stat-value text-primary">{statsQuery.data.totalFuncionarios}</div> <div class="stats shadow-lg bg-gradient-to-br from-success/10 to-success/20">
<div class="stat-desc">Funcionários cadastrados</div> <div class="stat">
</div> <div class="stat-figure text-success">
</div> <CheckCircle2 class="h-8 w-8" strokeWidth={2} />
</div>
<div class="stats shadow-lg bg-gradient-to-br from-success/10 to-success/20"> <div class="stat-title">Ativos</div>
<div class="stat"> <div class="stat-value text-success">{statsQuery.data.funcionariosAtivos}</div>
<div class="stat-figure text-success"> <div class="stat-desc">Funcionários ativos</div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </div>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> </div>
</svg>
</div> <div class="stats shadow-lg bg-gradient-to-br from-secondary/10 to-secondary/20">
<div class="stat-title">Ativos</div> <div class="stat">
<div class="stat-value text-success">{statsQuery.data.funcionariosAtivos}</div> <div class="stat-figure text-secondary">
<div class="stat-desc">Funcionários ativos</div> <BadgeCheck class="h-8 w-8" strokeWidth={2} />
</div> </div>
</div> <div class="stat-title">Símbolos</div>
<div class="stat-value text-secondary">{statsQuery.data.totalSimbolos}</div>
<div class="stats shadow-lg bg-gradient-to-br from-secondary/10 to-secondary/20"> <div class="stat-desc">Cargos e funções</div>
<div class="stat"> </div>
<div class="stat-figure text-secondary"> </div>
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z" /> <div class="stats shadow-lg bg-gradient-to-br from-accent/10 to-accent/20">
</svg> <div class="stat">
</div> <div class="stat-figure text-accent">
<div class="stat-title">Símbolos</div> <BarChart3 class="h-8 w-8" strokeWidth={2} />
<div class="stat-value text-secondary">{statsQuery.data.totalSimbolos}</div> </div>
<div class="stat-desc">Cargos e funções</div> <div class="stat-title">CC / FG</div>
</div> <div class="stat-value text-accent">{statsQuery.data.cargoComissionado} / {statsQuery.data.funcaoGratificada}</div>
</div> <div class="stat-desc">Distribuição</div>
</div>
<div class="stats shadow-lg bg-gradient-to-br from-accent/10 to-accent/20"> </div>
<div class="stat"> </div>
<div class="stat-figure text-accent"> {/if}
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" /> <!-- Menu de Opções -->
</svg> <div class="space-y-8">
</div> {#each menuItems as categoria}
<div class="stat-title">CC / FG</div> <div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-all duration-300">
<div class="stat-value text-accent">{statsQuery.data.cargoComissionado} / {statsQuery.data.funcaoGratificada}</div> <div class="card-body">
<div class="stat-desc">Distribuição</div> <!-- Cabeçalho da Categoria -->
</div> <div class="flex items-start gap-6 mb-6">
</div> <div class="p-4 {categoria.bgIcon} rounded-2xl">
</div> <svelte:component this={categoria.Icon} class="h-12 w-12 {categoria.accentColor}" strokeWidth={2} />
{/if} </div>
<div class="flex-1">
<!-- Menu de Opções --> <h2 class="card-title text-2xl mb-2 {categoria.accentColor}">
<div class="space-y-8"> {categoria.categoria}
{#each menuItems as categoria} </h2>
<div class="card bg-base-100 shadow-xl hover:shadow-2xl transition-all duration-300"> <p class="text-base-content/70">{categoria.descricao}</p>
<div class="card-body"> </div>
<!-- Cabeçalho da Categoria --> </div>
<div class="flex items-start gap-6 mb-6">
<div class="p-4 {categoria.bgIcon} rounded-2xl"> <!-- Grid de Opções -->
<div class="{categoria.accentColor}"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{@html categoria.icon} {#each categoria.opcoes as opcao}
</div> <a
</div> href={opcao.href}
<div class="flex-1"> class="group relative overflow-hidden rounded-xl border-2 border-base-300 bg-gradient-to-br {categoria.gradient} p-6 hover:border-primary hover:shadow-lg transition-all duration-300 transform hover:-translate-y-1"
<h2 class="card-title text-2xl mb-2 {categoria.accentColor}"> >
{categoria.categoria} <div class="flex flex-col h-full">
</h2> <div class="flex items-start justify-between mb-4">
<p class="text-base-content/70">{categoria.descricao}</p> <div class="p-3 bg-base-100 rounded-lg group-hover:bg-primary group-hover:text-white transition-colors duration-300">
</div> <svelte:component
</div> this={opcao.Icon}
class="h-5 w-5 {categoria.accentColor} group-hover:text-white transition-colors duration-300"
<!-- Grid de Opções --> strokeWidth={2}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4"> />
{#each categoria.opcoes as opcao} </div>
<a <ChevronRight
href={opcao.href} class="h-5 w-5 text-base-content/30 group-hover:text-primary transition-colors duration-300"
class="group relative overflow-hidden rounded-xl border-2 border-base-300 bg-gradient-to-br {categoria.gradient} p-6 hover:border-primary hover:shadow-lg transition-all duration-300 transform hover:-translate-y-1" strokeWidth={2}
> />
<div class="flex flex-col h-full"> </div>
<div class="flex items-start justify-between mb-4"> <h3 class="text-lg font-bold text-base-content mb-2 group-hover:text-primary transition-colors duration-300">
<div class="p-3 bg-base-100 rounded-lg group-hover:bg-primary group-hover:text-white transition-colors duration-300"> {opcao.nome}
<div class="{categoria.accentColor} group-hover:text-white"> </h3>
{@html opcao.icon} <p class="text-sm text-base-content/70 flex-1">
</div> {opcao.descricao}
</div> </p>
<svg </div>
xmlns="http://www.w3.org/2000/svg" </a>
class="h-5 w-5 text-base-content/30 group-hover:text-primary transition-colors duration-300" {/each}
fill="none" </div>
viewBox="0 0 24 24" </div>
stroke="currentColor" </div>
> {/each}
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> </div>
</svg>
</div> <!-- Card de Ajuda -->
<h3 class="text-lg font-bold text-base-content mb-2 group-hover:text-primary transition-colors duration-300"> <div class="alert alert-info shadow-lg mt-8">
{opcao.nome} <Info class="stroke-current shrink-0 w-6 h-6" strokeWidth={2} />
</h3> <div>
<p class="text-sm text-base-content/70 flex-1"> <h3 class="font-bold">Precisa de ajuda?</h3>
{opcao.descricao} <div class="text-sm">
</p> Entre em contato com o suporte técnico ou consulte a documentação do sistema para mais informações sobre as funcionalidades de Recursos Humanos.
</div> </div>
</a> </div>
{/each} </div>
</div> </main>
</div>
</div> <style>
{/each} @keyframes fadeInUp {
</div> from {
opacity: 0;
<!-- Card de Ajuda --> transform: translateY(20px);
<div class="alert alert-info shadow-lg mt-8"> }
<svg to {
xmlns="http://www.w3.org/2000/svg" opacity: 1;
fill="none" transform: translateY(0);
viewBox="0 0 24 24" }
class="stroke-current shrink-0 w-6 h-6" }
>
<path .card {
stroke-linecap="round" animation: fadeInUp 0.5s ease-out;
stroke-linejoin="round" }
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" .stats {
></path> animation: fadeInUp 0.6s ease-out;
</svg> }
<div> </style>
<h3 class="font-bold">Precisa de ajuda?</h3>
<div class="text-sm">
Entre em contato com o suporte técnico ou consulte a documentação do sistema para mais informações sobre as funcionalidades de Recursos Humanos.
</div>
</div>
</div>
</main>
<style>
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card {
animation: fadeInUp 0.5s ease-out;
}
.stats {
animation: fadeInUp 0.6s ease-out;
}
</style>

View File

@@ -2,6 +2,7 @@
import { useQuery, useConvexClient } from "convex-svelte"; import { useQuery, useConvexClient } from "convex-svelte";
import { api } from "@sgse-app/backend/convex/_generated/api"; import { api } from "@sgse-app/backend/convex/_generated/api";
import StatsCard from "$lib/components/ti/StatsCard.svelte"; import StatsCard from "$lib/components/ti/StatsCard.svelte";
import { BarChart3, Users, CheckCircle2, Ban, Clock, Plus, Layers, FileText, Info } from "lucide-svelte";
const client = useConvexClient(); const client = useConvexClient();
const usuariosQuery = useQuery(api.usuarios.listar, {}); const usuariosQuery = useQuery(api.usuarios.listar, {});
@@ -45,9 +46,7 @@
<div class="flex items-center justify-between mb-8"> <div class="flex items-center justify-between mb-8">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="p-3 bg-primary/10 rounded-xl"> <div class="p-3 bg-primary/10 rounded-xl">
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <BarChart3 class="h-8 w-8 text-primary" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
</div> </div>
<div> <div>
<h1 class="text-3xl font-bold text-base-content">Dashboard Administrativo TI</h1> <h1 class="text-3xl font-bold text-base-content">Dashboard Administrativo TI</h1>
@@ -62,7 +61,7 @@
<StatsCard <StatsCard
title="Total de Usuários" title="Total de Usuários"
value={stats.total} value={stats.total}
icon='<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />' Icon={Users}
color="primary" color="primary"
/> />
@@ -70,7 +69,7 @@
title="Usuários Ativos" title="Usuários Ativos"
value={stats.ativos} value={stats.ativos}
description="{stats.total > 0 ? ((stats.ativos / stats.total) * 100).toFixed(1) + '% do total' : '0% do total'}" description="{stats.total > 0 ? ((stats.ativos / stats.total) * 100).toFixed(1) + '% do total' : '0% do total'}"
icon='<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />' Icon={CheckCircle2}
color="success" color="success"
/> />
@@ -78,7 +77,7 @@
title="Usuários Bloqueados" title="Usuários Bloqueados"
value={stats.bloqueados} value={stats.bloqueados}
description="Requerem atenção" description="Requerem atenção"
icon='<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636" />' Icon={Ban}
color="error" color="error"
/> />
@@ -86,7 +85,7 @@
title="Usuários Inativos" title="Usuários Inativos"
value={stats.inativos} value={stats.inativos}
description="Desativados" description="Desativados"
icon='<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" />' Icon={Clock}
color="warning" color="warning"
/> />
</div> </div>
@@ -102,23 +101,17 @@
<h2 class="card-title text-2xl mb-4">Ações Rápidas</h2> <h2 class="card-title text-2xl mb-4">Ações Rápidas</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<a href="/ti/usuarios" class="btn btn-primary"> <a href="/ti/usuarios" class="btn btn-primary">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <Plus class="h-5 w-5" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
Criar Usuário Criar Usuário
</a> </a>
<a href="/ti/perfis" class="btn btn-secondary"> <a href="/ti/perfis" class="btn btn-secondary">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <Layers class="h-5 w-5" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
</svg>
Gerenciar Perfis Gerenciar Perfis
</a> </a>
<a href="/ti/auditoria" class="btn btn-accent"> <a href="/ti/auditoria" class="btn btn-accent">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <FileText class="h-5 w-5" strokeWidth={2} />
<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>
Ver Logs Ver Logs
</a> </a>
</div> </div>
@@ -127,9 +120,7 @@
<!-- Informação Sistema --> <!-- Informação Sistema -->
<div class="alert alert-info"> <div class="alert alert-info">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6"> <Info class="stroke-current shrink-0 w-6 h-6" strokeWidth={2} />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>Sistema de Gestão da Secretaria de Esportes - Versão 2.0 com controle avançado de acesso</span> <span>Sistema de Gestão da Secretaria de Esportes - Versão 2.0 com controle avançado de acesso</span>
</div> </div>
</div> </div>

View File

@@ -6,6 +6,7 @@
import type { Id } from "@sgse-app/backend/convex/_generated/dataModel"; import type { Id } from "@sgse-app/backend/convex/_generated/dataModel";
import { format } from "date-fns"; import { format } from "date-fns";
import { ptBR } from "date-fns/locale"; import { ptBR } from "date-fns/locale";
import { Users, Shield, AlertTriangle, Info, Building2 } from "lucide-svelte";
type Role = { type Role = {
_id: Id<"roles">; _id: Id<"roles">;
@@ -174,35 +175,35 @@
<StatsCard <StatsCard
title="Total de Perfis" title="Total de Perfis"
value={stats.total} value={stats.total}
icon='<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />' Icon={Users}
color="primary" color="primary"
/> />
<StatsCard <StatsCard
title="Nível Máximo" title="Nível Máximo"
value={stats.nivelMaximo} value={stats.nivelMaximo}
description="Acesso total" description="Acesso total"
icon='<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />' Icon={Shield}
color="error" color="error"
/> />
<StatsCard <StatsCard
title="Nível Alto" title="Nível Alto"
value={stats.nivelAlto} value={stats.nivelAlto}
description="Acesso elevado" description="Acesso elevado"
icon='<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" />' Icon={AlertTriangle}
color="warning" color="warning"
/> />
<StatsCard <StatsCard
title="Nível Médio" title="Nível Médio"
value={stats.nivelMedio} value={stats.nivelMedio}
description="Acesso padrão" description="Acesso padrão"
icon='<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />' Icon={Info}
color="info" color="info"
/> />
<StatsCard <StatsCard
title="Com Setor" title="Com Setor"
value={stats.comSetor} value={stats.comSetor}
description="{stats.total > 0 ? ((stats.comSetor / stats.total) * 100).toFixed(0) + '% do total' : '0%'}" description="{stats.total > 0 ? ((stats.comSetor / stats.total) * 100).toFixed(0) + '% do total' : '0%'}"
icon='<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 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />' Icon={Building2}
color="secondary" color="secondary"
/> />
</div> </div>

View File

@@ -6,6 +6,7 @@
import StatsCard from "$lib/components/ti/StatsCard.svelte"; import StatsCard from "$lib/components/ti/StatsCard.svelte";
import { format } from "date-fns"; import { format } from "date-fns";
import { ptBR } from "date-fns/locale"; import { ptBR } from "date-fns/locale";
import { FileText, Clock, CheckCircle2, XCircle } from "lucide-svelte";
type StatusSolicitacao = "pendente" | "aprovado" | "rejeitado"; type StatusSolicitacao = "pendente" | "aprovado" | "rejeitado";
@@ -294,7 +295,7 @@
<StatsCard <StatsCard
title="Total de Solicitações" title="Total de Solicitações"
value={stats.total} value={stats.total}
icon='<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" />' Icon={FileText}
color="primary" color="primary"
/> />
@@ -302,7 +303,7 @@
title="Pendentes" title="Pendentes"
value={stats.pendentes} value={stats.pendentes}
description="{stats.total > 0 ? ((stats.pendentes / stats.total) * 100).toFixed(1) + '% do total' : '0% do total'}" description="{stats.total > 0 ? ((stats.pendentes / stats.total) * 100).toFixed(1) + '% do total' : '0% do total'}"
icon='<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" />' Icon={Clock}
color="warning" color="warning"
/> />
@@ -310,7 +311,7 @@
title="Aprovadas" title="Aprovadas"
value={stats.aprovadas} value={stats.aprovadas}
description="{stats.total > 0 ? ((stats.aprovadas / stats.total) * 100).toFixed(1) + '% do total' : '0% do total'}" description="{stats.total > 0 ? ((stats.aprovadas / stats.total) * 100).toFixed(1) + '% do total' : '0% do total'}"
icon='<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />' Icon={CheckCircle2}
color="success" color="success"
/> />
@@ -318,7 +319,7 @@
title="Rejeitadas" title="Rejeitadas"
value={stats.rejeitadas} value={stats.rejeitadas}
description="{stats.total > 0 ? ((stats.rejeitadas / stats.total) * 100).toFixed(1) + '% do total' : '0% do total'}" description="{stats.total > 0 ? ((stats.rejeitadas / stats.total) * 100).toFixed(1) + '% do total' : '0% do total'}"
icon='<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" />' Icon={XCircle}
color="error" color="error"
/> />
</div> </div>