-
|
- {atividade.usuarioNome || "Sistema"}
+
+ {atividade.usuarioNome || "Sistema"}
+
{#if atividade.usuarioMatricula}
- {atividade.usuarioMatricula}
+
+ {atividade.usuarioMatricula}
+
{/if}
|
-
+
{getAcaoLabel(atividade.acao)}
|
- {atividade.recurso}
+ {atividade.recurso}
|
{#if atividade.detalhes}
-
+
{atividade.detalhes}
{:else}
@@ -270,25 +447,52 @@
-
-
+
+
Histórico de Logins
{#if logins?.data}
- {logins.data.length} registro{logins.data.length !== 1 ? 's' : ''}
+
+ {logins.data.length} registro{logins.data.length !== 1 ? "s" : ""}
+
{/if}
-
+
{#if !logins?.data}
{:else if logins.data.length === 0}
-
-
-
+
+
+
Nenhum login registrado
Os acessos ao sistema aparecerão aqui
@@ -312,53 +516,112 @@
-
-
+
+
- {formatarData(login.timestamp)}
+ {formatarData(login.timestamp)}
|
-
-
+
+
- {login.matriculaOuEmail}
+ {login.matriculaOuEmail}
|
{#if login.sucesso}
-
-
+
+
Sucesso
{:else}
-
-
+
+
Falhou
{#if login.motivoFalha}
- {login.motivoFalha}
+
+ {login.motivoFalha}
+
{/if}
{/if}
|
- {login.ipAddress || "-"}
+ {login.ipAddress || "-"}
|
- {login.device || "-"}
+
+ {login.device || "-"}
+
|
- {login.browser || "-"}
+
+ {login.browser || "-"}
+
|
- {login.sistema || "-"}
+
+ {login.sistema || "-"}
+
|
{/each}
@@ -372,13 +635,25 @@
-
-
+
+
Informação Importante
- Os logs são armazenados permanentemente e não podem ser alterados ou excluídos.
+
+ Os logs são armazenados permanentemente e não podem ser alterados ou
+ excluídos.
+
-
diff --git a/apps/web/src/routes/(dashboard)/ti/monitoramento/+page.svelte b/apps/web/src/routes/(dashboard)/ti/monitoramento/+page.svelte
index 4d620c1..82ab2b4 100644
--- a/apps/web/src/routes/(dashboard)/ti/monitoramento/+page.svelte
+++ b/apps/web/src/routes/(dashboard)/ti/monitoramento/+page.svelte
@@ -6,19 +6,45 @@
-
-
-
+
Monitoramento SGSE
- Sistema de monitoramento técnico em tempo real
+
+ Sistema de monitoramento técnico em tempo real
+
-
-
+
+
Voltar
@@ -27,4 +53,3 @@
-
diff --git a/apps/web/src/routes/(dashboard)/ti/notificacoes/+page.svelte b/apps/web/src/routes/(dashboard)/ti/notificacoes/+page.svelte
index e2ffd97..7105b46 100644
--- a/apps/web/src/routes/(dashboard)/ti/notificacoes/+page.svelte
+++ b/apps/web/src/routes/(dashboard)/ti/notificacoes/+page.svelte
@@ -53,13 +53,14 @@
let emailIdsRastreados = $state >(new Set());
// Query para buscar status dos emails
- const emailIdsArray = $derived(
- Array.from(emailIdsRastreados).map((id) => id as Id<"notificacoesEmail">),
- );
- const emailsStatusQuery = useQuery(
- api.email.buscarEmailsPorIds,
- emailIdsArray.length > 0 ? { emailIds: emailIdsArray } : undefined,
- );
+ const emailsStatusQuery = useQuery(api.email.buscarEmailsPorIds, () => {
+ const ids = Array.from(emailIdsRastreados).map(
+ (id) => id as Id<"notificacoesEmail">,
+ );
+ return ids.length > 0
+ ? { emailIds: ids }
+ : { emailIds: [] as Id<"notificacoesEmail">[] };
+ });
// Queries para agendamentos
const agendamentosEmailQuery = useQuery(
@@ -727,12 +728,12 @@
"enviando",
"Criando/buscando conversa...",
);
- const conversaResult = await client.mutation(
+ const conversaId = await client.mutation(
api.chat.criarOuBuscarConversaIndividual,
{ outroUsuarioId: destinatario._id as Id<"usuarios"> },
);
- if (conversaResult.conversaId) {
+ if (conversaId) {
const mensagem = usarTemplate
? templateSelecionado?.corpo || ""
: mensagemPersonalizada;
@@ -748,7 +749,7 @@
resultadoChat = await client.mutation(
api.chat.agendarMensagem,
{
- conversaId: conversaResult.conversaId,
+ conversaId: conversaId,
conteudo: mensagem,
agendadaPara: agendadaPara,
},
@@ -773,7 +774,7 @@
"Enviando mensagem...",
);
resultadoChat = await client.mutation(api.chat.enviarMensagem, {
- conversaId: conversaResult.conversaId,
+ conversaId: conversaId,
conteudo: mensagem,
tipo: "texto",
permitirNotificacaoParaSiMesmo: true,
@@ -813,7 +814,7 @@
);
if (usarTemplate && templateId) {
const template = templateSelecionado;
- if (template) {
+ if (template && authStore.usuario) {
resultadoEmail = await client.mutation(
api.email.enviarEmailComTemplate,
{
@@ -822,7 +823,7 @@
templateCodigo: template.codigo,
variaveis: {
nome: destinatario.nome,
- matricula: destinatario.matricula,
+ matricula: destinatario.matricula || "",
},
enviadoPorId: authStore.usuario._id as Id<"usuarios">,
agendadaPara: agendadaPara,
@@ -868,6 +869,15 @@
);
}
} else {
+ if (!authStore.usuario) {
+ adicionarLog(
+ "email",
+ destinatario.nome,
+ "erro",
+ "Usuário não autenticado",
+ );
+ return;
+ }
resultadoEmail = await client.mutation(
api.email.enfileirarEmail,
{
@@ -981,12 +991,12 @@
"enviando",
"Processando...",
);
- const conversaResult = await client.mutation(
+ const conversaId = await client.mutation(
api.chat.criarOuBuscarConversaIndividual,
{ outroUsuarioId: destinatario._id as Id<"usuarios"> },
);
- if (conversaResult.conversaId) {
+ if (conversaId) {
// Para templates, usar corpo direto (o backend já faz substituição via email)
// Para mensagem personalizada, usar diretamente
const mensagem = usarTemplate
@@ -995,7 +1005,7 @@
if (agendadaPara) {
await client.mutation(api.chat.agendarMensagem, {
- conversaId: conversaResult.conversaId,
+ conversaId: conversaId,
conteudo: mensagem,
agendadaPara: agendadaPara,
});
@@ -1012,7 +1022,7 @@
);
} else {
await client.mutation(api.chat.enviarMensagem, {
- conversaId: conversaResult.conversaId,
+ conversaId: conversaId,
conteudo: mensagem,
tipo: "texto",
permitirNotificacaoParaSiMesmo: true,
@@ -1063,7 +1073,7 @@
);
if (usarTemplate && templateId) {
const template = templateSelecionado;
- if (template) {
+ if (template && authStore.usuario) {
const resultadoEmail = await client.mutation(
api.email.enviarEmailComTemplate,
{
@@ -1121,6 +1131,16 @@
falhasEmail++;
}
} else {
+ if (!authStore.usuario) {
+ adicionarLog(
+ "email",
+ destinatario.nome,
+ "erro",
+ "Usuário não autenticado",
+ );
+ falhasEmail++;
+ continue;
+ }
const resultadoEmail = await client.mutation(
api.email.enfileirarEmail,
{
@@ -1376,12 +1396,12 @@
{/if}
{#if enviarParaTodos}
-
@@ -1623,9 +1643,9 @@
-
@@ -2241,6 +2266,16 @@
-
+ {
+ if (e.key === "Enter" || e.key === " ") {
+ fecharModalNovoTemplate();
+ }
+ }}
+ >
{/if}
diff --git a/apps/web/src/routes/(dashboard)/ti/painel-administrativo/+page.svelte b/apps/web/src/routes/(dashboard)/ti/painel-administrativo/+page.svelte
index 0349c24..aad2e49 100644
--- a/apps/web/src/routes/(dashboard)/ti/painel-administrativo/+page.svelte
+++ b/apps/web/src/routes/(dashboard)/ti/painel-administrativo/+page.svelte
@@ -2,40 +2,51 @@
import { useQuery, useConvexClient } from "convex-svelte";
import { api } from "@sgse-app/backend/convex/_generated/api";
import StatsCard from "$lib/components/ti/StatsCard.svelte";
+ import {
+ Users,
+ CheckCircle,
+ Ban,
+ Clock,
+ BarChart3,
+ Plus,
+ FolderTree,
+ FileText,
+ Info,
+ } from "lucide-svelte";
const client = useConvexClient();
const usuariosQuery = useQuery(api.usuarios.listar, {});
-
+
// Verificar se está carregando
const carregando = $derived(usuariosQuery === undefined);
-
+
// Extrair dados dos usuários
const usuarios = $derived(usuariosQuery?.data ?? []);
-
+
// Estatísticas derivadas
const stats = $derived.by(() => {
// Se ainda está carregando, retorna null para mostrar loading
if (carregando) return null;
-
+
// Se não há usuários, retorna stats zeradas (mas não null para não mostrar loading)
if (!Array.isArray(usuarios) || usuarios.length === 0) {
return {
total: 0,
ativos: 0,
bloqueados: 0,
- inativos: 0
+ inativos: 0,
};
}
-
- const ativos = usuarios.filter(u => u.ativo && !u.bloqueado).length;
- const bloqueados = usuarios.filter(u => u.bloqueado === true).length;
- const inativos = usuarios.filter(u => !u.ativo).length;
-
+
+ const ativos = usuarios.filter((u) => u.ativo && !u.bloqueado).length;
+ const bloqueados = usuarios.filter((u) => u.bloqueado === true).length;
+ const inativos = usuarios.filter((u) => !u.ativo).length;
+
return {
total: usuarios.length,
ativos,
bloqueados,
- inativos
+ inativos,
};
});
@@ -45,13 +56,15 @@
- Dashboard Administrativo TI
- Painel de controle e monitoramento do sistema
+
+ Dashboard Administrativo TI
+
+
+ Painel de controle e monitoramento do sistema
+
@@ -59,34 +72,38 @@
{#if stats}
-
+
-
-
+ 0 ? ((stats.ativos / stats.total) * 100).toFixed(1) + "% do total" : "0% do total"}`}
+ icon={CheckCircle}
color="success"
/>
-
-
+
-
-
+
@@ -102,23 +119,17 @@
Ações Rápidas
@@ -127,9 +138,10 @@
-
-
-
- Sistema de Gestão da Secretaria de Esportes - Versão 2.0 com controle avançado de acesso
+
+ Sistema de Gestão da Secretaria de Esportes - Versão 2.0 com controle
+ avançado de acesso
diff --git a/apps/web/src/routes/(dashboard)/ti/perfis/+page.svelte b/apps/web/src/routes/(dashboard)/ti/perfis/+page.svelte
index e61b8e5..9e89883 100644
--- a/apps/web/src/routes/(dashboard)/ti/perfis/+page.svelte
+++ b/apps/web/src/routes/(dashboard)/ti/perfis/+page.svelte
@@ -6,6 +6,21 @@
import type { Id } from "@sgse-app/backend/convex/_generated/dataModel";
import { format } from "date-fns";
import { ptBR } from "date-fns/locale";
+ import {
+ Users,
+ Shield,
+ ShieldAlert,
+ Info,
+ Building2,
+ Search,
+ X,
+ Eye,
+ Calendar,
+ Tag,
+ Settings,
+ User,
+ AlertCircle,
+ } from "lucide-svelte";
type Role = {
_id: Id<"roles">;
@@ -38,12 +53,12 @@
// Estatísticas
const stats = $derived.by(() => {
if (carregando) return null;
-
+
const porNivel = {
- 0: roles.filter(r => r.nivel === 0).length,
- 1: roles.filter(r => r.nivel === 1).length,
- 2: roles.filter(r => r.nivel === 2).length,
- 3: roles.filter(r => r.nivel >= 3).length,
+ 0: roles.filter((r) => r.nivel === 0).length,
+ 1: roles.filter((r) => r.nivel === 1).length,
+ 2: roles.filter((r) => r.nivel === 2).length,
+ 3: roles.filter((r) => r.nivel >= 3).length,
};
return {
@@ -52,7 +67,7 @@
nivelAlto: porNivel[1],
nivelMedio: porNivel[2],
nivelBaixo: porNivel[3],
- comSetor: roles.filter(r => r.setor).length,
+ comSetor: roles.filter((r) => r.setor).length,
};
});
@@ -65,7 +80,7 @@
resultado = resultado.filter(
(r) =>
r.nome.toLowerCase().includes(buscaLower) ||
- r.descricao.toLowerCase().includes(buscaLower)
+ r.descricao.toLowerCase().includes(buscaLower),
);
}
@@ -137,33 +152,27 @@
filtroNivel = "";
}
- const temFiltrosAtivos = $derived(busca.trim() !== "" || filtroSetor !== "" || filtroNivel !== "");
+ const temFiltrosAtivos = $derived(
+ busca.trim() !== "" || filtroSetor !== "" || filtroNivel !== "",
+ );
-
+
Gestão de Perfis
- Visualize e gerencie os perfis de acesso do sistema
+
+ Visualize e gerencie os perfis de acesso do sistema
+
@@ -171,38 +180,45 @@
{#if stats}
-
+
-
+
-
+
-
+
-
+ 0
+ ? ((stats.comSetor / stats.total) * 100).toFixed(0) + "% do total"
+ : "0%"}
+ icon={Building2}
color="secondary"
/>
@@ -214,38 +230,16 @@
{#if temFiltrosAtivos}
-
@@ -287,7 +270,11 @@
- |