@extends('layouts.panel') @section('title', 'Sesiones Activas - Piter Professional') @section('content') @php // 1. Resolución de Administrador para la consulta de sesiones $admin = \Illuminate\Support\Facades\Auth::guard('admin')->user() ?? \Illuminate\Support\Facades\Auth::user(); $user = $admin; // Alias para compatibilidad con la lógica de tema // --- LÓGICA DE TEMA DINÁMICO --- $convertColor = function($color) { if (empty($color)) return null; if (str_starts_with($color, '#')) return $color; if (str_starts_with($color, '0x')) return '#' . substr($color, -6); return $color; }; $getContrastColor = function($hex) { if(!$hex) return '#ffffff'; $hex = str_replace('#', '', $hex); if(strlen($hex) == 3) $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2]; $r = hexdec(substr($hex, 0, 2)); $g = hexdec(substr($hex, 2, 2)); $b = hexdec(substr($hex, 4, 2)); $yiq = (($r * 299) + ($g * 587) + ($b * 114)) / 1000; return ($yiq >= 128) ? '#0f172a' : '#ffffff'; }; $panelColor = $convertColor($user->color_preferido ?? null) ?? '#020617'; $bgColor = $convertColor($user->color_fondo_preferido ?? null) ?? '#f8fafc'; $textColor = $getContrastColor($bgColor); $isDark = ($textColor === '#ffffff'); $bgPage = $bgColor; $accentColor = $panelColor; if ($panelColor === '#020617' || $panelColor === '#000000') { $accentColor = '#ef4444'; // Rojo por defecto si el panel es negro/obsidian } if ($isDark) { $bgCard = 'color-mix(in srgb, '.$bgColor.', white 2%)'; $bgSurface = 'color-mix(in srgb, '.$bgColor.', white 5%)'; $textMain = '#ffffff'; $textMuted = '#94a3b8'; $borderColor = 'rgba(255,255,255,0.05)'; $shadowColor = 'rgba(0,0,0,0.8)'; } else { $bgCard = 'rgba(255, 255, 255, 0.95)'; $bgSurface = '#f8fafc'; $textMain = '#0f172a'; $textMuted = '#64748b'; $borderColor = '#e2e8f0'; $shadowColor = 'rgba(148, 163, 184, 0.15)'; } // Redefinimos $sessions para saltarnos las limitaciones del controlador // Y asegurar que solo vemos lo REALMENTE activo y sin duplicados por dispositivo $rawSessions = \Illuminate\Support\Facades\DB::table('dbo.Seguridad_Sesiones as s') ->join('dbo.Seguridad_Usuarios as u', 's.usuario_id', '=', 'u.id_usuario') ->leftJoin('dbo.Seguridad_Perfiles as p', 'u.perfil_id', '=', 'p.id_perfil') ->leftJoin('dbo.Nucleo_Sucursales as suc', function($join) { $join->on('u.sucursal_asignada_id', '=', 'suc.id_sucursal') ->orOn('u.id_usuario', '=', 'suc.gerente_encargado_id'); }) ->select( 's.id_sesiones', 's.ip', 's.user_agent', 's.fecha_ingreso', 's.fecha_expiracion', 'u.nombre_completo', 'u.email', 'u.foto_url', 'u.id_usuario', 'p.nombre as rol', 'suc.nombre as sucursal_nombre', 'suc.id_sucursal' ) ->where('u.empresa_id', $admin->empresa_id) ->where('u.estado', 'ACTIVO') ->where(function($q) { $q->where('p.nombre', 'NOT LIKE', '%DUENIO%') ->where('p.nombre', 'NOT LIKE', '%OWNER%') ->where('p.nombre', 'NOT LIKE', '%ADMIN%'); // Excluir perfiles administrativos superiores }) ->where('s.fecha_expiracion', '>', \Illuminate\Support\Facades\DB::raw('GETDATE()')) // Solo sesiones que no han expirado ->where('s.fecha_ingreso', '>', \Illuminate\Support\Facades\DB::raw('DATEADD(hour, -48, GETDATE())')) // Límite de 48h para considerar "Tiempo Real" ->orderByDesc('s.fecha_ingreso') ->get(); $isMobile = function($userAgent) { return preg_match('/Mobile|Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile|Dart|Flutter/i', $userAgent); }; // FILTRADO INTELIGENTE: Solo la última sesión activa por usuario y por TIPO de dispositivo (PC o App) // Esto evita que si alguien entró 10 veces, veas 10 tarjetas. $allSessions = $rawSessions->groupBy(function($s) use ($isMobile) { $deviceType = $isMobile($s->user_agent) ? 'MOBILE' : 'PC'; return $s->id_usuario . '_' . $deviceType; })->map(function($group) { return $group->first(); // Al estar ordenadas por fecha_ingreso DESC, el primero es el más reciente })->values(); // Obtener lista de sucursales para el filtro $sucursales = $allSessions->pluck('sucursal_nombre', 'id_sucursal')->filter()->unique(); // Aplicar filtrado por sucursal si se solicita $selectedBranch = request('sucursal_id'); if ($selectedBranch) { $filteredSessions = $allSessions->filter(function($s) use ($selectedBranch) { return $s->id_sucursal == $selectedBranch; }); } else { $filteredSessions = $allSessions; } // Agrupar $groupedSessions = $filteredSessions->map(function($s) { $s->sucursal_display = $s->sucursal_nombre ?? 'OFICINA CENTRAL / SIN SUCURSAL'; return $s; })->groupBy('sucursal_display'); $isMobile = function($userAgent) { return preg_match('/Mobile|Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile|Dart|Flutter/i', $userAgent); }; @endphp
{{-- Header Content --}}

Sesiones Vivas

Live

Control total • {{ $filteredSessions->count() }} Conexiones activas

{{-- Sucursal Filter --}}
@if($selectedBranch) @endif
{{-- Sessions Grid --}} {{-- Sessions Grid Grouped by Branch --}}
@forelse($groupedSessions as $branchName => $branchSessions)
{{-- Brach Header --}}

{{ $branchName }} {{ count($branchSessions) }}

@foreach($branchSessions as $session) @php $deviceMobile = $isMobile($session->user_agent); @endphp
{{-- Device Type Indicator (Upper Left) --}}
{{ $deviceMobile ? 'App Móvil' : 'PC • Web' }}
{{-- Status Dot (Upper Right) --}}
{{-- Avatar --}}
@php $userPhoto = 'https://ui-avatars.com/api/?name=' . urlencode($session->nombre_completo) . '&background=fef2f2&color=ef4444'; if (!empty($session->foto_url) && $session->foto_url !== 'null') { $cleanPath = str_replace(['uploads / ', 'uploads/', 'uploads\ ', 'uploads\\'], '', $session->foto_url); $cleanPath = trim(str_replace('\\', '/', $cleanPath)); if (str_starts_with($cleanPath, '/')) $cleanPath = substr($cleanPath, 1); $userPhoto = url('/api/Security/uploads/' . $cleanPath); } @endphp
{{-- Info --}}

{{ $session->nombre_completo }}

{{ $session->rol }}

En Línea

{{-- Device Info --}}
IP: {{ $session->ip }}
Ingreso: {{ \Carbon\Carbon::parse($session->fecha_ingreso)->format('d/m H:i') }}
{{-- Action - Botón Estilo Naranja de la Imagen --}}
@endforeach
@empty

Sin Sesiones Activas

No hay otros usuarios conectados a la plataforma en este momento.

@endforelse
@push('styles') @endpush @endsection @push('scripts') @endpush