From d79e6959c318f7e3440bc338663767ead95e683c Mon Sep 17 00:00:00 2001 From: deyvisonwanderley Date: Tue, 2 Dec 2025 16:36:02 -0300 Subject: [PATCH] feat: update ESLint and TypeScript configurations across frontend and backend; enhance component structure and improve data handling in various modules --- apps/web/convex/_generated/api.d.ts | 15 +- apps/web/convex/_generated/api.js | 2 +- apps/web/convex/_generated/dataModel.d.ts | 7 +- apps/web/convex/_generated/server.d.ts | 44 +- apps/web/convex/_generated/server.js | 18 +- apps/web/eslint.config.js | 46 +- apps/web/package.json | 128 +- apps/web/src/app.css | 759 ++- apps/web/src/app.d.ts | 10 +- apps/web/src/app.html | 30 +- apps/web/src/hooks.server.ts | 10 +- apps/web/src/lib/auth.ts | 10 +- .../web/src/lib/components/ActionGuard.svelte | 144 +- .../lib/components/AlterarStatusFerias.svelte | 5 +- .../lib/components/AprovarAusencias.svelte | 50 +- .../src/lib/components/AprovarFerias.svelte | 28 +- .../components/CalendarioAfastamentos.svelte | 964 ++-- apps/web/src/lib/components/ErrorModal.svelte | 79 +- apps/web/src/lib/components/FileUpload.svelte | 686 +-- .../FuncionarioMatriculaAutocomplete.svelte | 17 +- .../FuncionarioNomeAutocomplete.svelte | 17 +- apps/web/src/lib/components/Header.svelte | 6 +- .../src/lib/components/MenuProtection.svelte | 288 +- .../lib/components/ModelosDeclaracoes.svelte | 408 +- .../src/lib/components/ProtectedRoute.svelte | 4 +- .../components/PushNotificationManager.svelte | 252 +- .../src/lib/components/RelogioPrazo.svelte | 27 +- apps/web/src/lib/components/Sidebar.svelte | 45 +- .../ausencias/CalendarioAusencias.svelte | 1862 ++++--- .../WizardSolicitacaoAusencia.svelte | 4 +- .../lib/components/call/CallControls.svelte | 14 +- .../lib/components/call/CallSettings.svelte | 54 +- .../src/lib/components/call/CallWindow.svelte | 733 +-- .../lib/components/call/HostControls.svelte | 42 +- .../components/call/RecordingIndicator.svelte | 2 +- .../lib/components/chamados/SlaChart.svelte | 339 +- .../lib/components/chamados/TicketCard.svelte | 183 +- .../lib/components/chamados/TicketForm.svelte | 565 +-- .../components/chamados/TicketTimeline.svelte | 147 +- .../src/lib/components/chat/ChatList.svelte | 897 ++-- .../src/lib/components/chat/ChatWidget.svelte | 5 +- .../src/lib/components/chat/ChatWindow.svelte | 20 +- .../lib/components/chat/MessageInput.svelte | 978 ++-- .../lib/components/chat/MessageList.svelte | 1605 +++--- .../components/chat/NotificationBell.svelte | 1151 ++--- .../components/chat/PresenceManager.svelte | 191 +- .../components/chat/UserStatusBadge.svelte | 97 +- .../ferias/WizardSolicitacaoFerias.svelte | 41 +- .../components/ponto/ComprovantePonto.svelte | 281 +- .../components/ponto/LocalizacaoIcon.svelte | 8 +- .../components/ponto/PrintPontoModal.svelte | 34 +- .../lib/components/ponto/RegistroPonto.svelte | 704 +-- .../ponto/RelogioSincronizado.svelte | 31 +- .../ponto/SaldoDiarioComparativoBadge.svelte | 23 +- .../lib/components/ponto/WebcamCapture.svelte | 209 +- .../ponto/WidgetGestaoPontos.svelte | 67 +- .../src/lib/components/ti/StatsCard.svelte | 90 +- .../components/ti/SystemMonitorCard.svelte | 857 ++-- .../ti/SystemMonitorCardLocal.svelte | 7 +- .../lib/components/ti/UserStatusBadge.svelte | 26 +- .../lib/components/ti/charts/AreaChart.svelte | 227 +- .../lib/components/ti/charts/BarChart.svelte | 207 +- .../components/ti/charts/BarChart3D.svelte | 9 +- .../components/ti/charts/DoughnutChart.svelte | 181 +- .../lib/components/ti/charts/LineChart.svelte | 235 +- apps/web/src/lib/hooks/convexAuth.ts | 22 +- apps/web/src/lib/hooks/useConvexWithAuth.ts | 73 +- apps/web/src/lib/stores/auth.svelte.ts | 294 +- apps/web/src/lib/stores/callStore.ts | 34 +- apps/web/src/lib/stores/chamados.ts | 79 +- apps/web/src/lib/stores/chatStore.ts | 25 +- apps/web/src/lib/stores/convexAuth.ts | 79 +- apps/web/src/lib/stores/loginModal.svelte.ts | 23 +- apps/web/src/lib/utils/browserInfo.ts | 342 +- apps/web/src/lib/utils/callWindowManager.ts | 7 +- apps/web/src/lib/utils/chamados.ts | 175 +- apps/web/src/lib/utils/constants.ts | 77 +- .../web/src/lib/utils/declaracoesGenerator.ts | 1172 +++-- apps/web/src/lib/utils/deviceInfo.ts | 141 +- apps/web/src/lib/utils/documentos.ts | 343 +- apps/web/src/lib/utils/erroHelpers.ts | 22 +- apps/web/src/lib/utils/floatingWindow.ts | 48 +- apps/web/src/lib/utils/jitsi.ts | 53 +- apps/web/src/lib/utils/jitsiPolyfill.ts | 32 +- apps/web/src/lib/utils/mediaRecorder.ts | 16 +- apps/web/src/lib/utils/metricsCollector.ts | 442 +- apps/web/src/lib/utils/modelosDeclaracoes.ts | 84 +- apps/web/src/lib/utils/notifications.ts | 368 +- apps/web/src/lib/utils/ponto.ts | 21 +- apps/web/src/lib/utils/sincronizacaoTempo.ts | 1 - apps/web/src/lib/utils/temas.ts | 9 +- apps/web/src/lib/utils/webcam.ts | 27 +- .../src/routes/(dashboard)/+layout.server.ts | 22 +- .../web/src/routes/(dashboard)/+layout.svelte | 206 +- apps/web/src/routes/(dashboard)/+page.svelte | 1535 +++--- .../(dashboard)/abrir-chamado/+page.svelte | 436 +- .../(dashboard)/alterar-senha/+page.svelte | 116 +- .../routes/(dashboard)/compras/+page.svelte | 129 +- .../(dashboard)/comunicacao/+page.svelte | 94 +- .../configuracoes/setores/+page.svelte | 25 +- .../(dashboard)/controladoria/+page.svelte | 184 +- .../(dashboard)/financeiro/+page.svelte | 184 +- .../routes/(dashboard)/fluxos/+page.svelte | 143 +- .../fluxos/[id]-fluxo/+page.svelte | 366 +- .../fluxos/[id]/editor/+page.svelte | 383 +- .../fluxos/instancias/+page.svelte | 98 +- .../(dashboard)/gestao-pessoas/+page.svelte | 283 +- .../routes/(dashboard)/juridico/+page.svelte | 94 +- .../(dashboard)/licitacoes/+page.svelte | 340 +- .../licitacoes/contratos/[id]/+page.svelte | 10 +- .../licitacoes/contratos/novo/+page.svelte | 16 +- .../licitacoes/empresas/+page.svelte | 1717 ++++--- .../licitacoes/fluxos/+page.svelte | 100 +- .../licitacoes/fluxos/[id]/+page.svelte | 781 ++- .../routes/(dashboard)/perfil/+page.svelte | 70 +- .../(dashboard)/perfil/chamados/+page.svelte | 599 ++- .../perfil/privacidade/+page.svelte | 82 +- .../(dashboard)/privacidade/+page.svelte | 237 +- .../privacidade/meus-dados/+page.svelte | 82 +- .../programas-esportivos/+page.svelte | 191 +- .../(dashboard)/recursos-humanos/+page.svelte | 640 ++- .../atestados-licencas/+page.svelte | 216 +- .../recursos-humanos/ausencias/+page.svelte | 4 +- .../controle-ponto/+page.svelte | 45 +- .../controle-ponto/dispensa/+page.svelte | 65 +- .../controle-ponto/homologacao/+page.svelte | 544 +- .../recursos-humanos/ferias/+page.svelte | 1109 ++-- .../funcionarios/+page.svelte | 124 +- .../funcionarios/[funcionarioId]/+page.svelte | 1691 +++---- .../enderecos-marcacao/+page.svelte | 103 +- .../funcionarios/excluir/+page.svelte | 9 +- .../recursos-humanos/simbolos/+page.svelte | 6 +- .../simbolos/cadastro/+page.svelte | 10 +- .../secretaria-executiva/+page.svelte | 281 +- .../gestao-ausencias/+page.svelte | 3 +- .../termo-consentimento/+page.svelte | 120 +- .../src/routes/(dashboard)/ti/+page.svelte | 4 +- .../(dashboard)/ti/auditoria/+page.svelte | 1950 +++---- .../ti/configuracoes-jitsi/+page.svelte | 2 - .../ti/configuracoes-ponto/+page.svelte | 44 +- .../enderecos/+page.svelte | 225 +- .../ti/configuracoes-relogio/+page.svelte | 193 +- .../routes/(dashboard)/ti/lgpd/+page.svelte | 39 +- .../ti/lgpd/configuracoes/+page.svelte | 75 +- .../ti/lgpd/registros-tratamento/+page.svelte | 64 +- .../ti/lgpd/solicitacoes/+page.svelte | 90 +- .../(dashboard)/ti/monitoramento/+page.svelte | 4 +- .../(dashboard)/ti/notificacoes/+page.svelte | 1598 +++--- .../ti/notificacoes/templates/+page.svelte | 41 +- .../notificacoes/templates/[id]/+page.svelte | 246 +- .../ti/painel-administrativo/+page.svelte | 325 +- .../routes/(dashboard)/ti/perfis/+page.svelte | 4 +- .../ti/personalizar-permissoes/+page.svelte | 3 +- apps/web/src/routes/+layout.svelte | 92 +- .../src/routes/api/auth/[...all]/+server.ts | 2 +- apps/web/src/routes/call/+page.svelte | 3 +- apps/web/src/routes/todos/+page.svelte | 21 +- apps/web/static/sounds/README.md | 1 - apps/web/static/sw.js | 105 +- apps/web/svelte.config.js | 10 +- package.json | 4 +- packages/backend/convex/README.md | 86 +- packages/backend/convex/_generated/api.d.ts | 4464 ++++++++--------- packages/backend/convex/_generated/api.js | 2 +- .../backend/convex/_generated/dataModel.d.ts | 22 +- .../backend/convex/_generated/server.d.ts | 34 +- packages/backend/convex/_generated/server.js | 16 +- packages/backend/convex/actions/email.ts | 389 +- .../backend/convex/actions/jitsiServer.ts | 3 +- .../backend/convex/actions/linkPreview.ts | 237 +- .../convex/actions/pushNotifications.ts | 169 +- packages/backend/convex/actions/smtp.ts | 107 +- .../convex/actions/utils/nodeCrypto.ts | 113 +- packages/backend/convex/atestadosLicencas.ts | 8 +- packages/backend/convex/ausencias.ts | 1262 +++-- packages/backend/convex/autenticacao.ts | 13 +- packages/backend/convex/auth.config.ts | 12 +- packages/backend/convex/auth/utils.ts | 323 +- packages/backend/convex/chamadas.ts | 73 +- packages/backend/convex/chamados.ts | 1488 +++--- packages/backend/convex/configuracaoEmail.ts | 370 +- packages/backend/convex/configuracaoJitsi.ts | 125 +- packages/backend/convex/configuracaoPonto.ts | 17 +- .../backend/convex/configuracaoRelogio.ts | 99 +- packages/backend/convex/convex.config.ts | 6 +- packages/backend/convex/crons.ts | 55 +- packages/backend/convex/cursos.ts | 107 +- packages/backend/convex/dashboard.ts | 238 +- packages/backend/convex/documentos.ts | 357 +- packages/backend/convex/email.ts | 714 ++- packages/backend/convex/empresas.ts | 492 +- packages/backend/convex/enderecosMarcacao.ts | 87 +- packages/backend/convex/ferias.ts | 1367 +++-- .../backend/convex/funcionarioEnderecos.ts | 30 +- packages/backend/convex/healthCheck.ts | 6 +- packages/backend/convex/http.ts | 148 +- packages/backend/convex/lgpd.ts | 31 +- packages/backend/convex/logsAcesso.ts | 365 +- packages/backend/convex/logsLogin.ts | 933 ++-- packages/backend/convex/monitoramento.ts | 51 +- packages/backend/convex/pontos.ts | 380 +- packages/backend/convex/saldoFerias.ts | 738 ++- packages/backend/convex/setores.ts | 5 +- packages/backend/convex/templatesMensagens.ts | 1531 +++--- packages/backend/convex/times.ts | 607 ++- packages/backend/convex/tsconfig.json | 15 +- packages/backend/convex/types/web-push.d.ts | 73 +- packages/backend/convex/usuarios.ts | 4 +- .../convex/utils/chatTemplateWrapper.ts | 40 +- .../convex/utils/emailTemplateWrapper.ts | 83 +- packages/backend/convex/utils/getClientIP.ts | 267 +- .../backend/convex/utils/scanEmailSenders.ts | 321 +- .../backend/convex/verificarMatriculas.ts | 220 +- packages/backend/eslint.config.js | 6 +- packages/backend/package.json | 66 +- 215 files changed, 29474 insertions(+), 28173 deletions(-) diff --git a/apps/web/convex/_generated/api.d.ts b/apps/web/convex/_generated/api.d.ts index 73b85e4..f3e9748 100644 --- a/apps/web/convex/_generated/api.d.ts +++ b/apps/web/convex/_generated/api.d.ts @@ -8,11 +8,7 @@ * @module */ -import type { - ApiFromModules, - FilterApi, - FunctionReference, -} from "convex/server"; +import type { ApiFromModules, FilterApi, FunctionReference } from 'convex/server'; /** * A utility for referencing Convex functions in your app's API. @@ -25,13 +21,10 @@ import type { declare const fullApi: ApiFromModules<{}>; declare const fullApiWithMounts: typeof fullApi; -export declare const api: FilterApi< - typeof fullApiWithMounts, - FunctionReference ->; +export declare const api: FilterApi>; export declare const internal: FilterApi< - typeof fullApiWithMounts, - FunctionReference + typeof fullApiWithMounts, + FunctionReference >; export declare const components: {}; diff --git a/apps/web/convex/_generated/api.js b/apps/web/convex/_generated/api.js index 44bf985..24593c7 100644 --- a/apps/web/convex/_generated/api.js +++ b/apps/web/convex/_generated/api.js @@ -8,7 +8,7 @@ * @module */ -import { anyApi, componentsGeneric } from "convex/server"; +import { anyApi, componentsGeneric } from 'convex/server'; /** * A utility for referencing Convex functions in your app's API. diff --git a/apps/web/convex/_generated/dataModel.d.ts b/apps/web/convex/_generated/dataModel.d.ts index fb12533..8e4c6a1 100644 --- a/apps/web/convex/_generated/dataModel.d.ts +++ b/apps/web/convex/_generated/dataModel.d.ts @@ -8,8 +8,8 @@ * @module */ -import { AnyDataModel } from "convex/server"; -import type { GenericId } from "convex/values"; +import { AnyDataModel } from 'convex/server'; +import type { GenericId } from 'convex/values'; /** * No `schema.ts` file found! @@ -43,8 +43,7 @@ export type Doc = any; * IDs are just strings at runtime, but this type can be used to distinguish them from other * strings when type checking. */ -export type Id = - GenericId; +export type Id = GenericId; /** * A type describing your Convex data model. diff --git a/apps/web/convex/_generated/server.d.ts b/apps/web/convex/_generated/server.d.ts index b5c6828..9f1d83e 100644 --- a/apps/web/convex/_generated/server.d.ts +++ b/apps/web/convex/_generated/server.d.ts @@ -9,24 +9,24 @@ */ import { - ActionBuilder, - AnyComponents, - HttpActionBuilder, - MutationBuilder, - QueryBuilder, - GenericActionCtx, - GenericMutationCtx, - GenericQueryCtx, - GenericDatabaseReader, - GenericDatabaseWriter, - FunctionReference, -} from "convex/server"; -import type { DataModel } from "./dataModel.js"; + ActionBuilder, + AnyComponents, + HttpActionBuilder, + MutationBuilder, + QueryBuilder, + GenericActionCtx, + GenericMutationCtx, + GenericQueryCtx, + GenericDatabaseReader, + GenericDatabaseWriter, + FunctionReference +} from 'convex/server'; +import type { DataModel } from './dataModel.js'; type GenericCtx = - | GenericActionCtx - | GenericMutationCtx - | GenericQueryCtx; + | GenericActionCtx + | GenericMutationCtx + | GenericQueryCtx; /** * Define a query in this Convex app's public API. @@ -36,7 +36,7 @@ type GenericCtx = * @param func - The query function. It receives a {@link QueryCtx} as its first argument. * @returns The wrapped query. Include this as an `export` to name it and make it accessible. */ -export declare const query: QueryBuilder; +export declare const query: QueryBuilder; /** * Define a query that is only accessible from other Convex functions (but not from the client). @@ -46,7 +46,7 @@ export declare const query: QueryBuilder; * @param func - The query function. It receives a {@link QueryCtx} as its first argument. * @returns The wrapped query. Include this as an `export` to name it and make it accessible. */ -export declare const internalQuery: QueryBuilder; +export declare const internalQuery: QueryBuilder; /** * Define a mutation in this Convex app's public API. @@ -56,7 +56,7 @@ export declare const internalQuery: QueryBuilder; * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. */ -export declare const mutation: MutationBuilder; +export declare const mutation: MutationBuilder; /** * Define a mutation that is only accessible from other Convex functions (but not from the client). @@ -66,7 +66,7 @@ export declare const mutation: MutationBuilder; * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. */ -export declare const internalMutation: MutationBuilder; +export declare const internalMutation: MutationBuilder; /** * Define an action in this Convex app's public API. @@ -79,7 +79,7 @@ export declare const internalMutation: MutationBuilder; * @param func - The action. It receives an {@link ActionCtx} as its first argument. * @returns The wrapped action. Include this as an `export` to name it and make it accessible. */ -export declare const action: ActionBuilder; +export declare const action: ActionBuilder; /** * Define an action that is only accessible from other Convex functions (but not from the client). @@ -87,7 +87,7 @@ export declare const action: ActionBuilder; * @param func - The function. It receives an {@link ActionCtx} as its first argument. * @returns The wrapped function. Include this as an `export` to name it and make it accessible. */ -export declare const internalAction: ActionBuilder; +export declare const internalAction: ActionBuilder; /** * Define an HTTP action. diff --git a/apps/web/convex/_generated/server.js b/apps/web/convex/_generated/server.js index 4a21df4..230f1c6 100644 --- a/apps/web/convex/_generated/server.js +++ b/apps/web/convex/_generated/server.js @@ -9,15 +9,15 @@ */ import { - actionGeneric, - httpActionGeneric, - queryGeneric, - mutationGeneric, - internalActionGeneric, - internalMutationGeneric, - internalQueryGeneric, - componentsGeneric, -} from "convex/server"; + actionGeneric, + httpActionGeneric, + queryGeneric, + mutationGeneric, + internalActionGeneric, + internalMutationGeneric, + internalQueryGeneric, + componentsGeneric +} from 'convex/server'; /** * Define a query in this Convex app's public API. diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js index 60d180a..71da704 100644 --- a/apps/web/eslint.config.js +++ b/apps/web/eslint.config.js @@ -1,28 +1,28 @@ import { config as svelteConfigBase } from '@sgse-app/eslint-config/svelte'; import svelteConfig from './svelte.config.js'; import ts from 'typescript-eslint'; -import { defineConfig } from "eslint/config"; - +import { defineConfig } from 'eslint/config'; + /** @type {import("eslint").Linter.Config} */ export default defineConfig([ - ...svelteConfigBase, - { - files: ['**/*.svelte'], - languageOptions: { - parserOptions: { - parser: ts.parser, - extraFileExtensions: ['.svelte'], - svelteConfig - } - } - }, - { - ignores: [ - '**/node_modules/**', - '**/.svelte-kit/**', - '**/build/**', - '**/dist/**', - '**/.turbo/**' - ] - } -]) + ...svelteConfigBase, + { + files: ['**/*.svelte'], + languageOptions: { + parserOptions: { + parser: ts.parser, + extraFileExtensions: ['.svelte'], + svelteConfig + } + } + }, + { + ignores: [ + '**/node_modules/**', + '**/.svelte-kit/**', + '**/build/**', + '**/dist/**', + '**/.turbo/**' + ] + } +]); diff --git a/apps/web/package.json b/apps/web/package.json index 44bda5f..84c67e8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,64 +1,66 @@ { - "name": "web", - "private": true, - "version": "0.0.1", - "type": "module", - "scripts": { - "dev": "bunx --bun vite dev", - "build": "bunx --bun vite build", - "preview": "bunx --bun vite preview", - "prepare": "svelte-kit sync || echo ''", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" - }, - "devDependencies": { - "@sgse-app/eslint-config": "*", - "@sveltejs/adapter-auto": "^6.1.0", - "@sveltejs/kit": "^2.31.1", - "@sveltejs/vite-plugin-svelte": "^6.1.2", - "@tailwindcss/vite": "^4.1.12", - "autoprefixer": "^10.4.21", - "daisyui": "^5.3.8", - "esbuild": "^0.25.11", - "postcss": "^8.5.6", - "svelte": "^5.38.1", - "svelte-adapter-bun": "^1.0.1", - "svelte-check": "^4.3.1", - "svelte-dnd-action": "^0.9.67", - "tailwindcss": "^4.1.12", - "typescript": "catalog:", - "vite": "^7.1.2" - }, - "dependencies": { - "@convex-dev/better-auth": "^0.9.7", - "@dicebear/collection": "^9.2.4", - "@dicebear/core": "^9.2.4", - "@fullcalendar/core": "^6.1.19", - "@fullcalendar/daygrid": "^6.1.19", - "@fullcalendar/interaction": "^6.1.19", - "@fullcalendar/list": "^6.1.19", - "@fullcalendar/multimonth": "^6.1.19", - "@internationalized/date": "^3.10.0", - "@mmailaender/convex-better-auth-svelte": "^0.2.0", - "@sgse-app/backend": "*", - "@tanstack/svelte-form": "^1.19.2", - "@types/papaparse": "^5.3.14", - "better-auth": "catalog:", - "convex": "catalog:", - "convex-svelte": "^0.0.12", - "date-fns": "^4.1.0", - "emoji-picker-element": "^1.27.0", - "eslint": "catalog:", - "exceljs": "^4.4.0", - "is-network-error": "^1.3.0", - "jspdf": "^3.0.3", - "jspdf-autotable": "^5.0.2", - "lib-jitsi-meet": "^1.0.6", - "lucide-svelte": "^0.552.0", - "papaparse": "^5.4.1", - "svelte-sonner": "^1.0.5", - "xlsx": "^0.18.5", - "xlsx-js-style": "^1.2.0", - "zod": "^4.1.12" - } -} \ No newline at end of file + "name": "web", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "bunx --bun vite dev", + "lint": "eslint .", + "format": "prettier --write .", + "build": "bunx --bun vite build", + "preview": "bunx --bun vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "devDependencies": { + "@sgse-app/eslint-config": "*", + "@sveltejs/adapter-auto": "^6.1.0", + "@sveltejs/kit": "^2.31.1", + "@sveltejs/vite-plugin-svelte": "^6.1.2", + "@tailwindcss/vite": "^4.1.12", + "autoprefixer": "^10.4.21", + "daisyui": "^5.3.8", + "esbuild": "^0.25.11", + "postcss": "^8.5.6", + "svelte": "^5.38.1", + "svelte-adapter-bun": "^1.0.1", + "svelte-check": "^4.3.1", + "svelte-dnd-action": "^0.9.67", + "tailwindcss": "^4.1.12", + "typescript": "catalog:", + "vite": "^7.1.2" + }, + "dependencies": { + "@convex-dev/better-auth": "^0.9.7", + "@dicebear/collection": "^9.2.4", + "@dicebear/core": "^9.2.4", + "@fullcalendar/core": "^6.1.19", + "@fullcalendar/daygrid": "^6.1.19", + "@fullcalendar/interaction": "^6.1.19", + "@fullcalendar/list": "^6.1.19", + "@fullcalendar/multimonth": "^6.1.19", + "@internationalized/date": "^3.10.0", + "@mmailaender/convex-better-auth-svelte": "^0.2.0", + "@sgse-app/backend": "*", + "@tanstack/svelte-form": "^1.19.2", + "@types/papaparse": "^5.3.14", + "better-auth": "catalog:", + "convex": "catalog:", + "convex-svelte": "^0.0.12", + "date-fns": "^4.1.0", + "emoji-picker-element": "^1.27.0", + "eslint": "catalog:", + "exceljs": "^4.4.0", + "is-network-error": "^1.3.0", + "jspdf": "^3.0.3", + "jspdf-autotable": "^5.0.2", + "lib-jitsi-meet": "^1.0.6", + "lucide-svelte": "^0.552.0", + "papaparse": "^5.4.1", + "svelte-sonner": "^1.0.5", + "xlsx": "^0.18.5", + "xlsx-js-style": "^1.2.0", + "zod": "^4.1.12" + } +} diff --git a/apps/web/src/app.css b/apps/web/src/app.css index 865aaf1..9622199 100644 --- a/apps/web/src/app.css +++ b/apps/web/src/app.css @@ -1,426 +1,425 @@ -@import "tailwindcss"; -@plugin "daisyui"; +@import 'tailwindcss'; +@plugin 'daisyui'; /* FullCalendar CSS - v6 não exporta CSS separado, estilos são aplicados via JavaScript */ /* Estilo padrão dos botões - mesmo estilo do sidebar */ .btn-standard { - @apply font-medium flex items-center justify-center gap-2 text-center p-3 rounded-xl border border-base-300 bg-base-100 hover:bg-primary/60 active:bg-primary text-base-content hover:text-white active:text-white transition-colors; + @apply border-base-300 bg-base-100 hover:bg-primary/60 active:bg-primary text-base-content flex items-center justify-center gap-2 rounded-xl border p-3 text-center font-medium transition-colors hover:text-white active:text-white; } /* Sobrescrever estilos DaisyUI para seguir o padrão */ .btn-primary { - @apply font-medium flex items-center justify-center gap-2 text-center px-4 py-2 rounded-xl border border-base-300 bg-base-100 hover:bg-primary/60 active:bg-primary text-base-content hover:text-white active:text-white transition-colors; + @apply border-base-300 bg-base-100 hover:bg-primary/60 active:bg-primary text-base-content flex items-center justify-center gap-2 rounded-xl border px-4 py-2 text-center font-medium transition-colors hover:text-white active:text-white; } .btn-ghost { - @apply font-medium flex items-center justify-center gap-2 text-center px-4 py-2 rounded-xl border border-base-300 bg-base-100 hover:bg-base-200 active:bg-base-300 text-base-content transition-colors; + @apply border-base-300 bg-base-100 hover:bg-base-200 active:bg-base-300 text-base-content flex items-center justify-center gap-2 rounded-xl border px-4 py-2 text-center font-medium transition-colors; } .btn-error { - @apply font-medium flex items-center justify-center gap-2 text-center px-4 py-2 rounded-xl border border-error bg-base-100 hover:bg-error/60 active:bg-error text-error hover:text-white active:text-white transition-colors; + @apply border-error bg-base-100 hover:bg-error/60 active:bg-error text-error flex items-center justify-center gap-2 rounded-xl border px-4 py-2 text-center font-medium transition-colors hover:text-white active:text-white; } - /* Tema Aqua (padrão roxo/azul) - customizado para garantir funcionamento */ -html[data-theme="aqua"], -html[data-theme="aqua"] body, -[data-theme="aqua"] { - color-scheme: light; - --p: 217 91% 60%; - --pf: 217 91% 50%; - --pc: 0 0% 100%; - --s: 217 91% 60%; - --sf: 217 91% 50%; - --sc: 0 0% 100%; - --a: 217 91% 60%; - --af: 217 91% 50%; - --ac: 0 0% 100%; - --n: 217 20% 17%; - --nf: 217 20% 10%; - --nc: 0 0% 100%; - --b1: 0 0% 100%; - --b2: 217 20% 95%; - --b3: 217 20% 90%; - --bc: 217 20% 17%; - --in: 217 91% 60%; - --inc: 0 0% 100%; - --su: 142 76% 36%; - --suc: 0 0% 100%; - --wa: 38 92% 50%; - --wac: 0 0% 100%; - --er: 0 84% 60%; - --erc: 0 0% 100%; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: 0.2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; +html[data-theme='aqua'], +html[data-theme='aqua'] body, +[data-theme='aqua'] { + color-scheme: light; + --p: 217 91% 60%; + --pf: 217 91% 50%; + --pc: 0 0% 100%; + --s: 217 91% 60%; + --sf: 217 91% 50%; + --sc: 0 0% 100%; + --a: 217 91% 60%; + --af: 217 91% 50%; + --ac: 0 0% 100%; + --n: 217 20% 17%; + --nf: 217 20% 10%; + --nc: 0 0% 100%; + --b1: 0 0% 100%; + --b2: 217 20% 95%; + --b3: 217 20% 90%; + --bc: 217 20% 17%; + --in: 217 91% 60%; + --inc: 0 0% 100%; + --su: 142 76% 36%; + --suc: 0 0% 100%; + --wa: 38 92% 50%; + --wac: 0 0% 100%; + --er: 0 84% 60%; + --erc: 0 0% 100%; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: 0.2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; } /* Temas customizados para SGSE - Azul */ -html[data-theme="sgse-blue"], -html[data-theme="sgse-blue"] body, -[data-theme="sgse-blue"] { - color-scheme: light; - --p: 217 91% 60%; - --pf: 217 91% 50%; - --pc: 0 0% 100%; - --s: 217 91% 60%; - --sf: 217 91% 50%; - --sc: 0 0% 100%; - --a: 217 91% 60%; - --af: 217 91% 50%; - --ac: 0 0% 100%; - --n: 217 20% 17%; - --nf: 217 20% 10%; - --nc: 0 0% 100%; - --b1: 0 0% 100%; - --b2: 217 20% 95%; - --b3: 217 20% 90%; - --bc: 217 20% 17%; - --in: 217 91% 60%; - --inc: 0 0% 100%; - --su: 142 76% 36%; - --suc: 0 0% 100%; - --wa: 38 92% 50%; - --wac: 0 0% 100%; - --er: 0 84% 60%; - --erc: 0 0% 100%; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: 0.2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; +html[data-theme='sgse-blue'], +html[data-theme='sgse-blue'] body, +[data-theme='sgse-blue'] { + color-scheme: light; + --p: 217 91% 60%; + --pf: 217 91% 50%; + --pc: 0 0% 100%; + --s: 217 91% 60%; + --sf: 217 91% 50%; + --sc: 0 0% 100%; + --a: 217 91% 60%; + --af: 217 91% 50%; + --ac: 0 0% 100%; + --n: 217 20% 17%; + --nf: 217 20% 10%; + --nc: 0 0% 100%; + --b1: 0 0% 100%; + --b2: 217 20% 95%; + --b3: 217 20% 90%; + --bc: 217 20% 17%; + --in: 217 91% 60%; + --inc: 0 0% 100%; + --su: 142 76% 36%; + --suc: 0 0% 100%; + --wa: 38 92% 50%; + --wac: 0 0% 100%; + --er: 0 84% 60%; + --erc: 0 0% 100%; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: 0.2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; } /* Garantir que todas as variáveis CSS sejam aplicadas em todos os elementos */ html[data-theme] { - color-scheme: var(--color-scheme, light); + color-scheme: var(--color-scheme, light); } html[data-theme] * { - color-scheme: inherit; + color-scheme: inherit; } -html[data-theme="sgse-green"], -html[data-theme="sgse-green"] body, -[data-theme="sgse-green"] { - color-scheme: light; - --p: 142 76% 36%; - --pf: 142 76% 26%; - --pc: 0 0% 100%; - --s: 142 76% 36%; - --sf: 142 76% 26%; - --sc: 0 0% 100%; - --a: 142 76% 36%; - --af: 142 76% 26%; - --ac: 0 0% 100%; - --n: 142 20% 17%; - --nf: 142 20% 10%; - --nc: 0 0% 100%; - --b1: 0 0% 100%; - --b2: 142 20% 95%; - --b3: 142 20% 90%; - --bc: 142 20% 17%; - --in: 142 76% 36%; - --inc: 0 0% 100%; - --su: 142 76% 36%; - --suc: 0 0% 100%; - --wa: 38 92% 50%; - --wac: 0 0% 100%; - --er: 0 84% 60%; - --erc: 0 0% 100%; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: 0.2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; +html[data-theme='sgse-green'], +html[data-theme='sgse-green'] body, +[data-theme='sgse-green'] { + color-scheme: light; + --p: 142 76% 36%; + --pf: 142 76% 26%; + --pc: 0 0% 100%; + --s: 142 76% 36%; + --sf: 142 76% 26%; + --sc: 0 0% 100%; + --a: 142 76% 36%; + --af: 142 76% 26%; + --ac: 0 0% 100%; + --n: 142 20% 17%; + --nf: 142 20% 10%; + --nc: 0 0% 100%; + --b1: 0 0% 100%; + --b2: 142 20% 95%; + --b3: 142 20% 90%; + --bc: 142 20% 17%; + --in: 142 76% 36%; + --inc: 0 0% 100%; + --su: 142 76% 36%; + --suc: 0 0% 100%; + --wa: 38 92% 50%; + --wac: 0 0% 100%; + --er: 0 84% 60%; + --erc: 0 0% 100%; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: 0.2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; } -html[data-theme="sgse-orange"], -html[data-theme="sgse-orange"] body, -[data-theme="sgse-orange"] { - color-scheme: light; - --p: 25 95% 53%; - --pf: 25 95% 43%; - --pc: 0 0% 100%; - --s: 25 95% 53%; - --sf: 25 95% 43%; - --sc: 0 0% 100%; - --a: 25 95% 53%; - --af: 25 95% 43%; - --ac: 0 0% 100%; - --n: 25 20% 17%; - --nf: 25 20% 10%; - --nc: 0 0% 100%; - --b1: 0 0% 100%; - --b2: 25 20% 95%; - --b3: 25 20% 90%; - --bc: 25 20% 17%; - --in: 25 95% 53%; - --inc: 0 0% 100%; - --su: 142 76% 36%; - --suc: 0 0% 100%; - --wa: 38 92% 50%; - --wac: 0 0% 100%; - --er: 0 84% 60%; - --erc: 0 0% 100%; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: 0.2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; +html[data-theme='sgse-orange'], +html[data-theme='sgse-orange'] body, +[data-theme='sgse-orange'] { + color-scheme: light; + --p: 25 95% 53%; + --pf: 25 95% 43%; + --pc: 0 0% 100%; + --s: 25 95% 53%; + --sf: 25 95% 43%; + --sc: 0 0% 100%; + --a: 25 95% 53%; + --af: 25 95% 43%; + --ac: 0 0% 100%; + --n: 25 20% 17%; + --nf: 25 20% 10%; + --nc: 0 0% 100%; + --b1: 0 0% 100%; + --b2: 25 20% 95%; + --b3: 25 20% 90%; + --bc: 25 20% 17%; + --in: 25 95% 53%; + --inc: 0 0% 100%; + --su: 142 76% 36%; + --suc: 0 0% 100%; + --wa: 38 92% 50%; + --wac: 0 0% 100%; + --er: 0 84% 60%; + --erc: 0 0% 100%; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: 0.2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; } -html[data-theme="sgse-red"], -html[data-theme="sgse-red"] body, -[data-theme="sgse-red"] { - color-scheme: light; - --p: 0 84% 60%; - --pf: 0 84% 50%; - --pc: 0 0% 100%; - --s: 0 84% 60%; - --sf: 0 84% 50%; - --sc: 0 0% 100%; - --a: 0 84% 60%; - --af: 0 84% 50%; - --ac: 0 0% 100%; - --n: 0 20% 17%; - --nf: 0 20% 10%; - --nc: 0 0% 100%; - --b1: 0 0% 100%; - --b2: 0 20% 95%; - --b3: 0 20% 90%; - --bc: 0 20% 17%; - --in: 0 84% 60%; - --inc: 0 0% 100%; - --su: 142 76% 36%; - --suc: 0 0% 100%; - --wa: 38 92% 50%; - --wac: 0 0% 100%; - --er: 0 84% 60%; - --erc: 0 0% 100%; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: 0.2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; +html[data-theme='sgse-red'], +html[data-theme='sgse-red'] body, +[data-theme='sgse-red'] { + color-scheme: light; + --p: 0 84% 60%; + --pf: 0 84% 50%; + --pc: 0 0% 100%; + --s: 0 84% 60%; + --sf: 0 84% 50%; + --sc: 0 0% 100%; + --a: 0 84% 60%; + --af: 0 84% 50%; + --ac: 0 0% 100%; + --n: 0 20% 17%; + --nf: 0 20% 10%; + --nc: 0 0% 100%; + --b1: 0 0% 100%; + --b2: 0 20% 95%; + --b3: 0 20% 90%; + --bc: 0 20% 17%; + --in: 0 84% 60%; + --inc: 0 0% 100%; + --su: 142 76% 36%; + --suc: 0 0% 100%; + --wa: 38 92% 50%; + --wac: 0 0% 100%; + --er: 0 84% 60%; + --erc: 0 0% 100%; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: 0.2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; } -html[data-theme="sgse-pink"], -html[data-theme="sgse-pink"] body, -[data-theme="sgse-pink"] { - color-scheme: light; - --p: 330 81% 60%; - --pf: 330 81% 50%; - --pc: 0 0% 100%; - --s: 330 81% 60%; - --sf: 330 81% 50%; - --sc: 0 0% 100%; - --a: 330 81% 60%; - --af: 330 81% 50%; - --ac: 0 0% 100%; - --n: 330 20% 17%; - --nf: 330 20% 10%; - --nc: 0 0% 100%; - --b1: 0 0% 100%; - --b2: 330 20% 95%; - --b3: 330 20% 90%; - --bc: 330 20% 17%; - --in: 330 81% 60%; - --inc: 0 0% 100%; - --su: 142 76% 36%; - --suc: 0 0% 100%; - --wa: 38 92% 50%; - --wac: 0 0% 100%; - --er: 0 84% 60%; - --erc: 0 0% 100%; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: 0.2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; +html[data-theme='sgse-pink'], +html[data-theme='sgse-pink'] body, +[data-theme='sgse-pink'] { + color-scheme: light; + --p: 330 81% 60%; + --pf: 330 81% 50%; + --pc: 0 0% 100%; + --s: 330 81% 60%; + --sf: 330 81% 50%; + --sc: 0 0% 100%; + --a: 330 81% 60%; + --af: 330 81% 50%; + --ac: 0 0% 100%; + --n: 330 20% 17%; + --nf: 330 20% 10%; + --nc: 0 0% 100%; + --b1: 0 0% 100%; + --b2: 330 20% 95%; + --b3: 330 20% 90%; + --bc: 330 20% 17%; + --in: 330 81% 60%; + --inc: 0 0% 100%; + --su: 142 76% 36%; + --suc: 0 0% 100%; + --wa: 38 92% 50%; + --wac: 0 0% 100%; + --er: 0 84% 60%; + --erc: 0 0% 100%; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: 0.2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; } -html[data-theme="sgse-teal"], -html[data-theme="sgse-teal"] body, -[data-theme="sgse-teal"] { - color-scheme: light; - --p: 173 80% 40%; - --pf: 173 80% 30%; - --pc: 0 0% 100%; - --s: 173 80% 40%; - --sf: 173 80% 30%; - --sc: 0 0% 100%; - --a: 173 80% 40%; - --af: 173 80% 30%; - --ac: 0 0% 100%; - --n: 173 20% 17%; - --nf: 173 20% 10%; - --nc: 0 0% 100%; - --b1: 0 0% 100%; - --b2: 173 20% 95%; - --b3: 173 20% 90%; - --bc: 173 20% 17%; - --in: 173 80% 40%; - --inc: 0 0% 100%; - --su: 142 76% 36%; - --suc: 0 0% 100%; - --wa: 38 92% 50%; - --wac: 0 0% 100%; - --er: 0 84% 60%; - --erc: 0 0% 100%; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: 0.2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; +html[data-theme='sgse-teal'], +html[data-theme='sgse-teal'] body, +[data-theme='sgse-teal'] { + color-scheme: light; + --p: 173 80% 40%; + --pf: 173 80% 30%; + --pc: 0 0% 100%; + --s: 173 80% 40%; + --sf: 173 80% 30%; + --sc: 0 0% 100%; + --a: 173 80% 40%; + --af: 173 80% 30%; + --ac: 0 0% 100%; + --n: 173 20% 17%; + --nf: 173 20% 10%; + --nc: 0 0% 100%; + --b1: 0 0% 100%; + --b2: 173 20% 95%; + --b3: 173 20% 90%; + --bc: 173 20% 17%; + --in: 173 80% 40%; + --inc: 0 0% 100%; + --su: 142 76% 36%; + --suc: 0 0% 100%; + --wa: 38 92% 50%; + --wac: 0 0% 100%; + --er: 0 84% 60%; + --erc: 0 0% 100%; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: 0.2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; } -html[data-theme="sgse-corporate"], -html[data-theme="sgse-corporate"] body, -[data-theme="sgse-corporate"] { - color-scheme: dark; - --p: 217 91% 60%; - --pf: 217 91% 50%; - --pc: 0 0% 100%; - --s: 217 91% 60%; - --sf: 217 91% 50%; - --sc: 0 0% 100%; - --a: 217 91% 60%; - --af: 217 91% 50%; - --ac: 0 0% 100%; - --n: 217 30% 15%; - --nf: 217 30% 8%; - --nc: 0 0% 100%; - --b1: 217 30% 10%; - --b2: 217 30% 15%; - --b3: 217 30% 20%; - --bc: 217 10% 90%; - --in: 217 91% 60%; - --inc: 0 0% 100%; - --su: 142 76% 36%; - --suc: 0 0% 100%; - --wa: 38 92% 50%; - --wac: 0 0% 100%; - --er: 0 84% 60%; - --erc: 0 0% 100%; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: 0.2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; +html[data-theme='sgse-corporate'], +html[data-theme='sgse-corporate'] body, +[data-theme='sgse-corporate'] { + color-scheme: dark; + --p: 217 91% 60%; + --pf: 217 91% 50%; + --pc: 0 0% 100%; + --s: 217 91% 60%; + --sf: 217 91% 50%; + --sc: 0 0% 100%; + --a: 217 91% 60%; + --af: 217 91% 50%; + --ac: 0 0% 100%; + --n: 217 30% 15%; + --nf: 217 30% 8%; + --nc: 0 0% 100%; + --b1: 217 30% 10%; + --b2: 217 30% 15%; + --b3: 217 30% 20%; + --bc: 217 10% 90%; + --in: 217 91% 60%; + --inc: 0 0% 100%; + --su: 142 76% 36%; + --suc: 0 0% 100%; + --wa: 38 92% 50%; + --wac: 0 0% 100%; + --er: 0 84% 60%; + --erc: 0 0% 100%; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: 0.2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; } /* Tema Light customizado para garantir funcionamento completo */ -html[data-theme="light"], -html[data-theme="light"] body, -[data-theme="light"] { - color-scheme: light; - --p: 217 91% 60%; - --pf: 217 91% 50%; - --pc: 0 0% 100%; - --s: 217 91% 60%; - --sf: 217 91% 50%; - --sc: 0 0% 100%; - --a: 217 91% 60%; - --af: 217 91% 50%; - --ac: 0 0% 100%; - --n: 217 20% 17%; - --nf: 217 20% 10%; - --nc: 0 0% 100%; - --b1: 0 0% 100%; - --b2: 217 20% 95%; - --b3: 217 20% 90%; - --bc: 217 20% 17%; - --in: 217 91% 60%; - --inc: 0 0% 100%; - --su: 142 76% 36%; - --suc: 0 0% 100%; - --wa: 38 92% 50%; - --wac: 0 0% 100%; - --er: 0 84% 60%; - --erc: 0 0% 100%; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: 0.2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; +html[data-theme='light'], +html[data-theme='light'] body, +[data-theme='light'] { + color-scheme: light; + --p: 217 91% 60%; + --pf: 217 91% 50%; + --pc: 0 0% 100%; + --s: 217 91% 60%; + --sf: 217 91% 50%; + --sc: 0 0% 100%; + --a: 217 91% 60%; + --af: 217 91% 50%; + --ac: 0 0% 100%; + --n: 217 20% 17%; + --nf: 217 20% 10%; + --nc: 0 0% 100%; + --b1: 0 0% 100%; + --b2: 217 20% 95%; + --b3: 217 20% 90%; + --bc: 217 20% 17%; + --in: 217 91% 60%; + --inc: 0 0% 100%; + --su: 142 76% 36%; + --suc: 0 0% 100%; + --wa: 38 92% 50%; + --wac: 0 0% 100%; + --er: 0 84% 60%; + --erc: 0 0% 100%; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: 0.2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; } /* Tema Dark customizado para garantir funcionamento completo */ -html[data-theme="dark"], -html[data-theme="dark"] body, -[data-theme="dark"] { - color-scheme: dark; - --p: 217 91% 60%; - --pf: 217 91% 50%; - --pc: 0 0% 100%; - --s: 217 91% 60%; - --sf: 217 91% 50%; - --sc: 0 0% 100%; - --a: 217 91% 60%; - --af: 217 91% 50%; - --ac: 0 0% 100%; - --n: 217 30% 15%; - --nf: 217 30% 8%; - --nc: 0 0% 100%; - --b1: 217 30% 10%; - --b2: 217 30% 15%; - --b3: 217 30% 20%; - --bc: 217 10% 90%; - --in: 217 91% 60%; - --inc: 0 0% 100%; - --su: 142 76% 36%; - --suc: 0 0% 100%; - --wa: 38 92% 50%; - --wac: 0 0% 100%; - --er: 0 84% 60%; - --erc: 0 0% 100%; - --rounded-box: 1rem; - --rounded-btn: 0.5rem; - --rounded-badge: 1.9rem; - --animation-btn: 0.25s; - --animation-input: 0.2s; - --btn-focus-scale: 0.95; - --border-btn: 1px; - --tab-border: 1px; - --tab-radius: 0.5rem; -} \ No newline at end of file +html[data-theme='dark'], +html[data-theme='dark'] body, +[data-theme='dark'] { + color-scheme: dark; + --p: 217 91% 60%; + --pf: 217 91% 50%; + --pc: 0 0% 100%; + --s: 217 91% 60%; + --sf: 217 91% 50%; + --sc: 0 0% 100%; + --a: 217 91% 60%; + --af: 217 91% 50%; + --ac: 0 0% 100%; + --n: 217 30% 15%; + --nf: 217 30% 8%; + --nc: 0 0% 100%; + --b1: 217 30% 10%; + --b2: 217 30% 15%; + --b3: 217 30% 20%; + --bc: 217 10% 90%; + --in: 217 91% 60%; + --inc: 0 0% 100%; + --su: 142 76% 36%; + --suc: 0 0% 100%; + --wa: 38 92% 50%; + --wac: 0 0% 100%; + --er: 0 84% 60%; + --erc: 0 0% 100%; + --rounded-box: 1rem; + --rounded-btn: 0.5rem; + --rounded-badge: 1.9rem; + --animation-btn: 0.25s; + --animation-input: 0.2s; + --btn-focus-scale: 0.95; + --border-btn: 1px; + --tab-border: 1px; + --tab-radius: 0.5rem; +} diff --git a/apps/web/src/app.d.ts b/apps/web/src/app.d.ts index 6633a80..2104354 100644 --- a/apps/web/src/app.d.ts +++ b/apps/web/src/app.d.ts @@ -1,9 +1,9 @@ declare global { - namespace App { - interface Locals { - token: string | undefined; - } - } + namespace App { + interface Locals { + token: string | undefined; + } + } } export {}; diff --git a/apps/web/src/app.html b/apps/web/src/app.html index 7732350..e912c97 100644 --- a/apps/web/src/app.html +++ b/apps/web/src/app.html @@ -10,9 +10,9 @@ - -{#if verificando} -
-
- -

Verificando permissões...

-
-
-{:else if permitido} - {@render children?.()} -{:else} -
-
-
- -
-

Acesso Negado

-

- Você não tem permissão para acessar esta ação. -

-
-
-{/if} + + +{#if verificando} +
+
+ +

Verificando permissões...

+
+
+{:else if permitido} + {@render children?.()} +{:else} +
+
+
+ +
+

Acesso Negado

+

Você não tem permissão para acessar esta ação.

+
+
+{/if} diff --git a/apps/web/src/lib/components/AlterarStatusFerias.svelte b/apps/web/src/lib/components/AlterarStatusFerias.svelte index 2eff5bb..54f979f 100644 --- a/apps/web/src/lib/components/AlterarStatusFerias.svelte +++ b/apps/web/src/lib/components/AlterarStatusFerias.svelte @@ -172,7 +172,8 @@

Cancelar Férias

- Ao cancelar as férias, o status será alterado para "Cancelado RH" e a solicitação não poderá mais ser processada. + Ao cancelar as férias, o status será alterado para "Cancelado RH" e a solicitação não + poderá mais ser processada.
@@ -208,7 +209,7 @@ xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" - class="stroke-current h-6 w-6 shrink-0" + class="h-6 w-6 shrink-0 stroke-current" > -
+
-

-
+

+
-
-

+

+

Nome

@@ -173,14 +173,14 @@ nome={solicitacao.funcionario?.nome || 'N/A'} size="md" /> -

+

{solicitacao.funcionario?.nome || 'N/A'}

{#if solicitacao.time} -
-

+

+

Time

-

-
+

+
Data Início
-
+
{new Date(solicitacao.dataInicio).toLocaleDateString('pt-BR')}
Data Fim
-
+
{new Date(solicitacao.dataFim).toLocaleDateString('pt-BR')}
Total de Dias
-
+
{totalDias}
dias corridos
@@ -251,8 +251,8 @@
-

-
+

+
Motivo da Ausência

-
+
-

+

{solicitacao.motivo}

@@ -280,9 +280,9 @@
-
+
- Status:
@@ -370,14 +370,14 @@ {#if motivoReprovacao !== undefined} -
+
diff --git a/apps/web/src/lib/components/AprovarFerias.svelte b/apps/web/src/lib/components/AprovarFerias.svelte index c68aabc..69b5cd0 100644 --- a/apps/web/src/lib/components/AprovarFerias.svelte +++ b/apps/web/src/lib/components/AprovarFerias.svelte @@ -70,10 +70,12 @@ const validacao = await client.query(api.saldoFerias.validarSolicitacao, { funcionarioId: periodo.funcionario._id, anoReferencia: periodo.anoReferencia, - periodos: [{ - dataInicio: periodo.dataInicio, - dataFim: periodo.dataFim - }] + periodos: [ + { + dataInicio: periodo.dataInicio, + dataFim: periodo.dataFim + } + ] }); if (!validacao.valido) { @@ -141,10 +143,12 @@ const validacao = await client.query(api.saldoFerias.validarSolicitacao, { funcionarioId: periodo.funcionario._id, anoReferencia: periodo.anoReferencia, - periodos: [{ - dataInicio: novaDataInicio, - dataFim: novaDataFim - }], + periodos: [ + { + dataInicio: novaDataInicio, + dataFim: novaDataFim + } + ], feriasIdExcluir: periodo._id // Excluir o período original do cálculo de saldo }); @@ -243,15 +247,11 @@
Início: - {formatarDataString(periodo.dataInicio)} + {formatarDataString(periodo.dataInicio)}
Fim: - {formatarDataString(periodo.dataFim)} + {formatarDataString(periodo.dataFim)}
Dias: diff --git a/apps/web/src/lib/components/CalendarioAfastamentos.svelte b/apps/web/src/lib/components/CalendarioAfastamentos.svelte index b419a67..2c2fbe6 100644 --- a/apps/web/src/lib/components/CalendarioAfastamentos.svelte +++ b/apps/web/src/lib/components/CalendarioAfastamentos.svelte @@ -1,540 +1,528 @@
-
- -
-

Calendário de Afastamentos

+
+ +
+

Calendário de Afastamentos

- -
- Filtrar: -
- - - - - - -
-
-
+ +
+ Filtrar: +
+ + + + + + +
+
+
- -
-
-
- Atestado Médico -
-
-
- Declaração -
-
-
- Licença Maternidade -
-
-
- Licença Paternidade -
-
-
- Férias -
-
+ +
+
+
+ Atestado Médico +
+
+
+ Declaração +
+
+
+ Licença Maternidade +
+
+
+ Licença Paternidade +
+
+
+ Férias +
+
- -
-
-
+ +
+
+
- - {#if showModal && eventoSelecionado} - - -
(showModal = false)} - role="dialog" - aria-modal="true" - > - -
e.stopPropagation()} - > - -
-
-
-

- {eventoSelecionado.funcionarioNome} -

-

- {getTipoNome(eventoSelecionado.tipo)} -

-
- -
-
+ + {#if showModal && eventoSelecionado} + + +
(showModal = false)} + role="dialog" + aria-modal="true" + > + +
e.stopPropagation()} + > + +
+
+
+

+ {eventoSelecionado.funcionarioNome} +

+

+ {getTipoNome(eventoSelecionado.tipo)} +

+
+ +
+
- -
-
- - - -
-

Data Início

-

- {formatarData(eventoSelecionado.start)} -

-
-
+ +
+
+ + + +
+

Data Início

+

+ {formatarData(eventoSelecionado.start)} +

+
+
-
- - - -
-

Data Fim

-

- {formatarData(eventoSelecionado.end)} -

-
-
+
+ + + +
+

Data Fim

+

+ {formatarData(eventoSelecionado.end)} +

+
+
-
- - - -
-

Duração

-

- {(() => { - const inicio = new Date(eventoSelecionado.start); - const fim = new Date(eventoSelecionado.end); - const diffTime = Math.abs(fim.getTime() - inicio.getTime()); - const diffDays = - Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1; - return `${diffDays} ${diffDays === 1 ? "dia" : "dias"}`; - })()} -

-
-
-
+
+ + + +
+

Duração

+

+ {(() => { + const inicio = new Date(eventoSelecionado.start); + const fim = new Date(eventoSelecionado.end); + const diffTime = Math.abs(fim.getTime() - inicio.getTime()); + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1; + return `${diffDays} ${diffDays === 1 ? 'dia' : 'dias'}`; + })()} +

+
+
+
- -
- -
-
-
- {/if} -
+ +
+ +
+
+
+ {/if} +
diff --git a/apps/web/src/lib/components/ErrorModal.svelte b/apps/web/src/lib/components/ErrorModal.svelte index c1cb5a0..a9feb63 100644 --- a/apps/web/src/lib/components/ErrorModal.svelte +++ b/apps/web/src/lib/components/ErrorModal.svelte @@ -10,34 +10,34 @@ } let { open = $bindable(false), title = 'Erro', message, details, onClose }: Props = $props(); - + let modalPosition = $state<{ top: number; left: number } | null>(null); // Função para calcular a posição baseada no card de registro de ponto function calcularPosicaoModal() { // Procurar pelo elemento do card de registro de ponto const cardRef = document.getElementById('card-registro-ponto-ref'); - + if (cardRef) { const rect = cardRef.getBoundingClientRect(); const viewportHeight = window.innerHeight; - + // Posicionar o modal na mesma altura Y do card (top do card) - mesma posição do texto "Registrar Ponto" const top = rect.top; - + // Garantir que o modal não saia da viewport // Considerar uma altura mínima do modal (aproximadamente 300px) const minTop = 20; const maxTop = viewportHeight - 350; // Deixar espaço para o modal const finalTop = Math.max(minTop, Math.min(top, maxTop)); - + // Centralizar horizontalmente return { top: finalTop, left: window.innerWidth / 2 }; } - + // Se não encontrar, usar posição padrão (centro da tela) return null; } @@ -53,18 +53,18 @@ } }); }; - + // Aguardar um pouco mais para garantir que o DOM está atualizado setTimeout(updatePosition, 50); - + // Adicionar listener de scroll para atualizar posição const handleScroll = () => { updatePosition(); }; - + window.addEventListener('scroll', handleScroll, true); window.addEventListener('resize', handleScroll); - + return () => { window.removeEventListener('scroll', handleScroll, true); window.removeEventListener('resize', handleScroll); @@ -85,17 +85,19 @@ // Se não houver posição calculada, centralizar na tela return 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 100%; max-width: 700px;'; } - + // Verificar se details contém instruções ou apenas detalhes técnicos const temInstrucoes = $derived.by(() => { if (!details) return false; // Se contém palavras-chave de instruções, é uma instrução - return details.includes('Por favor') || - details.includes('aguarde') || - details.includes('recarregue') || - details.includes('Verifique') || - details.includes('tente novamente') || - details.match(/^\d+\./); // Começa com número (lista numerada) + return ( + details.includes('Por favor') || + details.includes('aguarde') || + details.includes('recarregue') || + details.includes('Verifique') || + details.includes('tente novamente') || + details.match(/^\d+\./) + ); // Começa com número (lista numerada) }); function handleClose() { @@ -105,27 +107,29 @@ {#if open} - diff --git a/apps/web/src/lib/components/chat/PresenceManager.svelte b/apps/web/src/lib/components/chat/PresenceManager.svelte index 7c84e6b..0227d5d 100644 --- a/apps/web/src/lib/components/chat/PresenceManager.svelte +++ b/apps/web/src/lib/components/chat/PresenceManager.svelte @@ -1,114 +1,119 @@ diff --git a/apps/web/src/lib/components/chat/UserStatusBadge.svelte b/apps/web/src/lib/components/chat/UserStatusBadge.svelte index 2e1999e..0a1e86a 100644 --- a/apps/web/src/lib/components/chat/UserStatusBadge.svelte +++ b/apps/web/src/lib/components/chat/UserStatusBadge.svelte @@ -1,75 +1,74 @@
- {@html config.icon} + {@html config.icon}
- diff --git a/apps/web/src/lib/components/ferias/WizardSolicitacaoFerias.svelte b/apps/web/src/lib/components/ferias/WizardSolicitacaoFerias.svelte index c748d87..38e2762 100644 --- a/apps/web/src/lib/components/ferias/WizardSolicitacaoFerias.svelte +++ b/apps/web/src/lib/components/ferias/WizardSolicitacaoFerias.svelte @@ -127,7 +127,9 @@ // Verificar se o total não excede 30 dias const novoTotal = totalDiasSelecionados + dias; if (novoTotal > 30) { - toast.error(`O total não pode exceder 30 dias. Você já tem ${totalDiasSelecionados} dias, adicionando ${dias} dias totalizaria ${novoTotal} dias.`); + toast.error( + `O total não pode exceder 30 dias. Você já tem ${totalDiasSelecionados} dias, adicionando ${dias} dias totalizaria ${novoTotal} dias.` + ); return; } } @@ -135,7 +137,9 @@ // Verificar se o total não excede o saldo disponível const novoTotal = totalDiasSelecionados + dias; if (saldo && novoTotal > saldo.diasDisponiveis) { - toast.error(`Total de dias (${novoTotal}) excede saldo disponível (${saldo.diasDisponiveis})`); + toast.error( + `Total de dias (${novoTotal}) excede saldo disponível (${saldo.diasDisponiveis})` + ); return; } @@ -149,7 +153,7 @@ ]; toast.success(`Período de ${dias} dias adicionado! ✅`); - + // Limpar campos dataInicioPeriodo = ''; dataFimPeriodo = ''; @@ -263,14 +267,17 @@
-

+

{labels[i]}

{#if i < totalPassos - 1}
i + 1} class:bg-base-300={passoAtual <= i + 1} @@ -303,7 +310,9 @@ style:border-width={anoSelecionado === ano ? '2px' : undefined} style:color={anoSelecionado === ano ? '#000000' : undefined} style:background-color={anoSelecionado === ano ? 'transparent' : undefined} - style:box-shadow={anoSelecionado === ano ? '0 0 10px rgba(249, 115, 22, 0.3)' : undefined} + style:box-shadow={anoSelecionado === ano + ? '0 0 10px rgba(249, 115, 22, 0.3)' + : undefined} onclick={() => (anoSelecionado = ano)} > {ano} @@ -413,7 +422,8 @@

{#if ehEstatutarioPEOuMunicipal}

- ⚠️ Regras: Períodos de 15 ou 30 dias. Máximo 2 períodos. Total não pode exceder 30 dias. + ⚠️ Regras: Períodos de 15 ou 30 dias. Máximo 2 períodos. Total não pode + exceder 30 dias.

{/if}
@@ -494,27 +504,24 @@

{#if ehEstatutarioPEOuMunicipal}

- ⚠️ Regras: Períodos de 15 ou 30 dias. Máximo 2 períodos. Total não pode exceder 30 dias. + ⚠️ Regras: Períodos de 15 ou 30 dias. Máximo 2 períodos. Total não pode exceder 30 + dias.

{/if}
-
+

Adicionar Período

- +
- +
@@ -534,7 +541,7 @@ Dias
- {diasPeriodoAtual} + {diasPeriodoAtual} dias
@@ -569,7 +576,7 @@ {#if periodosFerias.length > 0} -
+

Períodos Adicionados ({periodosFerias.length})

diff --git a/apps/web/src/lib/components/ponto/ComprovantePonto.svelte b/apps/web/src/lib/components/ponto/ComprovantePonto.svelte index 721b06b..a775954 100644 --- a/apps/web/src/lib/components/ponto/ComprovantePonto.svelte +++ b/apps/web/src/lib/components/ponto/ComprovantePonto.svelte @@ -25,27 +25,27 @@ function calcularPosicaoModal() { // Procurar pelo elemento do card de registro de ponto const cardRef = document.getElementById('card-registro-ponto-ref'); - + if (cardRef) { const rect = cardRef.getBoundingClientRect(); const viewportHeight = window.innerHeight; - + // Posicionar o modal na mesma altura Y do card (top do card) - mesma posição do texto "Registrar Ponto" const top = rect.top; - + // Garantir que o modal não saia da viewport // Considerar uma altura mínima do modal (aproximadamente 300px) const minTop = 20; const maxTop = viewportHeight - 350; // Deixar espaço para o modal const finalTop = Math.max(minTop, Math.min(top, maxTop)); - + // Centralizar horizontalmente return { top: finalTop, left: window.innerWidth / 2 }; } - + // Se não encontrar, usar posição padrão (centro da tela) return null; } @@ -53,37 +53,37 @@ // Atualizar posição quando o modal for aberto (quando registroQuery tiver dados) $effect(() => { if (registroQuery?.data) { - // Usar requestAnimationFrame para garantir que o DOM está completamente renderizado - const updatePosition = () => { - requestAnimationFrame(() => { - const pos = calcularPosicaoModal(); - if (pos) { - modalPosition = pos; + // Usar requestAnimationFrame para garantir que o DOM está completamente renderizado + const updatePosition = () => { + requestAnimationFrame(() => { + const pos = calcularPosicaoModal(); + if (pos) { + modalPosition = pos; } else { // Fallback para centralização modalPosition = { top: window.innerHeight / 2, left: window.innerWidth / 2 }; - } - }); - }; - + } + }); + }; + // Aguardar um pouco para garantir que o DOM está atualizado - setTimeout(updatePosition, 50); - - // Adicionar listener de scroll para atualizar posição - const handleScroll = () => { - updatePosition(); - }; - - window.addEventListener('scroll', handleScroll, true); - window.addEventListener('resize', handleScroll); - - return () => { - window.removeEventListener('scroll', handleScroll, true); - window.removeEventListener('resize', handleScroll); - }; + setTimeout(updatePosition, 50); + + // Adicionar listener de scroll para atualizar posição + const handleScroll = () => { + updatePosition(); + }; + + window.addEventListener('scroll', handleScroll, true); + window.addEventListener('resize', handleScroll); + + return () => { + window.removeEventListener('scroll', handleScroll, true); + window.removeEventListener('resize', handleScroll); + }; } else { // Limpar posição quando o modal for fechado modalPosition = null; @@ -137,7 +137,9 @@ doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.setTextColor(0, 0, 0); - doc.text('GOVERNO DO ESTADO DE PERNAMBUCO', 105, Math.max(yPosition - 10, 20), { align: 'center' }); + doc.text('GOVERNO DO ESTADO DE PERNAMBUCO', 105, Math.max(yPosition - 10, 20), { + align: 'center' + }); doc.setFontSize(12); doc.text('SECRETARIA DE ESPORTES', 105, Math.max(yPosition - 2, 28), { align: 'center' }); @@ -154,7 +156,7 @@ // Informações do Funcionário em tabela const funcionarioData: string[][] = []; - + if (registro.funcionario) { if (registro.funcionario.matricula) { funcionarioData.push(['Matrícula', registro.funcionario.matricula]); @@ -164,10 +166,14 @@ funcionarioData.push(['Cargo/Função', registro.funcionario.descricaoCargo]); } if (registro.funcionario.simbolo) { - const simboloTipo = registro.funcionario.simbolo.tipo === 'cargo_comissionado' - ? 'Cargo Comissionado' - : 'Função Gratificada'; - funcionarioData.push(['Símbolo', `${registro.funcionario.simbolo.nome} (${simboloTipo})`]); + const simboloTipo = + registro.funcionario.simbolo.tipo === 'cargo_comissionado' + ? 'Cargo Comissionado' + : 'Função Gratificada'; + funcionarioData.push([ + 'Símbolo', + `${registro.funcionario.simbolo.nome} (${simboloTipo})` + ]); } } @@ -202,12 +208,17 @@ nomeEntrada: config.nomeEntrada, nomeSaidaAlmoco: config.nomeSaidaAlmoco, nomeRetornoAlmoco: config.nomeRetornoAlmoco, - nomeSaida: config.nomeSaida, + nomeSaida: config.nomeSaida }) : getTipoRegistroLabel(registro.tipo); - - const dataHora = formatarDataHoraCompleta(registro.data, registro.hora, registro.minuto, registro.segundo); - + + const dataHora = formatarDataHoraCompleta( + registro.data, + registro.hora, + registro.minuto, + registro.segundo + ); + const registroData: string[][] = [ ['Tipo', tipoLabel], ['Data e Hora', dataHora], @@ -260,10 +271,10 @@ if (!response.ok) { throw new Error('Erro ao carregar imagem'); } - + const blob = await response.blob(); const reader = new FileReader(); - + // Converter blob para base64 const base64 = await new Promise((resolve, reject) => { reader.onloadend = () => { @@ -307,7 +318,7 @@ // Centralizar imagem const xPosition = (doc.internal.pageSize.getWidth() - imgWidth) / 2; - + // Verificar se cabe na página atual if (yPosition + imgHeight > doc.internal.pageSize.getHeight() - 20) { doc.addPage(); @@ -351,46 +362,53 @@ } -
e.key === 'Escape' && onClose()} - role="dialog" - aria-modal="true" - aria-labelledby="modal-comprovante-title" - > + role="dialog" + aria-modal="true" + aria-labelledby="modal-comprovante-title" +> -
- + -
e.stopPropagation()} > -
+
-
- +
+
- -

Detalhes do registro realizado

+ +

Detalhes do registro realizado

-
-

- Acompanhe e gerencie os fluxos de trabalho. Visualize o progresso, - documentos e responsáveis de cada etapa. + Acompanhe e gerencie os fluxos de trabalho. Visualize o progresso, documentos e + responsáveis de cada etapa.

- @@ -187,7 +195,9 @@

Nenhum fluxo encontrado

- {statusFilter ? 'Não há fluxos com este status.' : 'Clique em "Novo Fluxo" para iniciar um fluxo.'} + {statusFilter + ? 'Não há fluxos com este status.' + : 'Clique em "Novo Fluxo" para iniciar um fluxo.'}

{:else} @@ -207,7 +217,10 @@ {#each instancesQuery.data as instance (instance._id)} {@const statusBadge = getStatusBadge(instance.status)} - {@const progressPercent = getProgressPercentage(instance.progress.completed, instance.progress.total)} + {@const progressPercent = getProgressPercentage( + instance.progress.completed, + instance.progress.total + )}
{instance.templateName ?? 'Template desconhecido'}
@@ -222,12 +235,9 @@ {instance.managerName ?? '-'}
- - + {instance.progress.completed}/{instance.progress.total}
@@ -237,13 +247,27 @@ {formatDate(instance.startedAt)} - -
{/if} -
{ e.preventDefault(); handleCreate(); }} class="mt-4 space-y-4"> + { + e.preventDefault(); + handleCreate(); + }} + class="mt-4 space-y-4" + >
@@ -323,7 +355,9 @@ {/if}

- Opcional: vincule este fluxo a um contrato específico + Opcional: vincule este fluxo a um contrato específico

@@ -359,7 +393,11 @@
- +
{/if} - diff --git a/apps/web/src/routes/(dashboard)/licitacoes/fluxos/[id]/+page.svelte b/apps/web/src/routes/(dashboard)/licitacoes/fluxos/[id]/+page.svelte index f8fd33e..9d89d51 100644 --- a/apps/web/src/routes/(dashboard)/licitacoes/fluxos/[id]/+page.svelte +++ b/apps/web/src/routes/(dashboard)/licitacoes/fluxos/[id]/+page.svelte @@ -16,17 +16,16 @@ // Estado para query de usuários por setor let setorIdParaAtribuicao = $state | null>(null); - + // Query de usuários por setor para atribuição (reativa baseada no setorId) - const usuariosPorSetorQuery = useQuery( - api.flows.getUsuariosBySetorForAssignment, - () => setorIdParaAtribuicao ? { setorId: setorIdParaAtribuicao } : 'skip' + const usuariosPorSetorQuery = useQuery(api.flows.getUsuariosBySetorForAssignment, () => + setorIdParaAtribuicao ? { setorId: setorIdParaAtribuicao } : 'skip' ); // Estado de operações let isProcessing = $state(false); let processingError = $state(null); - + // Estado de toast let toastMessage = $state(null); let toastType = $state<'success' | 'error' | 'warning' | 'info'>('error'); @@ -38,7 +37,11 @@ // Modal de notas let showNotesModal = $state(false); - let stepForNotes = $state<{ _id: Id<'flowInstanceSteps'>; stepName: string; notes: string } | null>(null); + let stepForNotes = $state<{ + _id: Id<'flowInstanceSteps'>; + stepName: string; + notes: string; + } | null>(null); let editedNotes = $state(''); // Modal de upload @@ -114,7 +117,12 @@ } } - function openReassignModal(step: { _id: Id<'flowInstanceSteps'>; stepName: string; assignedToId?: Id<'usuarios'>; setorId: Id<'setores'> }) { + function openReassignModal(step: { + _id: Id<'flowInstanceSteps'>; + stepName: string; + assignedToId?: Id<'usuarios'>; + setorId: Id<'setores'>; + }) { stepToReassign = step; newAssigneeId = step.assignedToId ?? ''; setorIdParaAtribuicao = step.setorId; @@ -161,7 +169,11 @@ } } - function openNotesModal(step: { _id: Id<'flowInstanceSteps'>; stepName: string; notes?: string }) { + function openNotesModal(step: { + _id: Id<'flowInstanceSteps'>; + stepName: string; + notes?: string; + }) { stepForNotes = { ...step, notes: step.notes ?? '' }; editedNotes = step.notes ?? ''; showNotesModal = true; @@ -366,10 +378,14 @@ // Funções de sub-etapas function getFileIcon(fileName: string): string { const ext = fileName.split('.').pop()?.toLowerCase(); - if (['pdf'].includes(ext || '')) return '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'; - if (['doc', 'docx'].includes(ext || '')) return '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'; - if (['xls', 'xlsx'].includes(ext || '')) return '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'; - if (['jpg', 'jpeg', 'png', 'gif'].includes(ext || '')) return 'M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z'; + if (['pdf'].includes(ext || '')) + return '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'; + if (['doc', 'docx'].includes(ext || '')) + return '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'; + if (['xls', 'xlsx'].includes(ext || '')) + return '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'; + if (['jpg', 'jpeg', 'png', 'gif'].includes(ext || '')) + return 'M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z'; return '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'; } @@ -443,7 +459,10 @@ } } - async function handleAtualizarStatusSubEtapa(subEtapaId: Id<'flowSubSteps'>, novoStatus: 'pending' | 'in_progress' | 'completed' | 'blocked') { + async function handleAtualizarStatusSubEtapa( + subEtapaId: Id<'flowSubSteps'>, + novoStatus: 'pending' | 'in_progress' | 'completed' | 'blocked' + ) { try { await client.mutation(api.flows.atualizarSubEtapa, { subEtapaId, @@ -466,11 +485,25 @@
{:else if !instanceQuery.data} {:else} {@const instance = instanceQuery.data.instance} @@ -482,9 +515,11 @@ class="border-info/25 from-info/10 via-base-100 to-secondary/20 relative overflow-hidden rounded-3xl border bg-linear-to-br p-8 shadow-2xl" >
-
+
-
+
@@ -517,9 +564,21 @@ {instance.contratoId}
{/if} -
-
+
Gerente: {instance.managerName ?? '-'} @@ -529,24 +588,54 @@ onclick={openAlterarGestorModal} aria-label="Alterar gestor" > -
-
-
+ Iniciado: {formatDate(instance.startedAt)}
@@ -555,9 +644,21 @@ {#if instance.status === 'active'} - +
{/if} @@ -587,26 +700,66 @@ {@const stepStatus = getStatusBadge(step.status)} {@const isCurrent = isStepCurrent(step._id)} {@const overdue = step.status !== 'completed' && isOverdue(step.dueDate)} - {@const subEtapasQuery = useQuery(api.flows.listarSubEtapas, () => ({ flowInstanceStepId: step._id }))} + {@const subEtapasQuery = useQuery(api.flows.listarSubEtapas, () => ({ + flowInstanceStepId: step._id + }))} {@const subEtapas = subEtapasQuery.data} {@const subEtapasCount = subEtapas?.length ?? 0} - {@const subEtapasCompleted = subEtapas?.filter((s: { status: string }) => s.status === 'completed').length ?? 0} + {@const subEtapasCompleted = + subEtapas?.filter((s: { status: string }) => s.status === 'completed').length ?? 0}
{#if index < steps.length - 1} -
+
{/if} -
+
{#if step.status === 'completed'} - {index + 1} @@ -614,11 +767,15 @@
-
+
-
-
+
+

{step.stepName}

{stepStatus.label} - {#if step.assignedToName} - {/if} {#if step.dueDate} - - + Prazo: {formatDate(step.dueDate)} @@ -707,8 +904,20 @@ aria-label="Reatribuir responsável" title="Reatribuir responsável" > -
-
-
+
+
-

Sub-etapas

+

Sub-etapas

{#if subEtapasCount > 0} {subEtapasCompleted} / {subEtapasCount} concluídas @@ -737,14 +958,26 @@ onclick={() => openSubEtapaModal(step._id)} aria-label="Adicionar sub-etapa" > -
- + {#if subEtapasQuery.isLoading}
@@ -752,24 +985,50 @@ {:else if subEtapas && subEtapas.length > 0}
{#each subEtapas as subEtapa (subEtapa._id)} -
-
-
-
{subEtapa.name}
- - {subEtapa.status === 'completed' ? 'Concluída' : subEtapa.status === 'in_progress' ? 'Em Andamento' : subEtapa.status === 'blocked' ? 'Bloqueada' : 'Pendente'} +
+
+
+
{subEtapa.name}
+ + {subEtapa.status === 'completed' + ? 'Concluída' + : subEtapa.status === 'in_progress' + ? 'Em Andamento' + : subEtapa.status === 'blocked' + ? 'Bloqueada' + : 'Pendente'}
{#if subEtapa.description} -
{subEtapa.description}
+
+ {subEtapa.description} +
{/if}
-
+
{#if instance.status === 'active'} {:else if usuariosPorSetorQuery.data && usuariosPorSetorQuery.data.length === 0}
- Não há funcionários cadastrados neste setor
@@ -1125,10 +1591,12 @@
- +
{/if} @@ -1163,9 +1636,7 @@
- +
{/if} @@ -1206,10 +1678,12 @@ {/if}
- +
{/if} @@ -1225,16 +1704,14 @@ {#if showCancelModal} {/if} - diff --git a/apps/web/src/routes/(dashboard)/perfil/+page.svelte b/apps/web/src/routes/(dashboard)/perfil/+page.svelte index 92059d1..b994967 100644 --- a/apps/web/src/routes/(dashboard)/perfil/+page.svelte +++ b/apps/web/src/routes/(dashboard)/perfil/+page.svelte @@ -129,14 +129,13 @@ const gestorIdDisponivel = $derived(currentUser?.data?._id ?? null); // Verificar autenticação antes de executar queries - const usuarioAutenticado = $derived(currentUser?.data !== null && currentUser?.data !== undefined); + const usuarioAutenticado = $derived( + currentUser?.data !== null && currentUser?.data !== undefined + ); // ✅ CORRIGIDO: Queries condicionais - só executar se usuário estiver autenticado // Queries que não requerem argumentos são criadas uma vez - const funcionarioQuery = useQuery( - api.funcionarios.getCurrent, - usuarioAutenticado ? {} : 'skip' - ); + const funcionarioQuery = useQuery(api.funcionarios.getCurrent, usuarioAutenticado ? {} : 'skip'); const timesSubordinadosQuery = useQuery( api.times.listarSubordinadosDoGestorAtual, usuarioAutenticado ? {} : 'skip' @@ -604,8 +603,9 @@ // Garantir que o tema continue aplicado após salvar aplicarTema(temaSelecionado); - sucessoSalvarTema = 'Tema salvo com sucesso! Sua preferência será aplicada em acessos futuros.'; - + sucessoSalvarTema = + 'Tema salvo com sucesso! Sua preferência será aplicada em acessos futuros.'; + // Limpar mensagem após 3 segundos setTimeout(() => { sucessoSalvarTema = null; @@ -2216,18 +2216,18 @@ - -
- -
- {periodo.funcionario?.nome} + +
+ +
+ {periodo.funcionario?.nome} +
-
- + {#if periodo.time}
- -
- -
- {ausencia.funcionario?.nome || 'N/A'} + +
+ +
+ {ausencia.funcionario?.nome || 'N/A'} +
-
- + {#if ausencia.time}
selecionarTema(tema.id)} @@ -2516,9 +2516,7 @@ style="background: linear-gradient(135deg, {tema.corPrimaria} 0%, {tema.corSecundaria} 100%);" >
-
+
@@ -2566,9 +2564,9 @@

Como funciona?

- Clique em um tema para visualizar a prévia. O tema será aplicado - imediatamente, mas você precisa clicar em "Salvar Tema" para que a - preferência seja mantida em acessos futuros. + Clique em um tema para visualizar a prévia. O tema será aplicado imediatamente, mas + você precisa clicar em "Salvar Tema" para que a preferência seja mantida em acessos + futuros.

diff --git a/apps/web/src/routes/(dashboard)/perfil/chamados/+page.svelte b/apps/web/src/routes/(dashboard)/perfil/chamados/+page.svelte index 6c06919..21c0124 100644 --- a/apps/web/src/routes/(dashboard)/perfil/chamados/+page.svelte +++ b/apps/web/src/routes/(dashboard)/perfil/chamados/+page.svelte @@ -1,326 +1,323 @@
-
-
-
-

Meu Perfil

-

Meus Chamados

-

- Acompanhe o status, interaja com a equipe de TI e visualize a timeline de SLA em tempo real. -

-
-
- Abrir novo chamado - -
-
-
+
+
+
+

Meu Perfil

+

Meus Chamados

+

+ Acompanhe o status, interaja com a equipe de TI e visualize a timeline de SLA em tempo + real. +

+
+
+ Abrir novo chamado + +
+
+
-
- -
- {#if !selectedTicketId || !detalheAtual} -
- {#if carregandoDetalhe} - - {:else} -

Selecione um chamado para visualizar os detalhes.

- {/if} -
- {:else} -
-
-

Ticket {detalheAtual.ticket.numero}

-

{detalheAtual.ticket.titulo}

-

{detalheAtual.ticket.descricao}

-
- - {getStatusLabel(detalheAtual.ticket.status)} - -
+
+ {#if !selectedTicketId || !detalheAtual} +
+ {#if carregandoDetalhe} + + {:else} +

Selecione um chamado para visualizar os detalhes.

+ {/if} +
+ {:else} +
+
+

+ Ticket {detalheAtual.ticket.numero} +

+

{detalheAtual.ticket.titulo}

+

{detalheAtual.ticket.descricao}

+
+ + {getStatusLabel(detalheAtual.ticket.status)} + +
-
- - Tipo: {detalheAtual.ticket.tipo.charAt(0).toUpperCase() + detalheAtual.ticket.tipo.slice(1)} - - - Prioridade: {detalheAtual.ticket.prioridade} - - - Última interação: {formatarData(detalheAtual.ticket.ultimaInteracaoEm)} - -
+
+ + Tipo: {detalheAtual.ticket.tipo.charAt(0).toUpperCase() + + detalheAtual.ticket.tipo.slice(1)} + + + Prioridade: {detalheAtual.ticket.prioridade} + + + Última interação: {formatarData(detalheAtual.ticket.ultimaInteracaoEm)} + +
- {#if statusAlertas(detalheAtual.ticket).length > 0} -
- {#each statusAlertas(detalheAtual.ticket) as alerta (alerta.label)} -
- {alerta.label} -
- {/each} -
- {/if} + {#if statusAlertas(detalheAtual.ticket).length > 0} +
+ {#each statusAlertas(detalheAtual.ticket) as alerta (alerta.label)} +
+ {alerta.label} +
+ {/each} +
+ {/if} -
-
-

Timeline e SLA

-

- Etapas monitoradas com indicadores de prazo. -

-
- -
-
+
+
+

Timeline e SLA

+

Etapas monitoradas com indicadores de prazo.

+
+ +
+
-
-

Responsabilidade

-

- {detalheAtual.ticket.responsavelId - ? `Responsável: ${detalheAtual.ticket.setorResponsavel ?? "Equipe TI"}` - : "Aguardando atribuição"} -

-
-

Prazo resposta: {prazoRestante(detalheAtual.ticket.prazoResposta) ?? "--"}

-

Prazo conclusão: {prazoRestante(detalheAtual.ticket.prazoConclusao) ?? "--"}

-

Prazo encerramento: {prazoRestante(detalheAtual.ticket.prazoEncerramento) ?? "--"}

-
-

- {getStatusDescription(detalheAtual.ticket.status)} -

-
-
+
+

Responsabilidade

+

+ {detalheAtual.ticket.responsavelId + ? `Responsável: ${detalheAtual.ticket.setorResponsavel ?? 'Equipe TI'}` + : 'Aguardando atribuição'} +

+
+

Prazo resposta: {prazoRestante(detalheAtual.ticket.prazoResposta) ?? '--'}

+

Prazo conclusão: {prazoRestante(detalheAtual.ticket.prazoConclusao) ?? '--'}

+

+ Prazo encerramento: {prazoRestante(detalheAtual.ticket.prazoEncerramento) ?? '--'} +

+
+

+ {getStatusDescription(detalheAtual.ticket.status)} +

+
+
-
-
-

Interações

-
- {#if detalheAtual.interactions.length === 0} -

- Nenhuma interação registrada ainda. -

- {:else} - {#each detalheAtual.interactions as interacao (interacao._id)} -
-
- {interacao.origem === "usuario" ? "Você" : interacao.origem} - {formatarData(interacao.criadoEm)} -
-

- {interacao.conteudo} -

- {#if interacao.statusNovo && interacao.statusNovo !== interacao.statusAnterior} - - Status: {getStatusLabel(interacao.statusNovo)} - - {/if} -
- {/each} - {/if} -
-
+
+
+

Interações

+
+ {#if detalheAtual.interactions.length === 0} +

Nenhuma interação registrada ainda.

+ {:else} + {#each detalheAtual.interactions as interacao (interacao._id)} +
+
+ {interacao.origem === 'usuario' ? 'Você' : interacao.origem} + {formatarData(interacao.criadoEm)} +
+

+ {interacao.conteudo} +

+ {#if interacao.statusNovo && interacao.statusNovo !== interacao.statusAnterior} + + Status: {getStatusLabel(interacao.statusNovo)} + + {/if} +
+ {/each} + {/if} +
+
-
-

Enviar atualização

- - {#if erroMensagem} -

{erroMensagem}

- {/if} - {#if sucessoMensagem} -

{sucessoMensagem}

- {/if} - -
-
- {/if} -
-
+
+

Enviar atualização

+ + {#if erroMensagem} +

{erroMensagem}

+ {/if} + {#if sucessoMensagem} +

{sucessoMensagem}

+ {/if} + +
+
+ {/if} + +
- diff --git a/apps/web/src/routes/(dashboard)/perfil/privacidade/+page.svelte b/apps/web/src/routes/(dashboard)/perfil/privacidade/+page.svelte index 2994ed5..6128474 100644 --- a/apps/web/src/routes/(dashboard)/perfil/privacidade/+page.svelte +++ b/apps/web/src/routes/(dashboard)/perfil/privacidade/+page.svelte @@ -22,7 +22,7 @@ return labels[tipo] || tipo; } - async function revogar(tipo: string) { + async function revogar(tipo: string) { if (!confirm('Tem certeza que deseja revogar este consentimento?')) { return; } @@ -30,7 +30,13 @@ revogando = tipo; try { - await client.mutation(api.lgpd.revogarConsentimento, { tipo: tipo as 'termo_uso' | 'politica_privacidade' | 'comunicacoes' | 'compartilhamento_dados' }); + await client.mutation(api.lgpd.revogarConsentimento, { + tipo: tipo as + | 'termo_uso' + | 'politica_privacidade' + | 'comunicacoes' + | 'compartilhamento_dados' + }); toast.success('Consentimento revogado com sucesso'); } catch (error: any) { toast.error(error.message || 'Erro ao revogar consentimento'); @@ -40,15 +46,15 @@ } -
+
-
-
- +
+
+
-

Preferências de Privacidade

+

Preferências de Privacidade

Gerencie seus consentimentos e preferências de privacidade

@@ -57,32 +63,32 @@
-
+
-

Meus Consentimentos

+

Meus Consentimentos

{#if consentimentos === undefined} -
+
{:else if consentimentos.length === 0} -
- +
+

Nenhum consentimento registrado

{:else}
{#each consentimentos as consentimento} -
+
-
+
{#if consentimento.aceito && !consentimento.revogadoEm} - + {:else} - + {/if} -

+

{getTipoLabel(consentimento.tipo)}

{#if consentimento.aceito && !consentimento.revogadoEm} @@ -92,7 +98,7 @@ {/if}
-
+
@@ -103,18 +109,17 @@
- Versão: {consentimento.versao} + Versão: + {consentimento.versao}
{#if consentimento.revogadoEm} -
+
Revogado em:{' '} - {format( - new Date(consentimento.revogadoEm), - 'dd/MM/yyyy às HH:mm', - { locale: ptBR } - )} + {format(new Date(consentimento.revogadoEm), 'dd/MM/yyyy às HH:mm', { + locale: ptBR + })}
{/if} @@ -145,42 +150,42 @@
-

Informações Importantes

+

Informações Importantes

Atenção ao Revogar Consentimentos

- A revogação de alguns consentimentos pode impedir o acesso a funcionalidades do - sistema que dependem do tratamento de dados pessoais. + A revogação de alguns consentimentos pode impedir o acesso a funcionalidades do sistema + que dependem do tratamento de dados pessoais.

-
+

- Termo de Uso: Aceite obrigatório para utilização do sistema. A - revogação pode impedir o acesso. + Termo de Uso: Aceite obrigatório para utilização do sistema. A revogação pode + impedir o acesso.

- Política de Privacidade: Informa como seus dados são tratados. A - revogação não impede o tratamento, mas você pode solicitar exclusão. + Política de Privacidade: Informa como seus dados são tratados. A revogação + não impede o tratamento, mas você pode solicitar exclusão.

- Comunicações: Permite envio de notificações e comunicações do - sistema. A revogação pode limitar informações importantes. + Comunicações: Permite envio de notificações e comunicações do sistema. A revogação + pode limitar informações importantes.

- Compartilhamento de Dados: Permite compartilhamento com terceiros - quando necessário. A revogação pode afetar serviços terceirizados. + Compartilhamento de Dados: Permite compartilhamento com terceiros quando necessário. + A revogação pode afetar serviços terceirizados.

- - diff --git a/apps/web/src/routes/(dashboard)/privacidade/+page.svelte b/apps/web/src/routes/(dashboard)/privacidade/+page.svelte index 4e06757..a905b4b 100644 --- a/apps/web/src/routes/(dashboard)/privacidade/+page.svelte +++ b/apps/web/src/routes/(dashboard)/privacidade/+page.svelte @@ -6,27 +6,33 @@ const configLGPD = useQuery(api.lgpd.obterConfiguracaoLGPD, {}); - const encarregadoNome = $derived(configLGPD?.data?.encarregadoNome || 'Encarregado de Proteção de Dados'); - const encarregadoEmail = $derived(configLGPD?.data?.encarregadoEmail || 'lgpd@esportes.pe.gov.br'); + const encarregadoNome = $derived( + configLGPD?.data?.encarregadoNome || 'Encarregado de Proteção de Dados' + ); + const encarregadoEmail = $derived( + configLGPD?.data?.encarregadoEmail || 'lgpd@esportes.pe.gov.br' + ); const encarregadoTelefone = $derived(configLGPD?.data?.encarregadoTelefone || '(81) 3184-XXXX'); - const encarregadoHorario = $derived(configLGPD?.data?.encarregadoHorarioAtendimento || 'Segunda a Sexta, das 8h às 17h'); + const encarregadoHorario = $derived( + configLGPD?.data?.encarregadoHorarioAtendimento || 'Segunda a Sexta, das 8h às 17h' + ); -
+
-
-
- +
+
+
-

Política de Privacidade

+

Política de Privacidade

Lei Geral de Proteção de Dados Pessoais (LGPD) - Lei nº 13.709/2018

-
+
Última atualização: {new Date().toLocaleDateString('pt-BR')}
@@ -35,76 +41,76 @@
-
+
-

1. Introdução

+

1. Introdução

A Secretaria de Esportes do Estado de Pernambuco, no exercício de suas atribuições constitucionais e legais, está comprometida com a proteção dos dados pessoais de seus - servidores, colaboradores e cidadãos, em conformidade com a Lei Geral de Proteção de - Dados Pessoais (LGPD) - Lei nº 13.709/2018. + servidores, colaboradores e cidadãos, em conformidade com a Lei Geral de Proteção de Dados + Pessoais (LGPD) - Lei nº 13.709/2018.

- Esta Política de Privacidade descreve como coletamos, utilizamos, armazenamos e - protegemos seus dados pessoais no Sistema de Gestão da Secretaria de Esportes (SGSE). + Esta Política de Privacidade descreve como coletamos, utilizamos, armazenamos e protegemos + seus dados pessoais no Sistema de Gestão da Secretaria de Esportes (SGSE).

-
+
-

2. Dados Pessoais Coletados

+

2. Dados Pessoais Coletados

O SGSE coleta e processa os seguintes tipos de dados pessoais:

- +

Dados de Identificação

-

- Nome completo, CPF, RG, data de nascimento, naturalidade, nacionalidade, - estado civil, filiação (nome do pai e mãe) +

+ Nome completo, CPF, RG, data de nascimento, naturalidade, nacionalidade, estado + civil, filiação (nome do pai e mãe)

- +

Dados de Contato

-

+

E-mail, telefone, endereço residencial e endereços de marcação de ponto

- +

Dados Profissionais

-

- Matrícula, cargo, função, setor, data de admissão, regime de trabalho, - documentos profissionais (CTPS, título eleitor, reservista, PIS) +

+ Matrícula, cargo, função, setor, data de admissão, regime de trabalho, documentos + profissionais (CTPS, título eleitor, reservista, PIS)

- +

Dados de Saúde

-

- Atestados médicos, licenças de saúde, grupo sanguíneo, fator RH (quando - necessário para atividades específicas) +

+ Atestados médicos, licenças de saúde, grupo sanguíneo, fator RH (quando necessário + para atividades específicas)

- +

Dados de Acesso

-

- Credenciais de acesso, logs de acesso, endereço IP, histórico de atividades - no sistema +

+ Credenciais de acesso, logs de acesso, endereço IP, histórico de atividades no + sistema

@@ -113,13 +119,13 @@
-
+
-

3. Finalidade do Tratamento

+

3. Finalidade do Tratamento

Os dados pessoais são tratados para as seguintes finalidades:

-
    +
    • Gestão de recursos humanos e folha de pagamento
    • Controle de ponto e registro de jornada de trabalho
    • Gestão de férias, ausências e licenças
    • @@ -135,39 +141,38 @@
-
+
-

4. Base Legal do Tratamento

+

4. Base Legal do Tratamento

- O tratamento de dados pessoais no SGSE fundamenta-se nas seguintes bases legais, - conforme previsto no Art. 7º da LGPD: + O tratamento de dados pessoais no SGSE fundamenta-se nas seguintes bases legais, conforme + previsto no Art. 7º da LGPD:

-
-

I. Execução de Políticas Públicas

-

- Art. 7º, II - Para a execução de políticas públicas previstas em leis ou - regulamentos +

+

I. Execução de Políticas Públicas

+

+ Art. 7º, II - Para a execução de políticas públicas previstas em leis ou regulamentos

-
-

II. Cumprimento de Obrigação Legal

-

+

+

II. Cumprimento de Obrigação Legal

+

Art. 7º, I - Para cumprimento de obrigação legal ou regulatória pelo controlador

-
-

III. Execução de Contrato

-

- Art. 7º, V - Para a execução de contrato ou de procedimentos preliminares - relacionados a contrato do qual seja parte o titular +

+

III. Execução de Contrato

+

+ Art. 7º, V - Para a execução de contrato ou de procedimentos preliminares relacionados + a contrato do qual seja parte o titular

-
-

IV. Proteção da Vida e Saúde

-

- Art. 7º, VI e VII - Para a proteção da vida ou da incolumidade física do titular - ou de terceiro, e para a tutela da saúde +

+

IV. Proteção da Vida e Saúde

+

+ Art. 7º, VI e VII - Para a proteção da vida ou da incolumidade física do titular ou de + terceiro, e para a tutela da saúde

@@ -175,46 +180,43 @@
-
+
-

5. Compartilhamento de Dados

-

- Os dados pessoais podem ser compartilhados com: -

-
    +

    5. Compartilhamento de Dados

    +

    Os dados pessoais podem ser compartilhados com:

    +
    • - Órgãos Públicos: Quando necessário para cumprimento de obrigações - legais ou execução de políticas públicas + Órgãos Públicos: Quando necessário para cumprimento de obrigações legais + ou execução de políticas públicas
    • - Fornecedores de Serviços: Empresas contratadas para prestação de - serviços técnicos, sempre com garantias de proteção de dados + Fornecedores de Serviços: Empresas contratadas para prestação de serviços + técnicos, sempre com garantias de proteção de dados
    • - Autoridades Competentes: Quando exigido por determinação judicial ou - legal + Autoridades Competentes: Quando exigido por determinação judicial ou legal

    - Todos os compartilhamentos são realizados com base legal e com garantias de - proteção dos dados pessoais. + Todos os compartilhamentos são realizados com base legal e com garantias de proteção dos + dados pessoais.

-
+
-

6. Medidas de Segurança

+

6. Medidas de Segurança

Adotamos medidas técnicas e administrativas para proteger seus dados pessoais:

-
-
-

Medidas Técnicas

-
    +
    +
    +

    Medidas Técnicas

    +
    • • Criptografia de dados sensíveis
    • • Controle de acesso por permissões
    • • Logs de auditoria
    • @@ -222,9 +224,9 @@
    • • Monitoramento de segurança
    -
    -

    Medidas Administrativas

    -
      +
      +

      Medidas Administrativas

      +
      • • Treinamento de equipe
      • • Políticas de acesso
      • • Procedimentos de segurança
      • @@ -237,20 +239,19 @@
-
+
-

7. Prazo de Retenção

+

7. Prazo de Retenção

Os dados pessoais são mantidos pelo prazo necessário para:

-
    +
    • - Dados de Funcionários Ativos: Durante todo o período de vínculo - empregatício/estatutário + Dados de Funcionários Ativos: Durante todo o período de vínculo empregatício/estatutário
    • - Dados de Funcionários Inativos: Conforme prazo legal aplicável (em - geral, 5 anos após desligamento) + Dados de Funcionários Inativos: Conforme prazo legal aplicável (em geral, + 5 anos após desligamento)
    • Logs de Acesso: 2 anos, conforme recomendação da ANPD @@ -267,9 +268,9 @@
-
+
-

8. Direitos do Titular dos Dados

+

8. Direitos do Titular dos Dados

Conforme previsto no Art. 18 da LGPD, você possui os seguintes direitos:

@@ -278,16 +279,14 @@
1

Confirmação da Existência de Tratamento

-

- Confirmar se tratamos seus dados pessoais -

+

Confirmar se tratamos seus dados pessoais

2

Acesso aos Dados

-

+

Acessar seus dados pessoais tratados por nós

@@ -296,7 +295,7 @@
3

Correção de Dados

-

+

Solicitar correção de dados incompletos, inexatos ou desatualizados

@@ -305,9 +304,8 @@
4

Anonimização, Bloqueio ou Eliminação

-

- Solicitar anonimização, bloqueio ou eliminação de dados desnecessários ou - excessivos +

+ Solicitar anonimização, bloqueio ou eliminação de dados desnecessários ou excessivos

@@ -315,7 +313,7 @@
5

Portabilidade dos Dados

-

+

Solicitar a portabilidade dos dados a outro fornecedor de serviço ou produto

@@ -324,7 +322,7 @@
6

Eliminação de Dados

-

+

Solicitar a eliminação dos dados pessoais tratados com base em consentimento

@@ -333,7 +331,7 @@
7

Informação sobre Compartilhamento

-

+

Obter informações sobre compartilhamento de dados com terceiros

@@ -342,7 +340,7 @@
8

Revogação de Consentimento

-

+

Revogar seu consentimento, quando aplicável

@@ -361,17 +359,17 @@
-
+
-

9. Encarregado de Proteção de Dados (DPO)

+

9. Encarregado de Proteção de Dados (DPO)

Para exercer seus direitos ou esclarecer dúvidas sobre o tratamento de dados pessoais, entre em contato com nosso Encarregado de Proteção de Dados:

-
+
{#if encarregadoNome && encarregadoNome !== 'Encarregado de Proteção de Dados'}
- +

Nome

{encarregadoNome}

@@ -379,21 +377,21 @@
{/if}
- +

E-mail

{encarregadoEmail}

- +

Telefone

{encarregadoTelefone}

- +

Horário de Atendimento

{encarregadoHorario}

@@ -402,27 +400,27 @@

- As solicitações serão respondidas em até {configLGPD?.data?.prazoRespostaPadrao || 15} (quinze) dias, conforme previsto na - LGPD. + As solicitações serão respondidas em até {configLGPD?.data?.prazoRespostaPadrao || 15} (quinze) + dias, conforme previsto na LGPD.

-
+
-

10. Alterações nesta Política

+

10. Alterações nesta Política

- Esta Política de Privacidade pode ser atualizada periodicamente. Recomendamos que - você revise esta página regularmente para estar ciente de quaisquer alterações. A data - da última atualização está indicada no topo desta página. + Esta Política de Privacidade pode ser atualizada periodicamente. Recomendamos que você + revise esta página regularmente para estar ciente de quaisquer alterações. A data da + última atualização está indicada no topo desta página.

-
- diff --git a/apps/web/src/routes/(dashboard)/privacidade/meus-dados/+page.svelte b/apps/web/src/routes/(dashboard)/privacidade/meus-dados/+page.svelte index 63a3de1..c7b8137 100644 --- a/apps/web/src/routes/(dashboard)/privacidade/meus-dados/+page.svelte +++ b/apps/web/src/routes/(dashboard)/privacidade/meus-dados/+page.svelte @@ -24,14 +24,14 @@ | 'revogacao_consentimento' | 'informacao_compartilhamento'; -let tipoSelecionado = $state(null); -let dadosSolicitados = $state(''); -let observacoes = $state(''); -let carregando = $state(false); + let tipoSelecionado = $state(null); + let dadosSolicitados = $state(''); + let observacoes = $state(''); + let carregando = $state(false); const client = useConvexClient(); const minhasSolicitacoesQuery = useQuery(api.lgpd.listarMinhasSolicitacoes, {}); - + // Garantir que sempre seja um array ou undefined const minhasSolicitacoes = $derived( minhasSolicitacoesQuery === undefined || minhasSolicitacoesQuery === null @@ -40,7 +40,7 @@ let carregando = $state(false); ? minhasSolicitacoesQuery : [] ); - + const exportarDados = useQuery(api.lgpd.exportarDadosUsuario, {}); const tiposSolicitacao: Array<{ valor: TipoSolicitacao; label: string; descricao: string }> = [ @@ -152,15 +152,15 @@ let carregando = $state(false); } -
+
-
-
- +
+
+
-

Meus Direitos LGPD

+

Meus Direitos LGPD

Solicite o exercício dos seus direitos conforme a Lei Geral de Proteção de Dados

@@ -168,22 +168,19 @@ let carregando = $state(false);
-
+
-
+
-

Nova Solicitação

+

Nova Solicitação

- {#each tiposSolicitacao as tipo} @@ -241,8 +238,8 @@ let carregando = $state(false);

- Sua solicitação será analisada e respondida em até 15 dias úteis, conforme - previsto na LGPD. + Sua solicitação será analisada e respondida em até 15 dias úteis, conforme previsto na + LGPD.

@@ -251,24 +248,26 @@ let carregando = $state(false);
-
+

Minhas Solicitações

{#if minhasSolicitacoes && Array.isArray(minhasSolicitacoes)}
- {minhasSolicitacoes.length} solicitação{minhasSolicitacoes.length !== 1 ? 'ões' : ''} + {minhasSolicitacoes.length} solicitação{minhasSolicitacoes.length !== 1 + ? 'ões' + : ''}
{/if}
{#if minhasSolicitacoes === undefined || minhasSolicitacoes === null} -
+
{:else if !Array.isArray(minhasSolicitacoes) || minhasSolicitacoes.length === 0} -
- +
+

Nenhuma solicitação encontrada

-

+

Suas solicitações aparecerão aqui após serem criadas

@@ -277,16 +276,16 @@ let carregando = $state(false); {#each minhasSolicitacoes as solicitacao} {@const statusInfo = getStatusBadge(solicitacao.status)} {@const StatusIcon = getStatusIcon(solicitacao.status)} -
-
+
+
- +

- {tiposSolicitacao.find((t) => t.valor === solicitacao.tipo) - ?.label || solicitacao.tipo} + {tiposSolicitacao.find((t) => t.valor === solicitacao.tipo)?.label || + solicitacao.tipo}

-

+

Criada em{' '} {format(new Date(solicitacao.criadoEm), "dd/MM/yyyy 'às' HH:mm", { locale: ptBR @@ -298,9 +297,9 @@ let carregando = $state(false);

{#if solicitacao.resposta} -
-

Resposta:

-

{solicitacao.resposta}

+
+

Resposta:

+

{solicitacao.resposta}

{/if} @@ -318,7 +317,7 @@ let carregando = $state(false); {/if} {#if solicitacao.status === 'pendente' || solicitacao.status === 'em_analise'} -
+
Prazo para resposta:{' '} {format(new Date(solicitacao.prazoResposta), 'dd/MM/yyyy', { locale: ptBR @@ -338,8 +337,8 @@ let carregando = $state(false);
-

Exportar Meus Dados

-

+

Exportar Meus Dados

+

Baixe uma cópia completa dos seus dados pessoais em formato JSON.

- @@ -313,7 +319,7 @@
{:else}
- +
@@ -337,7 +343,7 @@
{dispensa.funcionario?.nome || '-'}
{#if dispensa.funcionario?.matricula} - + Mat: {dispensa.funcionario.matricula} {/if} @@ -348,7 +354,11 @@
Início:{' '} - {formatarDataHora(dispensa.dataInicio, dispensa.horaInicio, dispensa.minutoInicio)} + {formatarDataHora( + dispensa.dataInicio, + dispensa.horaInicio, + dispensa.minutoInicio + )}
Fim:{' '} @@ -389,11 +399,11 @@ {#if mostrandoModalExcluir && dispensaParaExcluir} {/if}
- diff --git a/apps/web/src/routes/(dashboard)/recursos-humanos/controle-ponto/homologacao/+page.svelte b/apps/web/src/routes/(dashboard)/recursos-humanos/controle-ponto/homologacao/+page.svelte index 0325dce..bc80580 100644 --- a/apps/web/src/routes/(dashboard)/recursos-humanos/controle-ponto/homologacao/+page.svelte +++ b/apps/web/src/routes/(dashboard)/recursos-humanos/controle-ponto/homologacao/+page.svelte @@ -2,7 +2,17 @@ import { onMount } from 'svelte'; import { useQuery, useConvexClient } from 'convex-svelte'; import { api } from '@sgse-app/backend/convex/_generated/api'; - import { Clock, Edit, TrendingUp, TrendingDown, Save, X, Trash2, Eye, MoreVertical } from 'lucide-svelte'; + import { + Clock, + Edit, + TrendingUp, + TrendingDown, + Save, + X, + Trash2, + Eye, + MoreVertical + } from 'lucide-svelte'; import type { Id } from '@sgse-app/backend/convex/_generated/dataModel'; import { formatarHoraPonto, getTipoRegistroLabel } from '$lib/utils/ponto'; import { toast } from 'svelte-sonner'; @@ -19,7 +29,7 @@ let homologacaoParaExcluir = $state | null>(null); let mostrandoModalDetalhes = $state(false); let mostrandoModalExcluir = $state(false); - + // Filtros de período const hoje = new Date(); const trintaDiasAtras = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); @@ -28,7 +38,11 @@ // Monitorar mudanças em funcionarioSelecionado $effect(() => { - console.log('🔄 [DEBUG] funcionarioSelecionado mudou:', funcionarioSelecionado, typeof funcionarioSelecionado); + console.log( + '🔄 [DEBUG] funcionarioSelecionado mudou:', + funcionarioSelecionado, + typeof funcionarioSelecionado + ); }); // Formulário de edição @@ -95,7 +109,7 @@ return data.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit', - year: 'numeric', + year: 'numeric' }); }); @@ -112,9 +126,9 @@ // Parâmetros reativos para queries const homologacoesParams = $derived({ - funcionarioId: funcionarioSelecionado || undefined, + funcionarioId: funcionarioSelecionado || undefined }); - + // Parâmetros para query de registros - só executa quando há funcionário selecionado const registrosQueryParams = $derived.by(() => { // Verificar se funcionarioSelecionado não é string vazia @@ -124,21 +138,19 @@ return { funcionarioId: funcionarioSelecionado as Id<'funcionarios'>, dataInicio: dataInicioFiltro, - dataFim: dataFimFiltro, + dataFim: dataFimFiltro }; }); const homologacoesQuery = useQuery(api.pontos.listarHomologacoes, homologacoesParams); const registrosQuery = $derived( - registrosQueryParams - ? useQuery(api.pontos.listarRegistrosPeriodo, registrosQueryParams) - : null + registrosQueryParams ? useQuery(api.pontos.listarRegistrosPeriodo, registrosQueryParams) : null ); const subordinados = $derived(subordinadosQuery?.data || []); const motivos = $derived(motivosQuery?.data); const homologacoes = $derived(homologacoesQuery?.data || []); - + // Registros já filtrados pela query no backend const registros = $derived.by(() => { if (!funcionarioSelecionado || funcionarioSelecionado === '' || !registrosQuery) { @@ -151,13 +163,18 @@ // A query do backend já filtra pelo funcionário, mas adicionamos verificação extra return dados.filter((r) => String(r.funcionarioId) === String(funcionarioSelecionado)); }); - + // Verificar se é gestor (tem subordinados) const isGestor = $derived(subordinados.length > 0); // Lista de funcionários do time const funcionarios = $derived.by(() => { - const funcs: Array<{ _id: Id<'funcionarios'>; nome: string; matricula?: string; fotoPerfilUrl?: string | null }> = []; + const funcs: Array<{ + _id: Id<'funcionarios'>; + nome: string; + matricula?: string; + fotoPerfilUrl?: string | null; + }> = []; for (const time of subordinados) { for (const membro of time.membros) { if (membro.funcionario && !funcs.find((f) => f._id === membro.funcionario._id)) { @@ -165,7 +182,7 @@ _id: membro.funcionario._id, nome: membro.funcionario.nome, matricula: membro.funcionario.matricula, - fotoPerfilUrl: membro.funcionario.fotoPerfilUrl, + fotoPerfilUrl: membro.funcionario.fotoPerfilUrl }); } } @@ -203,7 +220,7 @@ observacoes = ''; modoEdicao = true; abaAtiva = 'editar'; - + // Resetar campos de ajuste tipoAjuste = 'compensar'; const hoje = new Date().toISOString().split('T')[0]!; @@ -242,7 +259,7 @@ motivoId: motivoId || undefined, motivoTipo: motivoTipo || undefined, motivoDescricao: motivoDescricao || undefined, - observacoes: observacoes || undefined, + observacoes: observacoes || undefined }); toast.success('Registro editado com sucesso'); @@ -287,7 +304,7 @@ motivoId: motivoId || undefined, motivoTipo: motivoTipo || undefined, motivoDescricao: motivoDescricao || undefined, - observacoes: observacoes || undefined, + observacoes: observacoes || undefined }); toast.success('Banco de horas ajustado com sucesso'); @@ -323,7 +340,7 @@ try { await client.mutation(api.pontos.excluirHomologacao, { - homologacaoId: homologacaoParaExcluir, + homologacaoId: homologacaoParaExcluir }); toast.success('Homologação excluída com sucesso'); @@ -344,7 +361,9 @@ abrirEdicaoComAjuste(homologacao.registroId); } else { // Se for ajuste de banco de horas, não há como editar diretamente - toast.info('Ajustes de banco de horas não podem ser editados. Crie um novo ajuste para corrigir.'); + toast.info( + 'Ajustes de banco de horas não podem ser editados. Crie um novo ajuste para corrigir.' + ); } } @@ -356,23 +375,23 @@
-
+
-
- +
+
-

Homologação de Registro

+

Homologação de Registro

Edite registros de ponto e ajuste banco de horas

-
+

Selecionar Funcionário

-
+
Funcionário
- +
+
+ @@ -797,7 +855,9 @@ - +
Data Tipo {getTipoRegistroLabel(registro.tipo)} {formatarHoraPonto(registro.hora, registro.minuto)}{formatarHoraPonto(registro.hora, registro.minuto)} Histórico de Homologações {#if funcionarioSelecionado} - - - Funcionário selecionado - + - Funcionário selecionado {:else} - + - Todas as homologações do seu time {/if} @@ -850,134 +908,140 @@ Nenhuma homologação encontrada {:else} -
- - +
+
+ + + + {#if !funcionarioSelecionado} + + {/if} + + + + + {#if isGestor} + + {/if} + + + + {#each homologacoes as homologacao} - + {#if !funcionarioSelecionado} - + {/if} - - - - + + + + {#if isGestor} - + {/if} - - - {#each homologacoes as homologacao} - - - {#if !funcionarioSelecionado} - - {/if} - - - - - {#if isGestor} - - {/if} - - {/each} - -
DataFuncionárioTipoDetalhesMotivoObservaçõesAções
Data + {new Date(homologacao.criadoEm).toLocaleDateString('pt-BR')} + Funcionário +
+ +
+
{homologacao.funcionario?.nome || '-'}
+ {#if homologacao.funcionario?.matricula} + + Mat: {homologacao.funcionario.matricula} + + {/if} +
+
+
TipoDetalhesMotivoObservações + {#if homologacao.registroId} + Edição de Registro + {:else if homologacao.tipoAjuste} + + Ajuste: {homologacao.tipoAjuste} + + {/if} + + {#if homologacao.horaAnterior !== undefined} +
+ + {formatarHoraPonto( + homologacao.horaAnterior, + homologacao.minutoAnterior || 0 + )} + + {' → '} + + {formatarHoraPonto( + homologacao.horaNova || 0, + homologacao.minutoNova || 0 + )} + +
+ {:else if homologacao.ajusteMinutos} +
+ {homologacao.periodoDias || 0}d {homologacao.periodoHoras || 0}h{' '} + {homologacao.periodoMinutos || 0}min +
+ {/if} +
+
+ {homologacao.motivoDescricao || homologacao.motivoTipo || '-'} +
+
+
+ {homologacao.observacoes || '-'} +
+
Ações +
+ + {#if homologacao.registroId} + + {/if} + +
+
- {new Date(homologacao.criadoEm).toLocaleDateString('pt-BR')} - -
- -
-
{homologacao.funcionario?.nome || '-'}
- {#if homologacao.funcionario?.matricula} - - Mat: {homologacao.funcionario.matricula} - - {/if} -
-
-
- {#if homologacao.registroId} - Edição de Registro - {:else if homologacao.tipoAjuste} - - Ajuste: {homologacao.tipoAjuste} - - {/if} - - {#if homologacao.horaAnterior !== undefined} -
- - {formatarHoraPonto(homologacao.horaAnterior, homologacao.minutoAnterior || 0)} - - {' → '} - - {formatarHoraPonto(homologacao.horaNova || 0, homologacao.minutoNova || 0)} - -
- {:else if homologacao.ajusteMinutos} -
- {homologacao.periodoDias || 0}d {homologacao.periodoHoras || 0}h{' '} - {homologacao.periodoMinutos || 0}min -
- {/if} -
-
- {homologacao.motivoDescricao || homologacao.motivoTipo || '-'} -
-
-
- {homologacao.observacoes || '-'} -
-
-
- - {#if homologacao.registroId} - - {/if} - -
-
-
- {/if} - + {/each} + +
+
+ {/if}
+
{#if mostrandoModalDetalhes && homologacaoSelecionada}