diff --git a/apps/web/src/lib/components/ausencias/CalendarioAusencias.svelte b/apps/web/src/lib/components/ausencias/CalendarioAusencias.svelte index 986e577..4cb971a 100644 --- a/apps/web/src/lib/components/ausencias/CalendarioAusencias.svelte +++ b/apps/web/src/lib/components/ausencias/CalendarioAusencias.svelte @@ -5,6 +5,7 @@ import interactionPlugin from "@fullcalendar/interaction"; import multiMonthPlugin from "@fullcalendar/multimonth"; import ptBrLocale from "@fullcalendar/core/locales/pt-br"; + import { SvelteDate } from "svelte/reactivity"; interface Props { dataInicio?: string; @@ -34,18 +35,6 @@ let calendarEl: HTMLDivElement; let calendar: Calendar | null = null; let selecionando = $state(false); // Flag para evitar atualizações durante seleção - let eventos: Array<{ - id: string; - title: string; - start: string; - end: string; - backgroundColor: string; - borderColor: string; - textColor: string; - extendedProps: { - status: string; - }; - }> = $state([]); // Cores por status const coresStatus: Record< @@ -58,7 +47,7 @@ }; // Converter ausências existentes em eventos - function atualizarEventos() { + let eventos = $derived.by(() => { const novosEventos: Array<{ id: string; title: string; @@ -103,8 +92,8 @@ }); } - eventos = novosEventos; - } + return novosEventos; + }); function getStatusTexto(status: string): string { const textos: Record = { @@ -117,15 +106,15 @@ // Helper: Adicionar 1 dia à data fim (FullCalendar usa exclusive end) function calcularDataFim(dataFim: string): string { - const data = new Date(dataFim); + const data = new SvelteDate(dataFim); data.setDate(data.getDate() + 1); return data.toISOString().split("T")[0]; } // Helper: Calcular dias entre datas (inclusivo) function calcularDias(inicio: string, fim: string): number { - const dInicio = new Date(inicio); - const dFim = new Date(fim); + const dInicio = new SvelteDate(inicio); + const dFim = new SvelteDate(fim); const diffTime = Math.abs(dFim.getTime() - dInicio.getTime()); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1; return diffDays; @@ -133,20 +122,23 @@ // Helper: Verificar se há sobreposição de datas function verificarSobreposicao( - inicio1: Date, - fim1: Date, + inicio1: SvelteDate, + fim1: SvelteDate, inicio2: string, fim2: string, ): boolean { - const d2Inicio = new Date(inicio2); - const d2Fim = new Date(fim2); + const d2Inicio = new SvelteDate(inicio2); + const d2Fim = new SvelteDate(fim2); // Verificar sobreposição: início1 <= fim2 && início2 <= fim1 return inicio1 <= d2Fim && d2Inicio <= fim1; } // Helper: Verificar se período selecionado sobrepõe com ausências existentes - function verificarSobreposicaoComAusencias(inicio: Date, fim: Date): boolean { + function verificarSobreposicaoComAusencias( + inicio: SvelteDate, + fim: SvelteDate, + ): boolean { if (!ausenciasExistentes || ausenciasExistentes.length === 0) return false; // Verificar apenas ausências aprovadas ou aguardando aprovação @@ -159,12 +151,17 @@ ); } + interface FullCalendarDayCellInfo { + el: HTMLElement; + date: Date; + } + // Helper: Atualizar classe de seleção em uma célula - function atualizarClasseSelecionado(info: any) { + function atualizarClasseSelecionado(info: FullCalendarDayCellInfo) { if (dataInicio && dataFim && !readonly) { - const cellDate = new Date(info.date); - const inicio = new Date(dataInicio); - const fim = new Date(dataFim); + const cellDate = new SvelteDate(info.date); + const inicio = new SvelteDate(dataInicio); + const fim = new SvelteDate(dataFim); cellDate.setHours(0, 0, 0, 0); inicio.setHours(0, 0, 0, 0); @@ -181,13 +178,13 @@ } // Helper: Atualizar classe de bloqueio para dias com ausências existentes - function atualizarClasseBloqueado(info: any) { + function atualizarClasseBloqueado(info: FullCalendarDayCellInfo) { if (readonly || !ausenciasExistentes || ausenciasExistentes.length === 0) { info.el.classList.remove("fc-day-blocked"); return; } - const cellDate = new Date(info.date); + const cellDate = new SvelteDate(info.date); cellDate.setHours(0, 0, 0, 0); // Verificar se a data está dentro de alguma ausência aprovada ou aguardando aprovação @@ -196,8 +193,8 @@ (a) => a.status === "aprovado" || a.status === "aguardando_aprovacao", ) .some((ausencia) => { - const inicio = new Date(ausencia.dataInicio); - const fim = new Date(ausencia.dataFim); + const inicio = new SvelteDate(ausencia.dataInicio); + const fim = new SvelteDate(ausencia.dataFim); inicio.setHours(0, 0, 0, 0); fim.setHours(0, 0, 0, 0); return cellDate >= inicio && cellDate <= fim; @@ -218,8 +215,8 @@ const view = calendar.view; if (!view) return; - const inicio = new Date(dataInicio); - const fim = new Date(dataFim); + const inicio = new SvelteDate(dataInicio); + const fim = new SvelteDate(dataFim); inicio.setHours(0, 0, 0, 0); fim.setHours(0, 0, 0, 0); @@ -235,14 +232,14 @@ if (ariaLabel) { // Formato: "dia mês ano" ou similar try { - const cellDate = new Date(ariaLabel); + const cellDate = new SvelteDate(ariaLabel); if (!isNaN(cellDate.getTime())) { cellDate.setHours(0, 0, 0, 0); if (cellDate >= inicio && cellDate <= fim) { cell.classList.add("fc-day-selected"); } } - } catch (e) { + } catch { // Ignorar erros de parsing } } @@ -266,6 +263,7 @@ return; } + const calendarInstance = calendar; const cells = calendarEl.querySelectorAll(".fc-daygrid-day"); const ausenciasBloqueantes = ausenciasExistentes.filter( (a) => a.status === "aprovado" || a.status === "aguardando_aprovacao", @@ -280,17 +278,17 @@ cell.classList.remove("fc-day-blocked"); // Tentar obter a data de diferentes formas - let cellDate: Date | null = null; + let cellDate: SvelteDate | null = null; // Método 1: aria-label const ariaLabel = cell.getAttribute("aria-label"); if (ariaLabel) { try { - const parsed = new Date(ariaLabel); + const parsed = new SvelteDate(ariaLabel); if (!isNaN(parsed.getTime())) { cellDate = parsed; } - } catch (e) { + } catch { // Ignorar } } @@ -300,27 +298,27 @@ const dataDate = cell.getAttribute("data-date"); if (dataDate) { try { - const parsed = new Date(dataDate); + const parsed = new SvelteDate(dataDate); if (!isNaN(parsed.getTime())) { cellDate = parsed; } - } catch (e) { + } catch { // Ignorar } } } // Método 3: Tentar obter do número do dia e contexto do calendário - if (!cellDate && calendar.view) { + if (!cellDate && calendarInstance.view) { const dayNumberEl = cell.querySelector(".fc-daygrid-day-number"); if (dayNumberEl) { const dayNumber = parseInt(dayNumberEl.textContent || "0"); if (dayNumber > 0 && dayNumber <= 31) { // Usar a data da view atual e o número do dia - const viewStart = new Date(calendar.view.activeStart); + const viewStart = new SvelteDate(calendarInstance.view.activeStart); const cellIndex = Array.from(cells).indexOf(cell); if (cellIndex >= 0) { - const possibleDate = new Date(viewStart); + const possibleDate = new SvelteDate(viewStart); possibleDate.setDate(viewStart.getDate() + cellIndex); // Verificar se o número do dia corresponde if (possibleDate.getDate() === dayNumber) { @@ -335,11 +333,11 @@ cellDate.setHours(0, 0, 0, 0); const estaBloqueado = ausenciasBloqueantes.some((ausencia) => { - const inicio = new Date(ausencia.dataInicio); - const fim = new Date(ausencia.dataFim); + const inicio = new SvelteDate(ausencia.dataInicio); + const fim = new SvelteDate(ausencia.dataFim); inicio.setHours(0, 0, 0, 0); fim.setHours(0, 0, 0, 0); - return cellDate >= inicio && cellDate <= fim; + return cellDate! >= inicio && cellDate! <= fim; }); if (estaBloqueado) { @@ -354,9 +352,7 @@ if (!calendar || selecionando) return; // Não atualizar durante seleção // Garantir que temos as ausências antes de atualizar - const ausencias = ausenciasExistentes; - - atualizarEventos(); + void ausenciasExistentes; // Usar requestAnimationFrame para evitar múltiplas atualizações durante seleção requestAnimationFrame(() => { @@ -398,8 +394,6 @@ onMount(() => { if (!calendarEl) return; - atualizarEventos(); - calendar = new Calendar(calendarEl, { plugins: [dayGridPlugin, interactionPlugin, multiMonthPlugin], initialView: @@ -416,9 +410,9 @@ selectMirror: true, unselectAuto: false, selectOverlap: false, - selectConstraint: null, // Permite seleção entre meses diferentes + selectConstraint: undefined, // Permite seleção entre meses diferentes validRange: { - start: new Date().toISOString().split("T")[0], // Não permite selecionar datas passadas + start: new SvelteDate().toISOString().split("T")[0], // Não permite selecionar datas passadas }, events: eventos, @@ -437,12 +431,12 @@ // Usar setTimeout para evitar conflito com atualizações de estado setTimeout(() => { - const inicio = new Date(info.startStr); - const fim = new Date(info.endStr); + const inicio = new SvelteDate(info.startStr); + const fim = new SvelteDate(info.endStr); fim.setDate(fim.getDate() - 1); // FullCalendar usa exclusive end // Validar que não é no passado - const hoje = new Date(); + const hoje = new SvelteDate(); hoje.setHours(0, 0, 0, 0); if (inicio < hoje) { alert("A data de início não pode ser no passado"); @@ -511,11 +505,11 @@ // Desabilitar datas passadas e períodos que sobrepõem com ausências existentes selectAllow: (selectInfo) => { - const hoje = new Date(); + const hoje = new SvelteDate(); hoje.setHours(0, 0, 0, 0); // Bloquear datas passadas - if (new Date(selectInfo.start) < hoje) { + if (new SvelteDate(selectInfo.start) < hoje) { return false; } @@ -525,8 +519,8 @@ ausenciasExistentes && ausenciasExistentes.length > 0 ) { - const inicioSelecao = new Date(selectInfo.start); - const fimSelecao = new Date(selectInfo.end); + const inicioSelecao = new SvelteDate(selectInfo.start); + const fimSelecao = new SvelteDate(selectInfo.end); fimSelecao.setDate(fimSelecao.getDate() - 1); // FullCalendar usa exclusive end inicioSelecao.setHours(0, 0, 0, 0); @@ -578,7 +572,7 @@ ausenciasExistentes && ausenciasExistentes.length > 0 ) { - const cellDate = new Date(arg.date); + const cellDate = new SvelteDate(arg.date); cellDate.setHours(0, 0, 0, 0); const ausenciasBloqueantes = ausenciasExistentes.filter( @@ -587,8 +581,8 @@ ); const estaBloqueado = ausenciasBloqueantes.some((ausencia) => { - const inicio = new Date(ausencia.dataInicio); - const fim = new Date(ausencia.dataFim); + const inicio = new SvelteDate(ausencia.dataInicio); + const fim = new SvelteDate(ausencia.dataFim); inicio.setHours(0, 0, 0, 0); fim.setHours(0, 0, 0, 0); return cellDate >= inicio && cellDate <= fim; @@ -646,9 +640,6 @@ {#if ausenciasExistentes && ausenciasExistentes.filter((a) => a.status === "aprovado" || a.status === "aguardando_aprovacao").length > 0} - {@const ausenciasBloqueantes = ausenciasExistentes.filter( - (a) => a.status === "aprovado" || a.status === "aguardando_aprovacao", - )}
{#if dataInicio && dataFim && !readonly}
-

+

; @@ -67,7 +68,7 @@ return; } - const hoje = new Date(); + const hoje = new SvelteDate(); hoje.setHours(0, 0, 0, 0); const inicio = new Date(dataInicio); @@ -266,7 +267,7 @@

Período selecionado!

- De {new Date(dataInicio).toLocaleDateString('pt-BR')} até{' '} + De {new Date(dataInicio).toLocaleDateString('pt-BR')} até {new Date(dataFim).toLocaleDateString('pt-BR')} ({totalDias} dias)

@@ -286,7 +287,7 @@ {#if dataInicio && dataFim}

@@ -345,7 +346,7 @@ bind:value={motivo} maxlength={500} > -

@@ -282,10 +266,10 @@ Solicitações Pendentes

- {formatNumber(statsQuery.data.solicitacoesPendentes)} + 4

- de {statsQuery.data.totalSolicitacoesAcesso} total + de 5 total

@@ -357,12 +341,6 @@

Atividade (24h)

-

- {formatNumber( - activityQuery.data.funcionariosCadastrados24h + - activityQuery.data.solicitacoesAcesso24h, - )} -

{activityQuery.data.funcionariosCadastrados24h} cadastros

@@ -623,7 +601,7 @@
- {#each [10, 8, 6, 4, 2, 0] as val} + {#each [10, 8, 6, 4, 2, 0] as val (val)} {val} {/each}
@@ -631,7 +609,7 @@
- {#each Array.from({ length: 6 }) as _, i} + {#each [0, 1, 2, 3, 4, 5] as i (i)}
- {#each atividade.historico as ponto, idx} + {#each atividade.historico as ponto, idx (idx)}
+ {:else} + +
+ Não foi possível carregar os dados do dashboard. +
{/if} diff --git a/apps/web/src/routes/(dashboard)/ti/+page.svelte b/apps/web/src/routes/(dashboard)/ti/+page.svelte index 4a2935d..e0f92f2 100644 --- a/apps/web/src/routes/(dashboard)/ti/+page.svelte +++ b/apps/web/src/routes/(dashboard)/ti/+page.svelte @@ -11,7 +11,8 @@ | 'monitor' | 'document' | 'teams' - | 'userPlus'; + | 'userPlus' + | 'clock'; type PaletteKey = 'primary' | 'success' | 'secondary' | 'accent' | 'info' | 'error' | 'warning'; type TiRouteId = @@ -25,7 +26,9 @@ | '/(dashboard)/ti/solicitacoes-acesso' | '/(dashboard)/ti/times' | '/(dashboard)/ti/notificacoes' - | '/(dashboard)/ti/monitoramento'; + | '/(dashboard)/ti/monitoramento' + | '/(dashboard)/ti/configuracoes-ponto' + | '/(dashboard)/ti/configuracoes-relogio'; type FeatureCard = { title: string; @@ -192,6 +195,13 @@ strokeLinecap: 'round', strokeLinejoin: 'round' } + ], + clock: [ + { + d: 'M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z', + strokeLinecap: 'round', + strokeLinejoin: 'round' + } ] }; @@ -231,15 +241,6 @@ { label: 'Alertas', variant: 'outline' } ] }, - { - title: 'Suporte Técnico', - description: - 'Central de atendimento para resolução de problemas técnicos e dúvidas sobre o sistema.', - ctaLabel: 'Em breve', - palette: 'info', - icon: 'support', - disabled: true - }, { title: 'Gerenciar Permissões', description: @@ -298,15 +299,6 @@ palette: 'accent', icon: 'users' }, - { - title: 'Solicitações de Acesso', - description: - 'Gerencie e analise solicitações de acesso ao sistema. Aprove ou rejeite novas solicitações de forma eficiente.', - ctaLabel: 'Gerenciar Solicitações', - href: '/(dashboard)/ti/solicitacoes-acesso', - palette: 'warning', - icon: 'userPlus' - }, { title: 'Gestão de Times', description: diff --git a/apps/web/src/routes/(dashboard)/ti/solicitacoes-acesso/+page.svelte b/apps/web/src/routes/(dashboard)/ti/solicitacoes-acesso/+page.svelte deleted file mode 100644 index 7cd4ac9..0000000 --- a/apps/web/src/routes/(dashboard)/ti/solicitacoes-acesso/+page.svelte +++ /dev/null @@ -1,836 +0,0 @@ - - - -
- - {#if mensagem} -
- {#if mensagem.tipo === 'success'} - - - - {:else if mensagem.tipo === 'error'} - - - - {/if} - {mensagem.texto} -
- {/if} - - -
-
-
- - - -
-
-

Solicitações de Acesso

-

- Gerencie e analise solicitações de acesso ao sistema -

-
-
-
- - - {#if stats} -
- - - 0 - ? ((stats.pendentes / stats.total) * 100).toFixed(1) + '% do total' - : '0% do total'} - Icon={Clock} - color="warning" - /> - - 0 - ? ((stats.aprovadas / stats.total) * 100).toFixed(1) + '% do total' - : '0% do total'} - Icon={CheckCircle2} - color="success" - /> - - 0 - ? ((stats.rejeitadas / stats.total) * 100).toFixed(1) + '% do total' - : '0% do total'} - Icon={XCircle} - color="error" - /> -
- {:else} -
- -
- {/if} - - -
-
- -
- - - - -
- - -
- -
- - - - -
-
-
-
- - - {#if carregando} -
- -
- {:else if solicitacoesFiltradas.length === 0} -
-
- - - -

- Nenhuma solicitação encontrada -

-

- {#if busca.trim() || filtroStatus !== 'todos'} - Tente ajustar os filtros ou a busca. - {:else} - Ainda não há solicitações de acesso cadastradas. - {/if} -

-
-
- {:else} -
- {#each solicitacoesFiltradas as solicitacao} -
-
-
-
-
-

{solicitacao.nome}

- - {getStatusTexto(solicitacao.status)} - -
- -
-
- - - - Matrícula: - {solicitacao.matricula} -
- -
- - - - E-mail: - {solicitacao.email} -
- -
- - - - Telefone: - {solicitacao.telefone} -
-
- -
- Solicitado em: - {formatarData(solicitacao.dataSolicitacao)} ({formatarDataRelativa( - solicitacao.dataSolicitacao - )}) - {#if solicitacao.dataResposta} - Processado em: - {formatarData(solicitacao.dataResposta)} - {/if} -
-
- -
- - - {#if solicitacao.status === 'pendente'} - - - - {/if} -
-
-
-
- {/each} -
- {/if} - - - {#if modalDetalhesAberto && solicitacaoSelecionada} - - - - - {/if} - - - {#if modalAprovarAberto && solicitacaoSelecionada} - - - - - {/if} - - - {#if modalRejeitarAberto && solicitacaoSelecionada} - - - - - {/if} -
-
diff --git a/packages/backend/convex/_generated/api.d.ts b/packages/backend/convex/_generated/api.d.ts index fdf3457..ac18618 100644 --- a/packages/backend/convex/_generated/api.d.ts +++ b/packages/backend/convex/_generated/api.d.ts @@ -21,6 +21,8 @@ import type * as auth_utils from "../auth/utils.js"; import type * as chamados from "../chamados.js"; import type * as chat from "../chat.js"; import type * as configuracaoEmail from "../configuracaoEmail.js"; +import type * as configuracaoPonto from "../configuracaoPonto.js"; +import type * as configuracaoRelogio from "../configuracaoRelogio.js"; import type * as contratos from "../contratos.js"; import type * as crons from "../crons.js"; import type * as cursos from "../cursos.js"; @@ -45,7 +47,6 @@ import type * as saldoFerias from "../saldoFerias.js"; import type * as security from "../security.js"; import type * as seed from "../seed.js"; import type * as simbolos from "../simbolos.js"; -import type * as solicitacoesAcesso from "../solicitacoesAcesso.js"; import type * as templatesMensagens from "../templatesMensagens.js"; import type * as times from "../times.js"; import type * as todos from "../todos.js"; @@ -73,9 +74,9 @@ declare const fullApi: ApiFromModules<{ chamados: typeof chamados; chat: typeof chat; configuracaoEmail: typeof configuracaoEmail; - contratos: typeof contratos; configuracaoPonto: typeof configuracaoPonto; configuracaoRelogio: typeof configuracaoRelogio; + contratos: typeof contratos; crons: typeof crons; cursos: typeof cursos; dashboard: typeof dashboard; @@ -99,7 +100,6 @@ declare const fullApi: ApiFromModules<{ security: typeof security; seed: typeof seed; simbolos: typeof simbolos; - solicitacoesAcesso: typeof solicitacoesAcesso; templatesMensagens: typeof templatesMensagens; times: typeof times; todos: typeof todos; diff --git a/packages/backend/convex/dashboard.ts b/packages/backend/convex/dashboard.ts index 7612ce9..1308d9d 100644 --- a/packages/backend/convex/dashboard.ts +++ b/packages/backend/convex/dashboard.ts @@ -7,8 +7,6 @@ export const getStats = query({ returns: v.object({ totalFuncionarios: v.number(), totalSimbolos: v.number(), - totalSolicitacoesAcesso: v.number(), - solicitacoesPendentes: v.number(), funcionariosAtivos: v.number(), funcionariosDesligados: v.number(), cargoComissionado: v.number(), @@ -42,19 +40,9 @@ export const getStats = query({ const simbolos = await ctx.db.query("simbolos").collect(); const totalSimbolos = simbolos.length; - // Contar solicitações de acesso - const solicitacoes = await ctx.db.query("solicitacoesAcesso").collect(); - const totalSolicitacoesAcesso = solicitacoes.length; - - const solicitacoesPendentes = solicitacoes.filter( - (s) => s.status === "pendente" - ).length; - return { totalFuncionarios, totalSimbolos, - totalSolicitacoesAcesso, - solicitacoesPendentes, funcionariosAtivos, funcionariosDesligados, cargoComissionado, @@ -68,7 +56,6 @@ export const getRecentActivity = query({ args: {}, returns: v.object({ funcionariosCadastrados24h: v.number(), - solicitacoesAcesso24h: v.number(), simbolosCadastrados24h: v.number(), }), handler: async (ctx) => { @@ -81,11 +68,6 @@ export const getRecentActivity = query({ (f) => f._creationTime >= last24h ).length; - // Solicitações de acesso nas últimas 24h - const solicitacoes = await ctx.db.query("solicitacoesAcesso").collect(); - const solicitacoesAcesso24h = solicitacoes.filter( - (s) => s.dataSolicitacao >= last24h - ).length; // Símbolos cadastrados nas últimas 24h const simbolos = await ctx.db.query("simbolos").collect(); @@ -95,7 +77,6 @@ export const getRecentActivity = query({ return { funcionariosCadastrados24h, - solicitacoesAcesso24h, simbolosCadastrados24h, }; }, @@ -137,15 +118,13 @@ export const getEvolucaoCadastros = query({ v.object({ mes: v.string(), funcionarios: v.number(), - solicitacoes: v.number(), }) ), handler: async (ctx) => { const funcionarios = await ctx.db.query("funcionarios").collect(); - const solicitacoes = await ctx.db.query("solicitacoesAcesso").collect(); const now = new Date(); - const meses: Array<{ mes: string; funcionarios: number; solicitacoes: number }> = []; + const meses: Array<{ mes: string; funcionarios: number }> = []; // Últimos 6 meses for (let i = 5; i >= 0; i--) { @@ -161,14 +140,9 @@ export const getEvolucaoCadastros = query({ (f) => f._creationTime >= date.getTime() && f._creationTime < nextDate.getTime() ).length; - const solCount = solicitacoes.filter( - (s) => s.dataSolicitacao >= date.getTime() && s.dataSolicitacao < nextDate.getTime() - ).length; - meses.push({ mes: mesNome, funcionarios: funcCount, - solicitacoes: solCount, }); } diff --git a/packages/backend/convex/monitoramento.ts b/packages/backend/convex/monitoramento.ts index 0cbd46f..a3bb14f 100644 --- a/packages/backend/convex/monitoramento.ts +++ b/packages/backend/convex/monitoramento.ts @@ -594,12 +594,11 @@ export const getStatusSistema = query({ } // Total de registros (estimativa baseada em tabelas principais) - const [usuarios, funcionarios, simbolos, solicitacoesAcesso, alertas, metricas] = + const [usuarios, funcionarios, simbolos, alertas, metricas] = await Promise.all([ ctx.db.query('usuarios').collect(), ctx.db.query('funcionarios').collect(), ctx.db.query('simbolos').collect(), - ctx.db.query('solicitacoesAcesso').collect(), ctx.db.query('alertConfigurations').collect(), ctx.db.query('systemMetrics').take(100) // não precisa contar tudo ]); @@ -607,7 +606,6 @@ export const getStatusSistema = query({ usuarios.length + funcionarios.length + simbolos.length + - solicitacoesAcesso.length + alertas.length + metricas.length; diff --git a/packages/backend/convex/schema.ts b/packages/backend/convex/schema.ts index 9b6a3d4..e24cf06 100644 --- a/packages/backend/convex/schema.ts +++ b/packages/backend/convex/schema.ts @@ -514,24 +514,6 @@ export default defineSchema({ valor: v.string(), }), - solicitacoesAcesso: defineTable({ - nome: v.string(), - matricula: v.string(), - email: v.string(), - telefone: v.string(), - status: v.union( - v.literal("pendente"), - v.literal("aprovado"), - v.literal("rejeitado") - ), - dataSolicitacao: v.number(), - dataResposta: v.optional(v.number()), - observacoes: v.optional(v.string()), - }) - .index("by_status", ["status"]) - .index("by_matricula", ["matricula"]) - .index("by_email", ["email"]), - // Sistema de Autenticação e Controle de Acesso usuarios: defineTable({ authId: v.string(), diff --git a/packages/backend/convex/seed.ts b/packages/backend/convex/seed.ts index 2af404c..3fee8cb 100644 --- a/packages/backend/convex/seed.ts +++ b/packages/backend/convex/seed.ts @@ -164,27 +164,6 @@ const funcionariosData = [ } ]; -const solicitacoesAcessoData = [ - { - dataResposta: 1761445098933, - dataSolicitacao: 1761445038329, - email: 'severino@gmail.com', - matricula: '3231', - nome: 'Severino Gates', - observacoes: 'Aprovação realizada por Deyvison', - status: 'aprovado' as const, - telefone: '(81) 9942-3551' - }, - { - dataSolicitacao: 1761445187258, - email: 'michaeljackson@gmail.com', - matricula: '123321', - nome: 'Michael Jackson', - status: 'pendente' as const, - telefone: '(81) 99423-5551' - } -]; - /** * Seed inicial do banco de dados com os dados exportados do Convex Cloud */ @@ -338,8 +317,6 @@ export const seedCreateUsuariosParaFuncionarios = internalMutation({ }); delay += 50; } - // Agenda próxima etapa após as criações individuais - await ctx.scheduler.runAfter(delay + 300, internal.seed.seedInserirSolicitacoesAcesso, {}); return null; } }); @@ -402,55 +379,6 @@ export const seedCreateUsuarioParaFuncionario = internalMutation({ } }); -export const seedInserirSolicitacoesAcesso = internalMutation({ - args: {}, - returns: v.null(), - handler: async (ctx) => { - console.log('📋 Inserindo solicitações de acesso...'); - for (const solicitacao of solicitacoesAcessoData) { - // Evitar duplicidade por matrícula - const existente = await ctx.db - .query('solicitacoesAcesso') - .withIndex('by_matricula', (q) => q.eq('matricula', solicitacao.matricula)) - .first(); - if (existente) { - console.log(` ℹ️ Solicitação já existe p/ matrícula ${solicitacao.matricula}`); - continue; - } - const dadosSolicitacao: { - nome: string; - matricula: string; - email: string; - telefone: string; - status: 'pendente' | 'aprovado' | 'rejeitado'; - dataSolicitacao: number; - dataResposta?: number; - observacoes?: string; - } = { - nome: solicitacao.nome, - matricula: solicitacao.matricula, - email: solicitacao.email, - telefone: solicitacao.telefone, - status: solicitacao.status, - dataSolicitacao: solicitacao.dataSolicitacao - }; - - if (solicitacao.dataResposta) { - dadosSolicitacao.dataResposta = solicitacao.dataResposta; - } - - if (solicitacao.observacoes) { - dadosSolicitacao.observacoes = solicitacao.observacoes; - } - - await ctx.db.insert('solicitacoesAcesso', dadosSolicitacao); - console.log(` ✅ Solicitação criada: ${solicitacao.nome} (${solicitacao.status})`); - } - console.log('✨ Seed concluído!'); - return null; - } -}); - export const seedDatabase = internalAction({ args: {}, returns: v.null(), @@ -460,7 +388,6 @@ export const seedDatabase = internalAction({ await ctx.runMutation(internal.seed.seedCreateSimbolos, {}); await ctx.runMutation(internal.seed.seedCreateFuncionarios, {}); await ctx.runMutation(internal.seed.seedCreateUsuariosParaFuncionarios, {}); - await ctx.runMutation(internal.seed.seedInserirSolicitacoesAcesso, {}); console.log('✨ Seed do banco de dados concluído com sucesso pela action!'); return null; } @@ -677,13 +604,6 @@ export const clearDatabase = internalMutation({ } console.log(` ✅ ${funcionarios.length} funcionários removidos`); - // 20. Solicitações de acesso - const solicitacoesAcesso = await ctx.db.query('solicitacoesAcesso').collect(); - for (const solicitacao of solicitacoesAcesso) { - await ctx.db.delete(solicitacao._id); - } - console.log(` ✅ ${solicitacoesAcesso.length} solicitações de acesso removidas`); - // 21. Símbolos const simbolos = await ctx.db.query('simbolos').collect(); for (const simbolo of simbolos) { @@ -907,13 +827,6 @@ export const limparBanco = mutation({ } console.log(` ✅ ${funcionarios.length} funcionários removidos`); - // 20. Solicitações de acesso - const solicitacoesAcesso = await ctx.db.query('solicitacoesAcesso').collect(); - for (const solicitacao of solicitacoesAcesso) { - await ctx.db.delete(solicitacao._id); - } - console.log(` ✅ ${solicitacoesAcesso.length} solicitações de acesso removidas`); - // 21. Símbolos const simbolos = await ctx.db.query('simbolos').collect(); for (const simbolo of simbolos) { diff --git a/packages/backend/convex/solicitacoesAcesso.ts b/packages/backend/convex/solicitacoesAcesso.ts deleted file mode 100644 index b5ff1ef..0000000 --- a/packages/backend/convex/solicitacoesAcesso.ts +++ /dev/null @@ -1,234 +0,0 @@ -import { mutation, query } from "./_generated/server"; -import { v } from "convex/values"; - -// Criar uma nova solicitação de acesso -export const create = mutation({ - args: { - nome: v.string(), - matricula: v.string(), - email: v.string(), - telefone: v.string(), - }, - returns: v.object({ - solicitacaoId: v.id("solicitacoesAcesso"), - }), - handler: async (ctx, args) => { - // Verificar se já existe uma solicitação pendente com a mesma matrícula - const existingByMatricula = await ctx.db - .query("solicitacoesAcesso") - .withIndex("by_matricula", (q) => q.eq("matricula", args.matricula)) - .filter((q) => q.eq(q.field("status"), "pendente")) - .first(); - - if (existingByMatricula) { - throw new Error("Já existe uma solicitação pendente para esta matrícula."); - } - - // Verificar se já existe uma solicitação pendente com o mesmo email - const existingByEmail = await ctx.db - .query("solicitacoesAcesso") - .withIndex("by_email", (q) => q.eq("email", args.email)) - .filter((q) => q.eq(q.field("status"), "pendente")) - .first(); - - if (existingByEmail) { - throw new Error("Já existe uma solicitação pendente para este e-mail."); - } - - const solicitacaoId = await ctx.db.insert("solicitacoesAcesso", { - nome: args.nome, - matricula: args.matricula, - email: args.email, - telefone: args.telefone, - status: "pendente", - dataSolicitacao: Date.now(), - }); - - return { solicitacaoId }; - }, -}); - -// Listar todas as solicitações (para o painel administrativo) -export const getAll = query({ - args: {}, - returns: v.array( - v.object({ - _id: v.id("solicitacoesAcesso"), - _creationTime: v.number(), - nome: v.string(), - matricula: v.string(), - email: v.string(), - telefone: v.string(), - status: v.union( - v.literal("pendente"), - v.literal("aprovado"), - v.literal("rejeitado") - ), - dataSolicitacao: v.number(), - dataResposta: v.union(v.number(), v.null()), - observacoes: v.union(v.string(), v.null()), - }) - ), - handler: async (ctx) => { - const solicitacoes = await ctx.db - .query("solicitacoesAcesso") - .order("desc") - .collect(); - - return solicitacoes.map((s) => ({ - _id: s._id, - _creationTime: s._creationTime, - nome: s.nome, - matricula: s.matricula, - email: s.email, - telefone: s.telefone, - status: s.status, - dataSolicitacao: s.dataSolicitacao, - dataResposta: s.dataResposta ?? null, - observacoes: s.observacoes ?? null, - })); - }, -}); - -// Listar apenas solicitações pendentes -export const getPendentes = query({ - args: {}, - returns: v.array( - v.object({ - _id: v.id("solicitacoesAcesso"), - _creationTime: v.number(), - nome: v.string(), - matricula: v.string(), - email: v.string(), - telefone: v.string(), - status: v.union( - v.literal("pendente"), - v.literal("aprovado"), - v.literal("rejeitado") - ), - dataSolicitacao: v.number(), - dataResposta: v.union(v.number(), v.null()), - observacoes: v.union(v.string(), v.null()), - }) - ), - handler: async (ctx) => { - const solicitacoes = await ctx.db - .query("solicitacoesAcesso") - .withIndex("by_status", (q) => q.eq("status", "pendente")) - .order("desc") - .collect(); - - return solicitacoes.map((s) => ({ - _id: s._id, - _creationTime: s._creationTime, - nome: s.nome, - matricula: s.matricula, - email: s.email, - telefone: s.telefone, - status: s.status, - dataSolicitacao: s.dataSolicitacao, - dataResposta: s.dataResposta ?? null, - observacoes: s.observacoes ?? null, - })); - }, -}); - -// Aprovar uma solicitação -export const aprovar = mutation({ - args: { - solicitacaoId: v.id("solicitacoesAcesso"), - observacoes: v.optional(v.string()), - }, - returns: v.null(), - handler: async (ctx, args) => { - const solicitacao = await ctx.db.get(args.solicitacaoId); - if (!solicitacao) { - throw new Error("Solicitação não encontrada."); - } - - if (solicitacao.status !== "pendente") { - throw new Error("Esta solicitação já foi processada."); - } - - await ctx.db.patch(args.solicitacaoId, { - status: "aprovado", - dataResposta: Date.now(), - observacoes: args.observacoes, - }); - - return null; - }, -}); - -// Rejeitar uma solicitação -export const rejeitar = mutation({ - args: { - solicitacaoId: v.id("solicitacoesAcesso"), - observacoes: v.optional(v.string()), - }, - returns: v.null(), - handler: async (ctx, args) => { - const solicitacao = await ctx.db.get(args.solicitacaoId); - if (!solicitacao) { - throw new Error("Solicitação não encontrada."); - } - - if (solicitacao.status !== "pendente") { - throw new Error("Esta solicitação já foi processada."); - } - - await ctx.db.patch(args.solicitacaoId, { - status: "rejeitado", - dataResposta: Date.now(), - observacoes: args.observacoes, - }); - - return null; - }, -}); - -// Obter uma solicitação por ID -export const getById = query({ - args: { - solicitacaoId: v.id("solicitacoesAcesso"), - }, - returns: v.union( - v.object({ - _id: v.id("solicitacoesAcesso"), - _creationTime: v.number(), - nome: v.string(), - matricula: v.string(), - email: v.string(), - telefone: v.string(), - status: v.union( - v.literal("pendente"), - v.literal("aprovado"), - v.literal("rejeitado") - ), - dataSolicitacao: v.number(), - dataResposta: v.union(v.number(), v.null()), - observacoes: v.union(v.string(), v.null()), - }), - v.null() - ), - handler: async (ctx, args) => { - const solicitacao = await ctx.db.get(args.solicitacaoId); - if (!solicitacao) { - return null; - } - - return { - _id: solicitacao._id, - _creationTime: solicitacao._creationTime, - nome: solicitacao.nome, - matricula: solicitacao.matricula, - email: solicitacao.email, - telefone: solicitacao.telefone, - status: solicitacao.status, - dataSolicitacao: solicitacao.dataSolicitacao, - dataResposta: solicitacao.dataResposta ?? null, - observacoes: solicitacao.observacoes ?? null, - }; - }, -}); -