From c146a6c3c3668c78c1fa577e8be293a8c7eee9ec Mon Sep 17 00:00:00 2001 From: icamarillo Date: Thu, 26 Feb 2026 12:48:28 -0700 Subject: [PATCH] funcion dashboard y reporte de endpoints v1.16.0 --- .../src/lib/components/Sidebar.svelte | 5 + .../src/lib/stores/dashboardConfig.ts | 161 ++++++ frontend-internal/src/routes/+page.svelte | 159 ++++-- .../src/routes/test-report/+page.svelte | 492 ++++++++++++++++++ 4 files changed, 762 insertions(+), 55 deletions(-) create mode 100644 frontend-internal/src/lib/stores/dashboardConfig.ts create mode 100644 frontend-internal/src/routes/test-report/+page.svelte diff --git a/frontend-internal/src/lib/components/Sidebar.svelte b/frontend-internal/src/lib/components/Sidebar.svelte index 47ae2ed..7716930 100644 --- a/frontend-internal/src/lib/components/Sidebar.svelte +++ b/frontend-internal/src/lib/components/Sidebar.svelte @@ -72,6 +72,11 @@ name: 'Seguridad', href: '/audit/security', icon: 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z' + }, + { + name: 'Reporte Endpoints', + href: '/test-report', + icon: 'M9 3H5a2 2 0 00-2 2v4m6-6h10a2 2 0 012 2v4M9 3v18m0 0h10a2 2 0 002-2V9M9 21H5a2 2 0 01-2-2V9m0 0h18' } ); } diff --git a/frontend-internal/src/lib/stores/dashboardConfig.ts b/frontend-internal/src/lib/stores/dashboardConfig.ts new file mode 100644 index 0000000..e713ca3 --- /dev/null +++ b/frontend-internal/src/lib/stores/dashboardConfig.ts @@ -0,0 +1,161 @@ +import { writable } from 'svelte/store'; + +/** All available dashboard modules */ +export interface DashboardModule { + id: string; + title: string; + description: string; + icon: string; + href: string; + color: string; + /** Minimum role required to see this module */ + roles: string[]; +} + +export const ALL_MODULES: DashboardModule[] = [ + { + id: 'tenants', + title: 'Clientes', + description: 'Gestión de organizaciones y tenants', + icon: 'M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4', + href: '/tenants', + color: 'bg-blue-600', + roles: ['ADMIN', 'SUPPORT_MANAGER'] + }, + { + id: 'users', + title: 'Usuarios', + description: 'Administración de usuarios y roles', + icon: 'M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z', + href: '/users', + color: 'bg-green-600', + roles: ['ADMIN', 'SUPPORT_MANAGER'] + }, + { + id: 'tickets', + title: 'Tickets', + description: 'Gestión y seguimiento de tickets de soporte', + icon: 'M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2', + href: '/tickets', + color: 'bg-indigo-600', + roles: ['ADMIN', 'SUPPORT_MANAGER', 'AGENT'] + }, + { + id: 'systems', + title: 'Sistemas', + description: 'Catálogo de sistemas soportados', + icon: 'M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01', + href: '/systems', + color: 'bg-gray-700', + roles: ['ADMIN', 'SUPPORT_MANAGER'] + }, + { + id: 'categories', + title: 'Categorías', + description: 'Clasificación de tickets por área', + icon: 'M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10', + href: '/categories', + color: 'bg-orange-600', + roles: ['ADMIN', 'SUPPORT_MANAGER'] + }, + { + id: 'sla', + title: 'SLA Management', + description: 'Monitoreo de tiempos de respuesta y SLAs', + icon: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z', + href: '/sla', + color: 'bg-teal-600', + roles: ['ADMIN', 'SUPPORT_MANAGER'] + }, + { + id: 'reports', + title: 'Reportes', + description: 'Informes estadísticos y análisis de rendimiento', + icon: 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z', + href: '/reports', + color: 'bg-purple-600', + roles: ['ADMIN', 'SUPPORT_MANAGER'] + }, + { + id: 'audit', + title: 'Auditoría', + description: 'Bitácora de acciones y trazabilidad del sistema', + icon: 'M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z', + href: '/audit', + color: 'bg-red-700', + roles: ['ADMIN', 'AUDITOR'] + }, + { + id: 'security', + title: 'Seguridad', + description: 'Análisis de amenazas y eventos de seguridad', + icon: 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z', + href: '/audit/security', + color: 'bg-yellow-600', + roles: ['ADMIN'] + }, + { + id: 'endpoints', + title: 'Reporte de Endpoints', + description: 'Estado y diagnóstico de todos los endpoints API', + icon: 'M9 3H5a2 2 0 00-2 2v4m6-6h10a2 2 0 012 2v4M9 3v18m0 0h10a2 2 0 002-2V9M9 21H5a2 2 0 01-2-2V9m0 0h18', + href: '/test-report', + color: 'bg-cyan-600', + roles: ['ADMIN'] + } +]; + +const STORAGE_KEY = 'dashboard_module_visibility'; + +function getInitialVisibility(): Record { + if (typeof window === 'undefined') { + return Object.fromEntries(ALL_MODULES.map(m => [m.id, true])); + } + try { + const stored = localStorage.getItem(STORAGE_KEY); + if (stored) return JSON.parse(stored); + } catch { /* ignore */ } + return Object.fromEntries(ALL_MODULES.map(m => [m.id, true])); +} + +function createDashboardConfig() { + const { subscribe, set, update } = writable>(getInitialVisibility()); + + return { + subscribe, + toggle(id: string) { + update(state => { + const next = { ...state, [id]: !state[id] }; + if (typeof window !== 'undefined') { + localStorage.setItem(STORAGE_KEY, JSON.stringify(next)); + } + return next; + }); + }, + setVisible(id: string, visible: boolean) { + update(state => { + const next = { ...state, [id]: visible }; + if (typeof window !== 'undefined') { + localStorage.setItem(STORAGE_KEY, JSON.stringify(next)); + } + return next; + }); + }, + showAll() { + const all = Object.fromEntries(ALL_MODULES.map(m => [m.id, true])); + if (typeof window !== 'undefined') { + localStorage.setItem(STORAGE_KEY, JSON.stringify(all)); + } + set(all); + }, + reset() { + const defaults = Object.fromEntries(ALL_MODULES.map(m => [m.id, true])); + if (typeof window !== 'undefined') { + localStorage.setItem(STORAGE_KEY, JSON.stringify(defaults)); + } + set(defaults); + } + }; +} + +export const dashboardConfig = createDashboardConfig(); diff --git a/frontend-internal/src/routes/+page.svelte b/frontend-internal/src/routes/+page.svelte index 8075b59..1ebf81f 100644 --- a/frontend-internal/src/routes/+page.svelte +++ b/frontend-internal/src/routes/+page.svelte @@ -2,44 +2,27 @@ import { onMount } from 'svelte'; import { auth } from '$lib/stores/auth.js'; import { goto } from '$app/navigation'; - import Icon from '$lib/components/Icon.svelte'; - + import { dashboardConfig, ALL_MODULES, type DashboardModule } from '$lib/stores/dashboardConfig.js'; + + let showSettings = false; + onMount(() => { if (!$auth.isAuthenticated) { goto('/login'); } }); - const cards = [ - { - title: 'Clientes', - description: 'Gestión de organizaciones y tenants', - icon: 'users', - href: '/tenants', - color: 'bg-blue-600' - }, - { - title: 'Usuarios', - description: 'Administración de usuarios y roles', - icon: 'user-plus', - href: '/users', - color: 'bg-green-600' - }, - { - title: 'Sistemas', - description: 'Catálogo de sistemas soportados', - icon: 'server', - href: '/systems', - color: 'bg-gray-700' - }, - { - title: 'Categorías', - description: 'Clasificación de tickets', - icon: 'tag', - href: '/categories', - color: 'bg-orange-600' - } - ]; + const role = $auth.user?.role ?? ''; + + /** Only modules the current role can access */ + $: accessibleModules = ALL_MODULES.filter(m => m.roles.includes(role) || role === 'ADMIN'); + + /** Modules that are visible (enabled by user + accessible by role) */ + $: visibleModules = accessibleModules.filter(m => $dashboardConfig[m.id] !== false); + + function toggleSettings() { + showSettings = !showSettings; + } @@ -47,40 +30,106 @@
+

