|
@@ -405,7 +464,12 @@
|
-
+
+
+
+
+
+ (showAlertModal = false)}
+/>
+
+ {
+ if (confirmCallback) {
+ confirmCallback();
+ confirmCallback = null;
+ }
+ showConfirmModal = false;
+ }}
+ onCancel={() => {
+ confirmCallback = null;
+ showConfirmModal = false;
+ }}
+/>
diff --git a/apps/web/src/lib/components/ti/AlertDiagnosticsCard.svelte b/apps/web/src/lib/components/ti/AlertDiagnosticsCard.svelte
new file mode 100644
index 0000000..ba76d57
--- /dev/null
+++ b/apps/web/src/lib/components/ti/AlertDiagnosticsCard.svelte
@@ -0,0 +1,204 @@
+
+
+
+
+
+
+
+ Diagnóstico de Configuração de Alertas
+
+
+
+ Atualizar
+
+
+
+ {#if configQuery === undefined}
+
+
+
+ {:else if configQuery === null}
+
+
+ Erro ao carregar diagnóstico
+
+ {:else}
+ {@const config = configQuery}
+
+
+ Template de Email
+
+ {#if config.templateExiste}
+
+
+ Template encontrado
+ {#if config.templateInfo}
+
+ {config.templateInfo.nome} ({config.templateInfo.codigo})
+
+ {#if !config.templateInfo.htmlCorpo}
+
+ ⚠️ Template não possui HTML personalizado
+
+ {/if}
+ {/if}
+
+ {:else}
+
+
+ Template não encontrado
+
+ O template "monitoramento_alerta_sistema" não foi encontrado no banco de dados.
+
+
+ 💡 Execute a mutation "criarTemplatesPadrao" para criar os templates do sistema.
+
+
+ {/if}
+
+
+
+ Perfil TI_MASTER
+
+ {#if config.roleTiMasterExiste}
+
+
+ Perfil TI_MASTER encontrado
+
+ {config.usuariosTiMaster.length} usuário(s) com este perfil
+
+
+ {:else}
+
+
+ Perfil TI_MASTER não encontrado
+
+ A role "ti_master" não existe no banco de dados.
+
+
+ 💡 Execute o seed do banco de dados para criar as roles padrão.
+
+
+ {/if}
+
+
+
+ {#if config.usuariosTiMaster.length > 0}
+
+ {#each config.usuariosTiMaster as usuario}
+
+ {#if usuario.temEmail}
+
+ {:else}
+
+ {/if}
+
+ {usuario.nome}
+ {#if usuario.email}
+ ({usuario.email})
+ {:else}
+ - Sem email cadastrado
+ {/if}
+
+
+ {/each}
+
+ {:else if config.roleTiMasterExiste}
+
+
+
+ Nenhum usuário com perfil TI_MASTER encontrado
+
+
+ {/if}
+
+
+ Configuração SMTP
+
+ {#if config.configSmtpAtiva}
+
+
+ Configuração SMTP ativa
+ {#if config.configSmtpInfo}
+
+ Servidor: {config.configSmtpInfo.servidor}:{config.configSmtpInfo.porta}
+ Remetente: {config.configSmtpInfo.emailRemetente}
+
+ {/if}
+
+ {:else}
+
+
+ Configuração SMTP não encontrada ou inativa
+
+ Nenhuma configuração SMTP ativa foi encontrada no banco de dados.
+
+
+ 💡 Configure o SMTP em: Configurações de Email
+
+
+ {/if}
+
+
+
+ Estatísticas
+
+
+
+
+
+ Alertas Ativos
+ {config.alertasAtivos}
+ com notificação por email: {config.alertasComEmail}
+
+
+
+
+
+
+ Emails Pendentes
+ {config.emailsPendentes}
+ em falha: {config.emailsFalha}
+
+
+
+
+ Resumo
+
+ {#if config.templateExiste && config.roleTiMasterExiste && config.usuariosTiMaster.some(u => u.temEmail) && config.configSmtpAtiva}
+
+
+ ✅ Sistema configurado corretamente
+
+ Todos os componentes necessários estão configurados. Os alertas devem funcionar corretamente.
+
+
+ {:else}
+
+
+ ⚠️ Configuração incompleta
+
+ Alguns componentes necessários não estão configurados. Verifique os itens acima.
+
+
+ {/if}
+
+ {/if}
+
+
+
diff --git a/apps/web/src/lib/components/ti/SystemMonitorCardLocal.svelte b/apps/web/src/lib/components/ti/SystemMonitorCardLocal.svelte
index 021bf9c..e9556fa 100644
--- a/apps/web/src/lib/components/ti/SystemMonitorCardLocal.svelte
+++ b/apps/web/src/lib/components/ti/SystemMonitorCardLocal.svelte
@@ -346,7 +346,10 @@
try {
type UsuariosList = FunctionReturnType;
const usuarios = (await client.query(api.chat.listarTodosUsuarios, {})) as UsuariosList;
- return usuarios.filter((u) => u.statusPresenca === 'online').length;
+ if (!usuarios || !Array.isArray(usuarios)) {
+ return 0;
+ }
+ return usuarios.filter((u) => u?.statusPresenca === 'online').length;
} catch (e) {
console.error('Erro ao obter usuários:', e);
return 0;
@@ -553,31 +556,37 @@
// Coleta todas as métricas
async function collectAllMetrics() {
- const [
- cpuUsage,
- memoryUsage,
- networkLatency,
- storageUsed,
- usuariosOnline,
- tempoRespostaMedio,
- batteryInfo,
- indexedDBSize,
- deviceInfo
- ] = await Promise.all([
- estimateCPU(),
- Promise.resolve(getMemoryUsage()),
- measureLatency(),
- getStorageUsage(),
- getUsuariosOnline(),
- getResponseTime(),
- getBatteryInfo(),
- getIndexedDBSize(),
- obterInformacoesDispositivo().catch(() => ({}) as Record) // Capturar erro se falhar
- ]);
+ try {
+ const results = await Promise.allSettled([
+ estimateCPU(),
+ Promise.resolve(getMemoryUsage()),
+ measureLatency(),
+ getStorageUsage(),
+ getUsuariosOnline(),
+ getResponseTime(),
+ getBatteryInfo(),
+ getIndexedDBSize(),
+ obterInformacoesDispositivo().catch(() => ({}) as Record)
+ ]);
- const browserInfo = getBrowserInfo();
- const networkInfo = getNetworkInfo();
- const wsInfo = getWebSocketStatus();
+ const cpuUsage = results[0].status === 'fulfilled' ? results[0].value : 0;
+ const memoryUsage = results[1].status === 'fulfilled' ? results[1].value : 0;
+ const networkLatency = results[2].status === 'fulfilled' ? results[2].value : 0;
+ const storageUsed = results[3].status === 'fulfilled' ? results[3].value : 0;
+ const usuariosOnline = results[4].status === 'fulfilled' ? results[4].value : 0;
+ const tempoRespostaMedio = results[5].status === 'fulfilled' ? results[5].value : 0;
+ const batteryInfoResult = results[6].status === 'fulfilled'
+ ? results[6].value
+ : { level: 100, charging: false };
+ const batteryInfo = typeof batteryInfoResult === 'object' && batteryInfoResult !== null && 'level' in batteryInfoResult
+ ? batteryInfoResult as { level: number; charging: boolean }
+ : { level: 100, charging: false };
+ const indexedDBSize = results[7].status === 'fulfilled' ? results[7].value : 0;
+ const deviceInfo = results[8].status === 'fulfilled' ? results[8].value : ({} as Record);
+
+ const browserInfo = getBrowserInfo();
+ const networkInfo = getNetworkInfo();
+ const wsInfo = getWebSocketStatus();
const newMetrics: Metrics = {
timestamp: Date.now(),
@@ -634,11 +643,15 @@
// Adicionar ao histórico
metricsHistory = [...metricsHistory, newMetrics].slice(-100);
- // Salvar no localStorage para persistência
- try {
- localStorage.setItem('sgse_metrics_history', JSON.stringify(metricsHistory));
- } catch (e) {
- console.warn('Não foi possível salvar histórico:', e);
+ // Salvar no localStorage para persistência
+ try {
+ localStorage.setItem('sgse_metrics_history', JSON.stringify(metricsHistory));
+ } catch (e) {
+ console.warn('Não foi possível salvar histórico:', e);
+ }
+ } catch (error) {
+ console.error('Erro ao coletar métricas:', error);
+ // Continuar com métricas anteriores em caso de erro
}
}
@@ -661,10 +674,16 @@
}
// Coletar imediatamente
- collectAllMetrics();
+ collectAllMetrics().catch((error) => {
+ console.error('Erro na coleta inicial de métricas:', error);
+ });
// Configurar intervalo de 2 segundos
- intervalId = setInterval(collectAllMetrics, 2000);
+ intervalId = setInterval(() => {
+ collectAllMetrics().catch((error) => {
+ console.error('Erro na coleta periódica de métricas:', error);
+ });
+ }, 2000);
});
// Parar coleta ao desmontar
diff --git a/apps/web/src/lib/utils/fichaPontoPDF.ts b/apps/web/src/lib/utils/fichaPontoPDF.ts
index b72e8f1..4228a34 100644
--- a/apps/web/src/lib/utils/fichaPontoPDF.ts
+++ b/apps/web/src/lib/utils/fichaPontoPDF.ts
@@ -440,3 +440,6 @@ export function adicionarRodape(doc: jsPDF): void {
+
+
+
diff --git a/apps/web/src/routes/(dashboard)/+error.svelte b/apps/web/src/routes/(dashboard)/+error.svelte
index 7271340..dd5d59b 100644
--- a/apps/web/src/routes/(dashboard)/+error.svelte
+++ b/apps/web/src/routes/(dashboard)/+error.svelte
@@ -79,3 +79,6 @@
+
+
+
diff --git a/apps/web/src/routes/(dashboard)/fluxos/[id]/editor/+page.svelte b/apps/web/src/routes/(dashboard)/fluxos/[id]/editor/+page.svelte
index 525be97..054cdcf 100644
--- a/apps/web/src/routes/(dashboard)/fluxos/[id]/editor/+page.svelte
+++ b/apps/web/src/routes/(dashboard)/fluxos/[id]/editor/+page.svelte
@@ -9,11 +9,28 @@
const client = useConvexClient();
const templateId = $derived($page.params.id as Id<'flowTemplates'>);
- // Queries
- const templateQuery = useQuery(api.flows.getTemplate, () => ({ id: templateId }));
- const stepsQuery = useQuery(api.flows.listStepsByTemplate, () => ({
- flowTemplateId: templateId
- }));
+ // Função para validar se o ID é válido
+ function isValidConvexId(id: string | undefined): id is Id<'flowTemplates'> {
+ if (!id || id === '' || id.trim() === '') return false;
+ // Convex IDs têm formato específico (geralmente começam com letras e contêm apenas alfanuméricos)
+ // IDs válidos do Convex têm pelo menos alguns caracteres e não são strings vazias
+ return /^[a-z0-9]+$/i.test(id.trim()) && id.trim().length > 0;
+ }
+
+ // Queries - garantir que nunca seja chamado com ID vazio
+ const templateQuery = useQuery(api.flows.getTemplate, () => {
+ const id = templateId;
+ if (!id || !isValidConvexId(id)) {
+ return 'skip';
+ }
+ return { id: id as Id<'flowTemplates'> };
+ });
+ const stepsQuery = useQuery(api.flows.listStepsByTemplate, () => {
+ if (!isValidConvexId(templateId)) {
+ return 'skip';
+ }
+ return { flowTemplateId: templateId };
+ });
const setoresQuery = useQuery(api.setores.list, {});
// Query de sub-etapas (reativa baseada no step selecionado)
diff --git a/apps/web/src/routes/(dashboard)/recursos-humanos/atestados-licencas/+page.svelte b/apps/web/src/routes/(dashboard)/recursos-humanos/atestados-licencas/+page.svelte
index 8b250e3..ab4b25a 100644
--- a/apps/web/src/routes/(dashboard)/recursos-humanos/atestados-licencas/+page.svelte
+++ b/apps/web/src/routes/(dashboard)/recursos-humanos/atestados-licencas/+page.svelte
@@ -124,6 +124,14 @@
detalhes: ''
});
+ // Modal de documento
+ let documentoModal = $state({
+ aberto: false,
+ url: null as string | null,
+ loading: false,
+ titulo: ''
+ });
+
// Licenças maternidade para prorrogação (derivar dos dados já carregados)
const licencasMaternidade = $derived.by(() => {
const dados = dadosQuery?.data;
@@ -194,6 +202,58 @@
};
}
+ // Função para abrir modal de documento
+ async function abrirModalDocumento(storageId: Id<'_storage'>, titulo: string) {
+ try {
+ documentoModal = {
+ aberto: true,
+ url: null,
+ loading: true,
+ titulo
+ };
+
+ const url = await client.query(api.atestadosLicencas.obterUrlDocumento, {
+ storageId
+ });
+
+ if (url) {
+ documentoModal = {
+ aberto: true,
+ url,
+ loading: false,
+ titulo
+ };
+ } else {
+ documentoModal.aberto = false;
+ mostrarErro(
+ 'Erro ao visualizar documento',
+ 'Não foi possível obter a URL do documento.',
+ 'O documento pode ter sido removido ou não existe mais.'
+ );
+ }
+ } catch (err: any) {
+ console.error('Erro ao obter URL do documento:', err);
+ documentoModal.aberto = false;
+ mostrarErro(
+ 'Erro ao visualizar documento',
+ 'Não foi possível abrir o documento.',
+ err?.message || err?.toString() || 'Erro desconhecido'
+ );
+ }
+ }
+
+ // Função para baixar documento
+ function baixarDocumento(url: string, nomeArquivo: string) {
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = nomeArquivo;
+ link.target = '_blank';
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ toast.success('Download iniciado!');
+ }
+
// Dados para gráfico de área - Total de Dias por Tipo (Layerchart)
const chartDataTotalDiasPorTipo = $derived.by(() => {
if (!graficosQuery?.data?.totalDiasPorTipo) {
@@ -1548,32 +1608,11 @@
{#if atestado.documentoId}
{
- try {
- const url = await client.query(
- api.atestadosLicencas.obterUrlDocumento,
- {
- storageId: atestado.documentoId as any
- }
- );
- if (url) {
- window.open(url, '_blank');
- } else {
- mostrarErro(
- 'Erro ao visualizar documento',
- 'Não foi possível obter a URL do documento.',
- 'O documento pode ter sido removido ou não existe mais.'
- );
- }
- } catch (err: any) {
- console.error('Erro ao obter URL do documento:', err);
- mostrarErro(
- 'Erro ao visualizar documento',
- 'Não foi possível abrir o documento.',
- err?.message || err?.toString() || 'Erro desconhecido'
- );
- }
- }}
+ onclick={() =>
+ abrirModalDocumento(
+ atestado.documentoId as Id<'_storage'>,
+ `Documento - ${atestado.funcionario?.nome || 'Atestado'}`
+ )}
>
Ver Doc
@@ -1630,32 +1669,11 @@
{#if licenca.documentoId}
{
- try {
- const url = await client.query(
- api.atestadosLicencas.obterUrlDocumento,
- {
- storageId: licenca.documentoId as any
- }
- );
- if (url) {
- window.open(url, '_blank');
- } else {
- mostrarErro(
- 'Erro ao visualizar documento',
- 'Não foi possível obter a URL do documento.',
- 'O documento pode ter sido removido ou não existe mais.'
- );
- }
- } catch (err: any) {
- console.error('Erro ao obter URL do documento:', err);
- mostrarErro(
- 'Erro ao visualizar documento',
- 'Não foi possível abrir o documento.',
- err?.message || err?.toString() || 'Erro desconhecido'
- );
- }
- }}
+ onclick={() =>
+ abrirModalDocumento(
+ licenca.documentoId as Id<'_storage'>,
+ `Documento - ${licenca.funcionario?.nome || 'Licença'}`
+ )}
>
Ver Doc
@@ -2344,3 +2362,95 @@
erroModal.aberto = false;
}}
/>
+
+
+{#if documentoModal.aberto}
+
+
+ (documentoModal.aberto = false)}
+ role="dialog"
+ aria-modal="true"
+ >
+
+
+
+{/if}
diff --git a/apps/web/src/routes/(dashboard)/secretaria-executiva/gestao-ausencias/+page.svelte b/apps/web/src/routes/(dashboard)/secretaria-executiva/gestao-ausencias/+page.svelte
index 70a6c55..19e70b7 100644
--- a/apps/web/src/routes/(dashboard)/secretaria-executiva/gestao-ausencias/+page.svelte
+++ b/apps/web/src/routes/(dashboard)/secretaria-executiva/gestao-ausencias/+page.svelte
@@ -14,7 +14,6 @@
import ExcelJS from 'exceljs';
import logoGovPE from '$lib/assets/logo_governo_PE.png';
import { toast } from 'svelte-sonner';
- import UserAvatar from '$lib/components/chat/UserAvatar.svelte';
const client = useConvexClient();
const currentUser = useQuery(api.auth.getCurrentUser, {});
@@ -673,17 +672,8 @@
{#each ausenciasFiltradas as ausencia}
- |
-
-
-
- {ausencia.funcionario?.nome || 'N/A'}
-
-
+ |
+ {ausencia.funcionario?.nome || 'N/A'}
|
{#if ausencia.time}
diff --git a/apps/web/src/routes/(dashboard)/ti/erros-servidor/+page.svelte b/apps/web/src/routes/(dashboard)/ti/erros-servidor/+page.svelte
index 8eef610..614bf8d 100644
--- a/apps/web/src/routes/(dashboard)/ti/erros-servidor/+page.svelte
+++ b/apps/web/src/routes/(dashboard)/ti/erros-servidor/+page.svelte
@@ -26,8 +26,8 @@
let copiado = $state(false);
// Filtros
- let filtroStatusCode = $state(undefined);
- let filtroNotificado = $state(undefined);
+ let filtroStatusCode = $state('');
+ let filtroNotificado = $state('');
let filtroDataInicio = $state('');
let filtroDataFim = $state('');
@@ -36,8 +36,8 @@
// Argumentos para as queries (usando $derived para reatividade)
const argsErros = $derived({
limite,
- statusCode: filtroStatusCode,
- notificado: filtroNotificado,
+ statusCode: filtroStatusCode ? Number(filtroStatusCode) : undefined,
+ notificado: filtroNotificado === '' ? undefined : filtroNotificado === 'true',
dataInicio: filtroDataInicio ? new Date(filtroDataInicio).getTime() : undefined,
dataFim: filtroDataFim ? new Date(filtroDataFim + 'T23:59:59').getTime() : undefined
});
@@ -132,8 +132,8 @@
}
function limparFiltros() {
- filtroStatusCode = undefined;
- filtroNotificado = undefined;
+ filtroStatusCode = '';
+ filtroNotificado = '';
filtroDataInicio = '';
filtroDataFim = '';
}
@@ -248,33 +248,20 @@
- | |