fmatituy/selectospromanager
0
1from nicegui import ui
2from modules.personal.services_personal import get_dashboard_stats, get_novedades_activas
3from services.auth import require_auth, get_current_user
4from modules.layout import SelectosLayout
5
6@ui.page('/personal/dashboard')
7@require_auth
8def dashboard_personal_page():
9 user = get_current_user()
10 if user.get('rol') not in ["Administrador", "Gerente", "Supervisor", "SST"]:
11 return ui.navigate.to('/')
12
13 stats = get_dashboard_stats()
14 novedades = get_novedades_activas()
15
16 with SelectosLayout('Dashboard de Personal'):
17 ui.label('Monitor de Talento Humano').classes('text-3xl font-black text-slate-800 mb-6')
18
19 # Tarjetas KPI
20 with ui.row().classes('w-full gap-4 mb-8 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4'):
21 with ui.card().classes('p-6 bg-blue-50 border border-blue-100 rounded-3xl shadow-sm'):
22 with ui.row().classes('items-center gap-3'):
23 ui.icon('groups', color='blue-600', size='md').classes('p-3 bg-white rounded-2xl')
24 with ui.column().classes('gap-0'):
25 ui.label('Activos').classes('text-xs font-bold text-blue-600 uppercase')
26 ui.label(str(stats['total_empleados'])).classes('text-2xl font-black text-slate-800')
27
28 with ui.card().classes('p-6 bg-emerald-50 border border-emerald-100 rounded-3xl shadow-sm'):
29 with ui.row().classes('items-center gap-3'):
30 ui.icon('check_circle', color='emerald-600', size='md').classes('p-3 bg-white rounded-2xl')
31 with ui.column().classes('gap-0'):
32 ui.label('Presentes Hoy').classes('text-xs font-bold text-emerald-600 uppercase')
33 ui.label(str(stats['presentes_hoy'])).classes('text-2xl font-black text-slate-800')
34
35 with ui.card().classes('p-6 bg-rose-50 border border-rose-100 rounded-3xl shadow-sm'):
36 with ui.row().classes('items-center gap-3'):
37 ui.icon('event_busy', color='rose-600', size='md').classes('p-3 bg-white rounded-2xl')
38 with ui.column().classes('gap-0'):
39 ui.label('Novedades').classes('text-xs font-bold text-rose-600 uppercase')
40 ui.label(str(stats['novedades_hoy'])).classes('text-2xl font-black text-slate-800')
41
42 with ui.card().classes('p-6 bg-amber-50 border border-amber-100 rounded-3xl shadow-sm'):
43 with ui.row().classes('items-center gap-3'):
44 ui.icon('timer', color='amber-600', size='md').classes('p-3 bg-white rounded-2xl')
45 with ui.column().classes('gap-0'):
46 ui.label('Horas Mes').classes('text-xs font-bold text-amber-600 uppercase')
47 ui.label(f"{stats['horas_mes']:.0f}").classes('text-2xl font-black text-slate-800')
48
49 with ui.row().classes('w-full gap-8 items-start'):
50 # Gráfica de Asistencia Semanal (Mock/Simulada)
51 with ui.card().classes('flex-[2] p-8 rounded-3xl border border-slate-200 shadow-sm'):
52 ui.label('Tendencia de Asistencia').classes('text-lg font-black text-slate-800 mb-4')
53 ui.echart({
54 'xAxis': {'type': 'category', 'data': ['Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'Sab']},
55 'yAxis': {'type': 'value'},
56 'series': [
57 {'data': [12, 15, 14, 18, 16, 10], 'type': 'line', 'smooth': True, 'color': '#1e40af'},
58 {'data': [1, 0, 2, 1, 0, 1], 'type': 'bar', 'color': '#ef4444'}
59 ]
60 }).classes('h-64')
61
62 # Listado de Novedades Activas
63 with ui.card().classes('flex-1 p-8 rounded-3xl border border-slate-200 shadow-sm'):
64 ui.label('Novedades Activas').classes('text-lg font-black text-slate-800 mb-4')
65 if not novedades:
66 ui.label('No hay novedades para hoy.').classes('text-slate-400 italic')
67 else:
68 with ui.column().classes('w-full gap-3'):
69 for n in novedades:
70 with ui.row().classes('w-full items-center p-3 bg-slate-50 rounded-xl border border-slate-100'):
71 ui.icon('warning', color='rose-500', size='xs')
72 with ui.column().classes('gap-0'):
73 ui.label(n['empleado_nombre']).classes('text-xs font-bold text-slate-800')
74 ui.label(n['tipo']).classes('text-[10px] text-slate-500 uppercase')
75 