Panel de Administración

- Bienvenido al sistema de gestión interna. + Bienvenido al sistema de gestión interna.

+
+ +
-
- {#each cards as card} - - diff --git a/frontend-internal/src/routes/test-report/+page.svelte b/frontend-internal/src/routes/test-report/+page.svelte new file mode 100644 index 0000000..fd04e9c --- /dev/null +++ b/frontend-internal/src/routes/test-report/+page.svelte @@ -0,0 +1,492 @@ + + + + Reporte de Endpoints - ServiceManager + + +
+ +
+
+

Reporte de Endpoints & Páginas

+

+ Diagnóstico de conectividad de todos los endpoints del backend y páginas del frontend. +

+
+
+ +
+
+ + + {#if testedCount() > 0} +
+
+
{testedCount()}/{totalEndpoints}
+
Endpoints probados
+
+
+
{totalOk}
+
OK / Exitosos
+
+
+
{totalFail}
+
Errores / Fallidos
+
+
+ {/if} + + +
+ + +
+ + + {#if activeTab === 'endpoints'} +
+ {#each GROUPS as group} +
+ +
+
+ + {group.name} + + {group.endpoints.length} endpoint{group.endpoints.length !== 1 ? 's' : ''} +
+
+ {#each group.endpoints as ep} + {#if results[ep.id].tested} + + {/if} + {/each} +
+
+ + +
+ {#each group.endpoints as ep} + {@const r = results[ep.id]} + {@const badge = statusBadge(r)} +
+
+ + + {ep.method} + + + + /api/v1{ep.path} + + + {ep.label} + + +
+ {#if r.ms !== null && r.tested} + {r.ms}ms + {/if} + {badge.text} + + + + {#if r.tested && r.preview} + + {/if} +
+
+ + +

{ep.description}

+ + + {#if r.tested && r.error} +
{r.error}
+ {/if} + + + {#if expandedIds.has(ep.id) && r.preview} +
{r.preview}
+ {/if} +
+ {/each} +
+
+ {/each} +
+ {/if} + + + {#if activeTab === 'pages'} +
+ + + + + + + + + + + + {#each FRONTEND_PAGES as pg} + + + + + + + + {/each} + +
PáginaRutaDescripciónRolesAcción
+ {pg.label} + + {pg.href} + + {pg.description} + +
+ {#each pg.roles as role} + {role} + {/each} +
+
+ + Abrir ↗ + +
+ + +
+

+ Nota: Las páginas se abren en una nueva pestaña para verificar su renderizado. + Asegúrate de estar autenticado antes de acceder a rutas protegidas. +

+
+ {#each FRONTEND_PAGES as pg} + + + + + {pg.label} + {pg.href} + + {/each} +
+
+
+ {/if} +