wave-ai/frontend/src/app/features/dashboard/dashboard.component.ts

823 lines
46 KiB
TypeScript

import { Component, signal, computed, OnInit, inject } from '@angular/core';
import { Router } from '@angular/router';
import { CommonModule } from '@angular/common';
interface Employee {
id: number;
name: string;
role: string;
rating: number;
clients: number;
rate: number;
avatar: string;
tags: string[];
}
interface UserProfile {
name: string;
email: string;
role: string;
avatar: string;
}
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [CommonModule],
template: `
<div class="flex flex-col lg:flex-row gap-6 p-2 lg:p-6 min-h-[80vh]">
<!-- Sidebar Navigation Menu (Cyberpunk theme) -->
<!-- Main Workstation View Area -->
<main class="flex-grow flex flex-col gap-6">
<!-- ================= PAGE 1: INICIO (CONSORCIO) ================= -->
@if (activeTab() === 'inicio') {
<div style="display:flex;flex-direction:column;gap:32px;" class="animate-fadeIn">
<!-- Hero Banner -->
<!-- KPI Row (light cards) -->
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:16px;">
<div style="background:#fff;border:1px solid rgba(15,13,10,0.08);border-radius:16px;padding:20px 22px;border-top:3px solid #C9973C;box-shadow:0 1px 6px rgba(15,13,10,0.06);display:flex;flex-direction:column;gap:4px;">
<span style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.14em;color:#78716C;">Sedes Integradas</span>
<div style="font-family:'Inter',sans-serif;font-size:30px;font-weight:900;color:#0F0D0A;line-height:1.1;">3 Sedes</div>
<span style="font-size:10px;color:#C9973C;font-weight:600;">London · Tokyo · NY</span>
</div>
<div style="background:#fff;border:1px solid rgba(15,13,10,0.08);border-radius:16px;padding:20px 22px;border-top:3px solid #3730A3;box-shadow:0 1px 6px rgba(15,13,10,0.06);display:flex;flex-direction:column;gap:4px;">
<span style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.14em;color:#78716C;">Directorio Activo</span>
<div style="font-family:'Inter',sans-serif;font-size:30px;font-weight:900;color:#0F0D0A;line-height:1.1;">1,450+</div>
<span style="font-size:10px;color:#3730A3;font-weight:600;">Usuarios Sincronizados</span>
</div>
<div style="background:#fff;border:1px solid rgba(15,13,10,0.08);border-radius:16px;padding:20px 22px;border-top:3px solid #065F46;box-shadow:0 1px 6px rgba(15,13,10,0.06);display:flex;flex-direction:column;gap:4px;">
<span style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.14em;color:#78716C;">Salas Disponibles</span>
<div style="font-family:'Inter',sans-serif;font-size:30px;font-weight:900;color:#0F0D0A;line-height:1.1;">18 Salas</div>
<span style="font-size:10px;color:#065F46;font-weight:600;">Sistemas IoT Activos</span>
</div>
<div style="background:#fff;border:1px solid rgba(15,13,10,0.08);border-radius:16px;padding:20px 22px;border-top:3px solid #7C3AED;box-shadow:0 1px 6px rgba(15,13,10,0.06);display:flex;flex-direction:column;gap:4px;">
<span style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.14em;color:#78716C;">Flujos de Trabajo</span>
<div style="font-family:'Inter',sans-serif;font-size:30px;font-weight:900;color:#0F0D0A;line-height:1.1;">12 Activos</div>
<span style="font-size:10px;color:#7C3AED;font-weight:600;">Reglas AD Operativas</span>
</div>
</div>
<!-- Section header -->
<div>
<h3 style="font-family:'Inter',sans-serif;font-size:20px;font-weight:900;letter-spacing:1.5px;text-transform:uppercase;color:#0F0D0A;margin:0 0 4px;">Módulos del Sistema</h3>
<p style="font-size:13px;color:#78716C;margin:0;font-weight:400;">Selecciona un módulo para acceder a su gestión</p>
</div>
<!-- 4 Jewel-Tone Blackjack Cards -->
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:20px;">
<!-- Card 1: Info Hub — Deep Navy #0D1B2A -->
<div (click)="setActiveTab('workflow')"
style="position:relative;background:#0D1B2A;border:none;border-radius:20px;padding:40px 24px;cursor:pointer;display:flex;flex-direction:column;align-items:center;text-align:center;gap:22px;transition:all 0.22s cubic-bezier(0.34,1.56,0.64,1);overflow:hidden;min-height:270px;justify-content:center;box-shadow:0 8px 32px rgba(13,27,42,0.28);">
<!-- Inner decorative border -->
<div style="position:absolute;inset:10px;border:1px solid rgba(255,255,255,0.10);border-radius:12px;pointer-events:none;"></div>
<!-- Ambient top glow -->
<div style="position:absolute;top:-40px;right:-40px;width:160px;height:160px;background:radial-gradient(circle,rgba(201,151,60,0.14) 0%,transparent 70%);pointer-events:none;"></div>
<!-- Gold icon -->
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" style="position:relative;z-index:1;filter:drop-shadow(0 0 20px rgba(201,151,60,0.55));">
<path d="M24 6L42 15L24 24L6 15L24 6Z" stroke="#C9973C" stroke-width="2.5" stroke-linejoin="round"/>
<path d="M6 24L24 33L42 24" stroke="#C9973C" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6 33L24 42L42 33" stroke="#C9973C" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<div style="position:relative;z-index:1;display:flex;flex-direction:column;gap:8px;">
<h4 style="font-family:'Inter',sans-serif;font-size:12px;font-weight:900;letter-spacing:0.16em;text-transform:uppercase;color:#fff;margin:0;">Info Hub</h4>
<p style="font-size:12px;color:rgba(255,255,255,0.45);line-height:1.65;margin:0;font-weight:300;">Gestión de contenidos corporativos y publicación de artículos.</p>
</div>
</div>
<!-- Card 2: Directorio — Deep Plum #1A0E2E -->
<div (click)="navigateToDirectorio()"
style="position:relative;background:#1A0E2E;border:none;border-radius:20px;padding:40px 24px;cursor:pointer;display:flex;flex-direction:column;align-items:center;text-align:center;gap:22px;transition:all 0.22s cubic-bezier(0.34,1.56,0.64,1);overflow:hidden;min-height:270px;justify-content:center;box-shadow:0 8px 32px rgba(26,14,46,0.30);">
<div style="position:absolute;inset:10px;border:1px solid rgba(255,255,255,0.08);border-radius:12px;pointer-events:none;"></div>
<div style="position:absolute;top:-40px;left:-40px;width:180px;height:180px;background:radial-gradient(circle,rgba(124,58,237,0.16) 0%,transparent 70%);pointer-events:none;"></div>
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" style="position:relative;z-index:1;filter:drop-shadow(0 0 20px rgba(167,139,250,0.45));">
<path d="M8 16V8H16" stroke="#A78BFA" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M40 16V8H32" stroke="#A78BFA" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 32V40H16" stroke="#A78BFA" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M40 32V40H32" stroke="#A78BFA" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="18" cy="22" r="4" stroke="#A78BFA" stroke-width="2"/>
<circle cx="30" cy="22" r="4" stroke="#A78BFA" stroke-width="2"/>
<path d="M11 33c0-3.866 3.134-7 7-7s7 3.134 7 7" stroke="#A78BFA" stroke-width="2" stroke-linecap="round"/>
<path d="M23 33c0-3.866 3.134-7 7-7s7 3.134 7 7" stroke="#A78BFA" stroke-width="2" stroke-linecap="round"/>
</svg>
<div style="position:relative;z-index:1;display:flex;flex-direction:column;gap:8px;">
<h4 style="font-family:'Inter',sans-serif;font-size:12px;font-weight:900;letter-spacing:0.16em;text-transform:uppercase;color:#fff;margin:0;">Directorio AD</h4>
<p style="font-size:12px;color:rgba(255,255,255,0.40);line-height:1.65;margin:0;font-weight:300;">Sincronización con Active Directory y perfiles dinámicos.</p>
</div>
</div>
<!-- Card 3: Workflows — Deep Viridian #0A2520 -->
<div (click)="setActiveTab('workflow')"
style="position:relative;background:#0A2520;border:none;border-radius:20px;padding:40px 24px;cursor:pointer;display:flex;flex-direction:column;align-items:center;text-align:center;gap:22px;transition:all 0.22s cubic-bezier(0.34,1.56,0.64,1);overflow:hidden;min-height:270px;justify-content:center;box-shadow:0 8px 32px rgba(10,37,32,0.30);">
<div style="position:absolute;inset:10px;border:1px solid rgba(255,255,255,0.08);border-radius:12px;pointer-events:none;"></div>
<div style="position:absolute;bottom:-40px;right:-40px;width:180px;height:180px;background:radial-gradient(circle,rgba(16,185,129,0.14) 0%,transparent 70%);pointer-events:none;"></div>
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" style="position:relative;z-index:1;filter:drop-shadow(0 0 20px rgba(52,211,153,0.45));">
<circle cx="10" cy="12" r="5" stroke="#34D399" stroke-width="2.5"/>
<circle cx="38" cy="12" r="5" stroke="#34D399" stroke-width="2.5"/>
<circle cx="24" cy="36" r="5" stroke="#34D399" stroke-width="2.5"/>
<path d="M15 12H33" stroke="#34D399" stroke-width="2" stroke-linecap="round"/>
<path d="M10 17V28c0 2.5 2 4.5 4.5 4.5H19" stroke="#34D399" stroke-width="2" stroke-linecap="round"/>
<path d="M38 17V28c0 2.5-2 4.5-4.5 4.5H29" stroke="#34D399" stroke-width="2" stroke-linecap="round"/>
</svg>
<div style="position:relative;z-index:1;display:flex;flex-direction:column;gap:8px;">
<h4 style="font-family:'Inter',sans-serif;font-size:12px;font-weight:900;letter-spacing:0.16em;text-transform:uppercase;color:#fff;margin:0;">Workflows</h4>
<p style="font-size:12px;color:rgba(255,255,255,0.40);line-height:1.65;margin:0;font-weight:300;">Líneas de aprobación, revisión y publicación de contenido.</p>
</div>
</div>
<!-- Card 4: Espacios — Deep Espresso #1C0E08 -->
<div (click)="navigateToCalendario()"
style="position:relative;background:#1C0E08;border:none;border-radius:20px;padding:40px 24px;cursor:pointer;display:flex;flex-direction:column;align-items:center;text-align:center;gap:22px;transition:all 0.22s cubic-bezier(0.34,1.56,0.64,1);overflow:hidden;min-height:270px;justify-content:center;box-shadow:0 8px 32px rgba(28,14,8,0.32);">
<div style="position:absolute;inset:10px;border:1px solid rgba(255,255,255,0.08);border-radius:12px;pointer-events:none;"></div>
<div style="position:absolute;top:-40px;left:-40px;width:180px;height:180px;background:radial-gradient(circle,rgba(251,146,60,0.14) 0%,transparent 70%);pointer-events:none;"></div>
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" style="position:relative;z-index:1;filter:drop-shadow(0 0 20px rgba(251,146,60,0.50));">
<rect x="6" y="10" width="36" height="32" rx="4" stroke="#FB923C" stroke-width="2.5"/>
<path d="M6 20H42" stroke="#FB923C" stroke-width="2.5" stroke-linecap="round"/>
<path d="M16 6V14" stroke="#FB923C" stroke-width="2.5" stroke-linecap="round"/>
<path d="M32 6V14" stroke="#FB923C" stroke-width="2.5" stroke-linecap="round"/>
<path d="M17 31L22 36L32 26" stroke="#FB923C" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<div style="position:relative;z-index:1;display:flex;flex-direction:column;gap:8px;">
<h4 style="font-family:'Inter',sans-serif;font-size:12px;font-weight:900;letter-spacing:0.16em;text-transform:uppercase;color:#fff;margin:0;">Espacios</h4>
<p style="font-size:12px;color:rgba(255,255,255,0.40);line-height:1.65;margin:0;font-weight:300;">Autogestión de salas, oficinas y activos de la empresa.</p>
</div>
</div>
</div>
</div>
}
<!-- ================= PAGE 2: DIRECTORY ================= -->
@if (activeTab() === 'directory') {
<div class="flex flex-col gap-6 animate-fadeIn">
<h2 class="text-2xl font-black text-white uppercase tracking-tight">Interfaz Directory Modern</h2>
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
<!-- Sidebar Filters -->
<div class="glass-panel p-6 rounded-2xl flex flex-col gap-5 h-fit">
<h3 class="text-sm font-bold uppercase tracking-widest text-slate-400">Búsqueda & Filtrado</h3>
<p class="text-xs text-white/50 leading-relaxed">Uso de bordes suaves y ergonómicos para optimizar la visualización de datos.</p>
<div class="flex flex-col gap-4">
<!-- Search input -->
<div class="flex flex-col gap-1.5">
<label class="text-[10px] uppercase font-bold text-slate-400 tracking-wider">Buscar Empleado</label>
<input type="text" placeholder="Buscar empleado..."
(input)="updateSearch($event)"
class="px-4 py-2.5 rounded-full input-cyberpunk text-sm w-full" />
</div>
<!-- Department select -->
<div class="flex flex-col gap-1.5">
<label class="text-[10px] uppercase font-bold text-slate-400 tracking-wider">Departamento</label>
<select (change)="updateDept($event)" class="px-4 py-2.5 rounded-full input-cyberpunk text-sm w-full bg-slate-900">
<option value="ALL">Todos los Departamentos</option>
<option value="IT">IT & Desarrollo</option>
<option value="Diseño">Diseño y UI/UX</option>
<option value="Mantenimiento">Mantenimiento</option>
</select>
</div>
<button class="w-full py-2.5 rounded-full btn-cyan text-xs uppercase font-extrabold tracking-wider mt-2">
APLICAR FILTROS
</button>
</div>
</div>
<!-- Directory grid -->
<div class="lg:col-span-3 flex flex-col gap-4">
<!-- Search Result Summary -->
<div class="text-xs text-slate-400 flex justify-between items-center">
<span>Mostrando {{ filteredEmployees().length }} empleados registrados</span>
<span class="text-neon-cyan">Sincronizado con Active Directory</span>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
@for (emp of filteredEmployees(); track emp.id) {
<!-- Profile Card in cyberpunk style exactly matching the presentation's John Morton cards -->
<div class="glass-panel rounded-3xl overflow-hidden relative flex flex-col hover:border-cyan-500/40 transition-all duration-300">
<!-- Header Gradient Background of card -->
<div class="h-20 bg-gradient-to-r from-purple-600 via-magenta-500 to-cyan-400 relative">
<span class="absolute top-3 right-3 text-xs bg-black/40 backdrop-blur px-2.5 py-0.5 rounded-full text-white/95">
ID: AD-{{ emp.id * 123 }}
</span>
</div>
<div class="p-6 pt-0 relative flex-grow flex flex-col">
<!-- Profile Image offset -->
<div class="relative -mt-10 mb-3 flex justify-between items-end">
<div class="w-20 h-20 rounded-full border-4 border-[#0c0f1d] overflow-hidden bg-slate-800">
<div class="w-full h-full flex items-center justify-center text-4xl bg-slate-900 text-white font-bold select-none">
{{ emp.avatar }}
</div>
</div>
<!-- Interactive star badge -->
<div class="flex items-center gap-1 text-xs bg-slate-900/80 backdrop-blur px-2.5 py-1 rounded-full border border-white/5">
<span class="text-amber-400">★</span>
<span class="font-bold text-white">{{ emp.rating }}</span>
</div>
</div>
<!-- Info -->
<h4 class="text-lg font-extrabold text-white leading-tight">{{ emp.name }}</h4>
<span class="text-xs text-neon-cyan font-semibold tracking-wide uppercase mb-3 block">{{ emp.role }}</span>
<!-- Tags -->
<div class="flex flex-wrap gap-1.5 mb-4">
@for (tag of emp.tags; track tag) {
<span class="text-[10px] bg-white/5 border border-white/5 rounded-full px-2.5 py-0.5 text-slate-300 font-medium">
{{ tag }}
</span>
}
</div>
<hr class="border-white/5 my-3" />
<!-- Stats Grid -->
<div class="grid grid-cols-2 gap-4 mb-4 text-center">
<div class="bg-white/5 p-2 rounded-xl border border-white/5">
<div class="text-[10px] text-white/50 uppercase font-bold tracking-wider">Clientes</div>
<div class="text-sm font-extrabold text-white">{{ emp.clients }}</div>
</div>
<div class="bg-white/5 p-2 rounded-xl border border-white/5">
<div class="text-[10px] text-white/50 uppercase font-bold tracking-wider">Tarifa/Hora</div>
<div class="text-sm font-extrabold text-white">\${{ emp.rate }}/hr</div>
</div>
</div>
<!-- Action button -->
<button (click)="contactEmployee(emp.name)" class="w-full py-2.5 rounded-2xl bg-white text-black font-extrabold text-xs uppercase tracking-wider hover:bg-cyan-400 hover:text-black transition-all">
Get In Touch
</button>
</div>
</div>
}
</div>
</div>
</div>
</div>
}
<!-- ================= PAGE 3: WORKFLOW ================= -->
@if (activeTab() === 'workflow') {
<div class="flex flex-col gap-6 animate-fadeIn">
<h2 class="text-2xl font-black text-white uppercase tracking-tight">Workflow de Aprobación</h2>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Details & Steps -->
<div class="glass-panel p-6 rounded-2xl flex flex-col gap-6 justify-between">
<div class="flex flex-col gap-4">
<span class="text-neon-magenta text-xs font-bold uppercase tracking-widest">CICLO COMPLETO DE VIDA</span>
<h3 class="text-xl font-bold text-white">Validación y Publicación Automatizada</h3>
<p class="text-sm text-slate-400 leading-relaxed">
Las propuestas de contenidos y flujos pasan por un motor estructurado que valida y publica automáticamente en canales institucionales y redes corporativas.
</p>
<!-- Step List -->
<div class="flex flex-col gap-3 mt-2">
<div class="flex items-start gap-3.5 p-3.5 rounded-xl bg-white/5 border border-white/5">
<span class="text-neon-cyan font-bold">✓</span>
<div>
<strong class="text-white block text-sm">Creación</strong>
<span class="text-xs text-slate-400">Captura de datos optimizada a través de formularios reactivos del lado del cliente.</span>
</div>
</div>
<div class="flex items-start gap-3.5 p-3.5 rounded-xl bg-white/5 border border-white/5">
<span class="text-neon-cyan font-bold">✓</span>
<div>
<strong class="text-white block text-sm">Validación</strong>
<span class="text-xs text-slate-400">Motor de reglas multinivel integrado con SSO y Active Directory para jerarquías de aprobación.</span>
</div>
</div>
<div class="flex items-start gap-3.5 p-3.5 rounded-xl bg-white/5 border border-white/5">
<span class="text-neon-cyan font-bold">✓</span>
<div>
<strong class="text-white block text-sm">Social</strong>
<span class="text-xs text-slate-400">Posteo automatizado e inmediato en las redes de comunicación interna de Kinetix.</span>
</div>
</div>
</div>
</div>
<div class="flex gap-4">
<button (click)="runSimulation()" class="px-5 py-2.5 rounded-full btn-purple text-xs uppercase font-extrabold tracking-wider">
SIMULAR MOTOR DE REGLAS
</button>
@if (simulationStatus()) {
<span class="text-xs text-green-400 font-bold self-center animate-pulse">{{ simulationStatus() }}</span>
}
</div>
</div>
<!-- Animated Neon Gear / Visualization -->
<div class="glass-panel p-6 rounded-2xl flex items-center justify-center relative overflow-hidden bg-slate-950/60 min-h-[300px]">
<div class="absolute -right-20 -bottom-20 w-48 h-48 bg-magenta-500/5 rounded-full blur-3xl"></div>
<div class="flex flex-col items-center gap-6">
<!-- Custom SVG rotating gears for premium visual animation -->
<svg class="w-40 h-40 text-green-400" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="30" stroke="currentColor" stroke-width="2" stroke-dasharray="10, 5" class="animate-[spin_10s_linear_infinite]" />
<circle cx="50" cy="50" r="20" stroke="currentColor" stroke-width="1.5" stroke-dasharray="5, 3" class="animate-[spin_6s_linear_infinite_reverse]" />
<g transform="translate(50, 50) rotate(0)">
<rect x="-4" y="-28" width="8" height="56" fill="currentColor" opacity="0.3" class="animate-[spin_8s_linear_infinite]" />
<rect x="-28" y="-4" width="56" height="8" fill="currentColor" opacity="0.3" class="animate-[spin_8s_linear_infinite]" />
</g>
<!-- Text elements -->
<text x="50" y="53" text-anchor="middle" font-size="8" fill="#fff" font-weight="bold" class="font-mono">AD ENGINE</text>
</svg>
<div class="text-center font-mono text-xs text-green-400 border border-green-500/30 px-3 py-1.5 rounded-lg bg-green-950/20">
STATUS: LISTENING ON PORT 3000
</div>
</div>
</div>
</div>
</div>
}
<!-- ================= PAGE 4: SPACES ================= -->
@if (activeTab() === 'spaces') {
<div class="flex flex-col gap-6 animate-fadeIn">
<h2 class="text-2xl font-black text-white uppercase tracking-tight">Gestión de Espacios & Incidencias</h2>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Room details & Gallery -->
<div class="glass-panel rounded-2xl overflow-hidden flex flex-col">
<div class="h-64 bg-slate-900 relative">
<!-- Nice placeholder simulating a modern boardroom -->
<div class="absolute inset-0 bg-cover bg-center" style="background-image: url('https://images.unsplash.com/photo-1497366216548-37526070297c?auto=format&fit=crop&w=800&q=80')"></div>
<div class="absolute inset-0 bg-gradient-to-t from-black via-black/30 to-transparent"></div>
<span class="absolute bottom-4 left-4 text-xs bg-purple-600 px-3 py-1 rounded-full font-bold uppercase tracking-wider">
SALA DE CONFERENCIAS VIP
</span>
</div>
<div class="p-6 flex flex-col gap-4">
<h3 class="text-lg font-bold text-white">Sala London Hall — Piso 4</h3>
<p class="text-sm text-slate-400 leading-relaxed">
Equipada con pantalla interactiva 4K de 85 pulgadas, sistema de audio envolvente Jabra, conectividad inalámbrica y capacidad para 12 personas.
</p>
<div class="flex gap-4 text-xs text-white/50">
<span>👥 Capacidad: 12 pers.</span>
<span>📶 Wi-Fi 6 Giga</span>
<span>🌡️ Termostato IoT</span>
</div>
</div>
</div>
<!-- Booking Form -->
<div class="glass-panel p-6 rounded-2xl flex flex-col gap-5 justify-between">
<div class="flex flex-col gap-4">
<span class="text-neon-purple text-xs font-bold uppercase tracking-widest">AUTOGESTIÓN DE ALQUILER</span>
<h3 class="text-xl font-bold text-white">Reserva Directa de Espacios</h3>
<p class="text-sm text-slate-400">Reserva de salas de conferencias, oficinas temporales y puestos de trabajo con confirmación instantánea.</p>
<div class="flex flex-col gap-4 mt-2">
<div class="grid grid-cols-2 gap-4">
<!-- Date input -->
<div class="flex flex-col gap-1.5">
<label class="text-[10px] uppercase font-bold text-slate-400 tracking-wider">Fecha</label>
<input type="text" value="Fecha: 15/06" class="px-4 py-2.5 rounded-full input-cyberpunk text-sm w-full" />
</div>
<!-- Time input -->
<div class="flex flex-col gap-1.5">
<label class="text-[10px] uppercase font-bold text-slate-400 tracking-wider">Hora</label>
<input type="text" value="Hora: 09:00 AM" class="px-4 py-2.5 rounded-full input-cyberpunk text-sm w-full" />
</div>
</div>
<!-- Room selector -->
<div class="flex flex-col gap-1.5">
<label class="text-[10px] uppercase font-bold text-slate-400 tracking-wider">Seleccionar Sala</label>
<select class="px-4 py-2.5 rounded-full input-cyberpunk text-sm w-full bg-slate-900">
<option>Sala London Hall (Piso 4)</option>
<option>Sala Tokyo Lab (Piso 2)</option>
<option>Oficina Flexible 15B (Piso 1)</option>
<option>Sala New York Boardroom (Piso 5)</option>
</select>
</div>
</div>
</div>
<div class="flex flex-col gap-3">
<button (click)="bookSpace()" class="w-full py-2.5 rounded-full btn-purple text-xs uppercase font-extrabold tracking-wider">
RESERVAR ESPACIO
</button>
@if (bookingSuccess()) {
<div class="p-3 bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 rounded-xl text-xs text-center font-bold animate-fadeIn">
¡Reserva confirmada con éxito! Se ha notificado a tu correo y calendario de Active Directory.
</div>
}
</div>
</div>
</div>
</div>
}
<!-- ================= PAGE 5: METRICS ================= -->
@if (activeTab() === 'metrics') {
<div class="flex flex-col gap-6 animate-fadeIn">
<h2 class="text-2xl font-black text-white uppercase tracking-tight">Proyección de Adopción & KPIs</h2>
<!-- Line chart & KPI -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- SVG chart visualizer -->
<div class="glass-panel p-6 rounded-2xl lg:col-span-2 flex flex-col gap-4">
<div class="flex justify-between items-center">
<h3 class="text-sm font-bold uppercase tracking-widest text-slate-400">Proyección de Adopción Kinetix</h3>
<span class="text-xs text-emerald-400 font-bold bg-emerald-500/10 px-2.5 py-0.5 rounded-full">ROI +85%</span>
</div>
<p class="text-xs text-slate-400">Crecimiento exponencial de interacciones tras la implementación del diseño UI/UX renovado.</p>
<!-- Pure SVG chart to avoid requiring Chart.js libraries and guarantee pixel perfection -->
<div class="w-full h-48 mt-2">
<svg class="w-full h-full" viewBox="0 0 500 200" preserveAspectRatio="none">
<defs>
<linearGradient id="chartGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#00f2fe" stop-opacity="0.3"/>
<stop offset="100%" stop-color="#00f2fe" stop-opacity="0.0"/>
</linearGradient>
</defs>
<!-- Grid Lines -->
<line x1="50" y1="150" x2="480" y2="150" stroke="rgba(255,255,255,0.05)" stroke-width="1" />
<line x1="50" y1="100" x2="480" y2="100" stroke="rgba(255,255,255,0.05)" stroke-width="1" />
<line x1="50" y1="50" x2="480" y2="50" stroke="rgba(255,255,255,0.05)" stroke-width="1" />
<!-- Line Path -->
<path d="M 50,150 Q 150,140 250,90 T 450,40 L 450,170 L 50,170 Z" fill="url(#chartGrad)" />
<path d="M 50,150 Q 150,140 250,90 T 450,40" fill="none" stroke="#00f2fe" stroke-width="3" />
<!-- Nodes -->
<circle cx="50" cy="150" r="4" fill="#00f2fe" />
<circle cx="250" cy="90" r="4" fill="#00f2fe" />
<circle cx="450" cy="40" r="5" fill="#f00c93" />
<!-- Labels -->
<text x="50" y="165" fill="rgba(255,255,255,0.4)" font-size="9" text-anchor="middle">Q1 2026</text>
<text x="250" y="110" fill="rgba(255,255,255,0.4)" font-size="9" text-anchor="middle">Despliegue</text>
<text x="450" y="25" fill="#f00c93" font-size="10" font-weight="bold" text-anchor="middle">ROI +85%</text>
</svg>
</div>
</div>
<!-- Speed KPI Card -->
<div class="glass-panel p-6 rounded-2xl flex flex-col justify-between">
<div class="flex flex-col gap-2">
<h3 class="text-sm font-bold uppercase tracking-widest text-slate-400">Indicadores Clave (KPIs)</h3>
<div class="text-5xl font-black text-neon-cyan my-3">0.4s</div>
<div class="text-sm font-bold text-white">Tiempo de Carga Promedio</div>
</div>
<div class="flex flex-col gap-3 mt-4">
<div class="text-xs text-slate-400 flex gap-2">
<span class="text-neon-cyan">⚡</span> Renderizado del lado del cliente altamente eficiente.
</div>
<div class="text-xs text-slate-400 flex gap-2">
<span class="text-neon-cyan">⚡</span> Compresión automatizada de activos de imagen.
</div>
<div class="text-xs text-slate-400 flex gap-2">
<span class="text-neon-cyan">⚡</span> Memoria caché distribuida para directorios AD.
</div>
</div>
</div>
</div>
<!-- Incident response efficiency comparison (Slide 9) -->
<div class="glass-panel p-6 rounded-2xl flex flex-col gap-4">
<h3 class="text-sm font-bold uppercase tracking-widest text-slate-400">Eficiencia en Resolución de Incidencias</h3>
<p class="text-xs text-slate-400">Reducción drástica del tiempo de respuesta mediante el módulo de ticketing inteligente de Kinetix.</p>
<!-- Custom styled CSS bar chart -->
<div class="flex flex-col gap-4 mt-2">
<!-- Kinetix V2 Bar -->
<div class="flex flex-col md:flex-row md:items-center gap-2">
<span class="w-32 text-xs font-bold text-white">Kinetix V2 (Actual)</span>
<div class="flex-grow bg-white/5 rounded-full overflow-hidden h-7 border border-white/5 relative">
<div class="bg-gradient-to-r from-cyan-400 to-indigo-500 h-full rounded-full transition-all duration-1000" style="width: 26%"></div>
<span class="absolute right-3 top-1 text-xs font-bold text-cyan-400">12 min</span>
</div>
</div>
<!-- Industry Average Bar -->
<div class="flex flex-col md:flex-row md:items-center gap-2">
<span class="w-32 text-xs font-bold text-slate-400">Promedio Industria</span>
<div class="flex-grow bg-white/5 rounded-full overflow-hidden h-7 border border-white/5 relative">
<div class="bg-slate-700 h-full rounded-full transition-all duration-1000" style="width: 55%"></div>
<span class="absolute right-3 top-1 text-xs font-bold text-slate-300">25 min</span>
</div>
</div>
<!-- Previous system Bar -->
<div class="flex flex-col md:flex-row md:items-center gap-2">
<span class="w-32 text-xs font-bold text-slate-400">Sistema Anterior</span>
<div class="flex-grow bg-white/5 rounded-full overflow-hidden h-7 border border-white/5 relative">
<div class="bg-slate-800 h-full rounded-full transition-all duration-1000" style="width: 100%"></div>
<span class="absolute right-3 top-1 text-xs font-bold text-slate-400">45 min</span>
</div>
</div>
</div>
</div>
</div>
}
<!-- ================= PAGE 6: ADMIN ================= -->
@if (activeTab() === 'admin') {
<div class="flex flex-col gap-6 animate-fadeIn">
<h2 class="text-2xl font-black text-white uppercase tracking-tight">Visión de Administrador</h2>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Radial/Circle visualization of status -->
<div class="glass-panel p-6 rounded-2xl flex flex-col items-center justify-center gap-4">
<h3 class="text-sm font-bold uppercase tracking-widest text-slate-400 self-start">Control Total</h3>
<div class="relative w-40 h-40 flex items-center justify-center">
<!-- Custom CSS circle progress -->
<div class="w-full h-full rounded-full border-8 border-white/5 absolute"></div>
<div class="w-full h-full rounded-full border-8 border-cyan-400 border-t-transparent border-l-transparent absolute animate-[spin_4s_linear_infinite]"></div>
<div class="flex flex-col items-center">
<span class="text-3xl font-black text-white">94%</span>
<span class="text-[9px] uppercase tracking-widest text-slate-400">ESTABILIDAD</span>
</div>
</div>
<span class="text-xs text-slate-400 text-center mt-2">Monitoreo activo de solicitudes y API gateways de NestJS.</span>
</div>
<!-- Critical Alerts & Approvals -->
<div class="lg:col-span-2 flex flex-col gap-6">
<!-- Critical Alerts -->
<div class="glass-panel p-6 rounded-2xl flex flex-col gap-4 border-l-4 border-l-magenta-500">
<h3 class="text-sm font-bold uppercase tracking-widest text-magenta-400">Alertas Críticas</h3>
<div class="flex flex-col gap-2">
<div class="flex justify-between items-center text-xs p-2.5 rounded-lg bg-magenta-500/5 border border-magenta-500/10 text-magenta-300">
<span>🔧 Mantenimiento de servidor de base de datos PostgreSQL</span>
<strong class="text-magenta-400">PENDIENTE</strong>
</div>
<div class="flex justify-between items-center text-xs p-2.5 rounded-lg bg-white/5 border border-white/5 text-slate-300">
<span>🔒 Expiración de secretos de Azure AD Client credentials</span>
<strong class="text-white/50">En 12 días</strong>
</div>
</div>
</div>
<!-- Approvals List -->
<div class="glass-panel p-6 rounded-2xl flex flex-col gap-4 border-l-4 border-l-purple-500">
<h3 class="text-sm font-bold uppercase tracking-widest text-purple-400">Aprobaciones de Contenido</h3>
<div class="flex flex-col gap-2">
<div class="flex justify-between items-center text-xs p-3 rounded-lg bg-white/5 border border-white/5">
<div class="flex flex-col">
<span class="font-bold text-white">Lanzamiento Kinetix V2 Intranet</span>
<span class="text-[10px] text-slate-400">Autor: Director de Tecnología (SSO AD)</span>
</div>
<button (click)="approveItem('Lanzamiento Kinetix V2')" class="px-3 py-1.5 rounded-lg bg-purple-600 text-white font-extrabold text-[10px] uppercase hover:bg-purple-500 transition">
Aprobar y Postear
</button>
</div>
<div class="flex justify-between items-center text-xs p-3 rounded-lg bg-white/5 border border-white/5">
<div class="flex flex-col">
<span class="font-bold text-white">Manual del Desarrollador Backend NestJS</span>
<span class="text-[10px] text-slate-400">Autor: Arquitecto Principal</span>
</div>
<button (click)="approveItem('Manual del Desarrollador')" class="px-3 py-1.5 rounded-lg bg-purple-600 text-white font-extrabold text-[10px] uppercase hover:bg-purple-500 transition">
Aprobar y Postear
</button>
</div>
</div>
@if (approvalSuccess()) {
<div class="text-xs text-green-400 font-bold bg-green-500/10 border border-green-500/15 p-2 rounded-lg text-center animate-fadeIn">
{{ approvalSuccess() }}
</div>
}
</div>
</div>
</div>
</div>
}
<aside class="w-full lg:w-64 flex flex-col gap-3">
<!-- Active User Profile Widget Sincronizado con Active Directory -->
<div class="glass-panel p-4 rounded-2xl flex flex-col gap-3 relative border-cyan-500/20 bg-slate-950/45">
<div class="flex justify-between items-center">
<span class="text-white/50"></span>
<span class="text-emerald-400 font-bold flex items-center gap-1.5">
<span class="w-2 h-2 rounded-full bg-emerald-400 animate-pulse"></span>
</span>
</div>
<div class="w-full py-1.5 rounded-xl border border-white/10 hover:border-magenta-500/30 hover:bg-magenta-950/20 text-white/60 hover:text-magenta-400 font-extrabold text-[10px] uppercase tracking-wider transition-all mt-1">
</div>
</div>
</aside>
<div class="absolute -right-2 -top-2 w-8 h-8 bg-cyan-500/10 rounded-full blur-md">
<button (click)="logout()" class="w-full py-1.5 rounded-xl border border-white/10 hover:border-magenta-500/30 hover:bg-magenta-950/20 text-white/60 hover:text-magenta-400 font-extrabold text-[10px] uppercase tracking-wider transition-all mt-1">
Cerrar Sesión
</button>
</div>
</main>
</div>
`,
styles: [`
.animate-fadeIn {
animation: fadeIn 0.4s ease-out forwards;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(4px); }
to { opacity: 1; transform: translateY(0); }
}
`]
})
export class DashboardComponent implements OnInit {
private router = inject(Router);
// Navigation active tab signal
activeTab = signal<'inicio' | 'directory' | 'workflow' | 'spaces' | 'metrics' | 'admin'>('inicio');
// Directory filter signals
searchQuery = signal<string>('');
selectedDept = signal<string>('ALL');
// Booking & Simulation mock state signals
bookingSuccess = signal<boolean>(false);
simulationStatus = signal<string>('');
approvalSuccess = signal<string>('');
// Sincronized Active Directory Active Profile signal
currentUser = signal<UserProfile>({
name: 'Invitado',
email: 'guest@kinetix.com',
role: 'Employee',
avatar: 'G'
});
// Active Directory mock database of employees exactly styled as Slide 4
employees = signal<Employee[]>([
{
id: 1,
name: 'John Morton',
role: 'UI/UX Designer',
rating: 4.9,
clients: 50,
rate: 65,
avatar: 'JM',
tags: ['App Design', 'Web Design', 'Figma', 'Active Directory']
},
{
id: 2,
name: 'Sarah Jenkins',
role: 'Backend NestJS Architect',
rating: 4.8,
clients: 42,
rate: 75,
avatar: 'SJ',
tags: ['NestJS', 'PostgreSQL', 'JWT', 'Active Directory']
},
{
id: 3,
name: 'Alex Rivera',
role: 'Senior Angular Developer',
rating: 4.9,
clients: 68,
rate: 70,
avatar: 'AR',
tags: ['Angular 17', 'Signals', 'RxJS', 'TypeScript']
},
{
id: 4,
name: 'David Chen',
role: 'Infrastructure Security Engineer',
rating: 4.7,
clients: 25,
rate: 80,
avatar: 'DC',
tags: ['Azure Entra ID', 'SSO', 'Docker', 'PostgreSQL']
}
]);
// Computed signal to filter employee list reactively
filteredEmployees = computed(() => {
const query = this.searchQuery().toLowerCase();
const dept = this.selectedDept();
return this.employees().filter(emp => {
// Basic text search filter
const matchesSearch = emp.name.toLowerCase().includes(query) ||
emp.role.toLowerCase().includes(query) ||
emp.tags.some(t => t.toLowerCase().includes(query));
// Basic department match
let matchesDept = true;
if (dept !== 'ALL') {
if (dept === 'IT') {
matchesDept = emp.role.includes('Backend') || emp.role.includes('Angular') || emp.role.includes('Infrastructure');
} else if (dept === 'Diseño') {
matchesDept = emp.role.includes('Designer') || emp.role.includes('UX');
} else if (dept === 'Mantenimiento') {
matchesDept = emp.role.includes('Security');
}
}
return matchesSearch && matchesDept;
});
});
ngOnInit() {
// Load Active Directory profile from local storage
const userStr = localStorage.getItem('kinetix_user');
if (userStr) {
try {
const user = JSON.parse(userStr);
this.currentUser.set(user);
} catch (e) {
console.error('Error parsing user profile from local storage', e);
}
}
}
setActiveTab(tab: 'inicio' | 'directory' | 'workflow' | 'spaces' | 'metrics' | 'admin') {
this.activeTab.set(tab);
// Reset secondary flags
this.bookingSuccess.set(false);
this.simulationStatus.set('');
this.approvalSuccess.set('');
}
updateSearch(event: Event) {
const value = (event.target as HTMLInputElement).value;
this.searchQuery.set(value);
}
updateDept(event: Event) {
const value = (event.target as HTMLSelectElement).value;
this.selectedDept.set(value);
}
contactEmployee(name: string) {
alert(`Iniciando canal seguro encriptado de comunicación con ${name} vía Microsoft Teams (SSO Active Directory)...`);
}
runSimulation() {
this.simulationStatus.set('Procesando reglas...');
setTimeout(() => {
this.simulationStatus.set('Creación capturada ✓');
setTimeout(() => {
this.simulationStatus.set('Reglas AD aprobadas ✓');
setTimeout(() => {
this.simulationStatus.set('Posteo social programado ✓');
}, 1000);
}, 1000);
}, 1000);
}
navigateToCalendario(): void {
this.router.navigate(['/reservas/calendario']);
}
navigateToDirectorio(): void {
this.router.navigate(['/directorio']);
}
bookSpace() {
this.bookingSuccess.set(true);
}
approveItem(title: string) {
this.approvalSuccess.set(`Publicación "${title}" aprobada y publicada en redes corporativas con éxito.`);
setTimeout(() => {
this.approvalSuccess.set('');
}, 4000);
}
logout() {
localStorage.removeItem('kinetix_token');
localStorage.removeItem('kinetix_user');
this.router.navigate(['/login']);
}
}