fmatituy/selectospromanager
0
1from nicegui import ui
2from services.auth import get_current_user
3
4def render_view_selector(current_view='gerencial'):
5 """Renders the top switcher between Management and Operational views."""
6 user = get_current_user()
7 rol = user.get('rol', '')
8
9 with ui.row().classes('w-full justify-center mb-6'):
10 with ui.row().classes('bg-slate-200 p-1.5 rounded-2xl gap-1 shadow-inner'):
11 # Gerencial Button - Only for Gerencia
12 if 'Gerente' in rol or 'Gerencia' in rol:
13 ui.button('VISTA GERENCIAL',
14 on_click=lambda: ui.navigate.to('/dashboard/gerencial'),
15 icon='insights').props(
16 f'unelevated rounded-xl size=sm {"color=primary" if current_view == "gerencial" else "flat color=slate-500"}'
17 ).classes('px-6 font-black tracking-widest')
18
19 # Operativo Button
20 ui.button('VISTA OPERATIVA',
21 on_click=lambda: ui.navigate.to('/dashboard/operativo'),
22 icon='engineering').props(
23 f'unelevated rounded-xl size=sm {"color=primary" if current_view == "operativo" else "flat color=slate-500"}'
24 ).classes('px-6 font-black tracking-widest')
25
26def render_kpi_card_industrial(title, val, icon, color='blue-600', trend=None, subtitle=None, on_click_info=None, spark_data=None):
27 """Modern industrial KPI card - COMPACT VERSION."""
28 color_map = {'blue-600': '#2563eb', 'emerald-600': '#059669', 'indigo-600': '#4f46e5', 'orange-600': '#ea580c', 'rose-600': '#e11d48'}
29 hex_color = color_map.get(color, '#2563eb')
30
31 with ui.card().classes('h-full p-4 rounded-xl border border-slate-100 shadow-sm hover:shadow-md transition-all relative overflow-hidden bg-white group'):
32 # Glow strip
33 ui.element('div').classes(f'absolute top-0 left-0 w-full h-1 bg-{color} opacity-60 group-hover:opacity-100 transition-opacity')
34
35 with ui.row().classes('justify-between items-center w-full mb-1'):
36 ui.label(title).classes('text-[9px] font-black text-slate-400 uppercase tracking-[0.15em]')
37 ui.icon(icon, size='14px').classes(f'text-{color} opacity-40')
38
39 with ui.row().classes('items-baseline gap-2'):
40 ui.label(val).classes('text-2xl font-black text-slate-800 tracking-tighter')
41 if trend:
42 is_up = '↑' in trend
43 tc = 'emerald-500' if is_up else 'rose-500'
44 ui.label(trend).classes(f'text-[8px] font-black text-{tc} bg-{tc}/10 px-1.5 py-0.5 rounded')
45
46 if spark_data:
47 ui.echart({
48 'grid': {'left': 0, 'right': 0, 'top': 2, 'bottom': 2},
49 'xAxis': {'show': False, 'type': 'category'},
50 'yAxis': {'show': False, 'type': 'value'},
51 'series': [{
52 'data': spark_data,
53 'type': 'line',
54 'smooth': True,
55 'symbol': 'none',
56 'lineStyle': {'width': 1.5, 'color': hex_color},
57 'areaStyle': {'opacity': 0.1, 'color': hex_color}
58 }]
59 }).classes('h-8 w-full mt-1')
60 elif subtitle:
61 ui.label(subtitle).classes('text-[8px] font-bold text-slate-400 mt-1 uppercase tracking-tight')
62
63def render_smart_project_card(p):
64 """Deep-info project card for AEC industrial control - COMPACT VERSION."""
65 risk_colors = {'Bajo': 'emerald', 'Medio': 'amber', 'Crítico': 'rose'}
66 rc = risk_colors.get(p.get('nivel_riesgo', 'Bajo'), 'emerald')
67
68 with ui.card().classes('p-0 rounded-xl border border-slate-100 shadow-sm bg-white overflow-hidden hover:shadow-md transition-all group'):
69 # Status Sidebar (Semaphore)
70 with ui.row().classes('w-full h-1.5').classes(f'bg-{rc}-500'): pass
71
72 with ui.column().classes('p-3.5 w-full gap-2.5'):
73 # Header
74 with ui.row().classes('w-full justify-between items-start no-wrap'):
75 with ui.column().classes('gap-0 flex-1'):
76 ui.label(p['nombre']).classes('text-[13px] font-black text-slate-800 line-clamp-1 group-hover:text-primary transition-colors')
77 ui.label(f"Resp: {p['responsable'] or 'Global'}").classes('text-[8px] font-bold text-slate-400 uppercase tracking-tighter')
78 ui.badge(p['nivel_riesgo'], color=f'{rc}-100').classes(f'text-{rc}-700 text-[8px] font-black px-1.5 rounded py-0.5 h-fit')
79
80 # Progress & KPI
81 with ui.column().classes('w-full gap-1.5'):
82 with ui.row().classes('w-full justify-between items-end text-[9px] font-black text-slate-400'):
83 ui.label('AVANCE')
84 ui.label(f"{p['avance']}%")
85 ui.linear_progress(p['avance']/100).classes('w-full h-1 rounded-full bg-slate-50 text-transparent').props(f'color={rc}-600')
86
87 # SST & Permissions Metadata
88 with ui.row().classes('w-full justify-between items-center mt-1 pt-2 border-t border-slate-50'):
89 with ui.row().classes('gap-2'):
90 # Permits
91 with ui.row().classes('items-center gap-1'):
92 ui.icon('description', size='10px', color='slate-300')
93 ui.label(str(p.get('permisos_pendientes', 0))).classes('text-[9px] font-black text-slate-600')
94 # SST Status
95 with ui.row().classes('items-center gap-1'):
96 ui.icon('verified_user' if p['estado_sst'] == 'Aprobado' else 'warning', size='10px', color='emerald-400' if p['estado_sst'] == 'Aprobado' else 'amber-500')
97 ui.label(p['estado_sst']).classes('text-[8px] font-black text-slate-500 uppercase')
98
99 ui.button(icon='open_in_new').props('flat round size=xs color=slate-300').classes('hover:bg-slate-50 scale-75')
100 