CoolFace
Apppublic

fmatituy/selectospromanager

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
auth_pages.py121 linesDownload Raw Back to modules
1from nicegui import ui, app, run
2from database.queries import get_all_roles, create_user
3from database.db import hash_password
4from services.auth import authenticate, is_authenticated
5from modules.layout import init_pwa_headers
6from core.events import event_bus
7from core.logger import logger
8
9def register_auth_pages():
10    @ui.page('/login')
11    def login_page(return_to: str = '/'):
12        init_pwa_headers()
13        # Bypass si ya está autenticado realmente
14        if is_authenticated():
15            ui.navigate.to(return_to)
16            return
17            
18        ui.colors(primary='#1e40af', secondary='#475569')
19        main_container = ui.column().classes('w-full h-screen items-center justify-center p-0 bg-slate-900 relative overflow-hidden')
20
21        with main_container:
22            # Capa de Imagen Full Bleed
23            with ui.element('div').classes('absolute inset-0 w-full h-full p-0 overflow-hidden'):
24                # Imagen de fondo sin márgenes (Ultra HD)
25                ui.image('/static/app/login_bg.png').classes('absolute inset-0 w-full h-full object-cover')
26                
27                # Degradado oscuro sutil para legibilidad del logo
28                with ui.element('div').classes('absolute inset-0 bg-gradient-to-b from-black/40 via-transparent to-black/20'):
29                    pass
30                
31                pass
32                
33                pass
34                
35            with ui.card().classes('w-full max-w-[360px] p-8 shadow-2xl rounded-[2.5rem] z-10 bg-white border border-slate-200 mx-4 transition-all hover:shadow-blue-900/10'):
36                with ui.column().classes('items-center w-full mb-6'):
37                    with ui.element('div').classes('w-16 h-16 bg-blue-50 rounded-2xl flex items-center justify-center mb-6 shadow-inner border border-blue-100'):
38                        ui.icon('settings', size='40px', color='primary').classes('animate-[spin_6s_linear_infinite]')
39                        
40                    with ui.row().classes('items-center gap-1 sm:gap-1.5 leading-none'):
41                        ui.label('PRO').classes('text-2xl sm:text-3xl font-black tracking-tighter text-blue-600')
42                        with ui.element('div').classes('w-6 h-6 sm:w-8 sm:h-8 rounded-full flex items-center justify-center bg-blue-600 shadow-lg'):
43                            ui.icon('settings', size='14px', color='white') # Corregido: nicegui no soporta clases responsive en size prop directamente, se maneja estilo
44                        ui.label('MANAGER').classes('text-2xl sm:text-3xl font-black tracking-tighter text-slate-800')
45                    ui.label('ERP SISTEMA INTEGRAL DE GESTIÓN DE PROYECTOS').classes('text-[8px] sm:text-[9px] font-bold tracking-[0.2em] text-slate-400 uppercase mt-1 text-center px-2')
46                    
47                username = ui.input('Usuario').props('outlined rounded-3xl autocomplete="off"').classes('w-full text-sm mb-4 bg-slate-50/50')
48                password = ui.input('Contraseña', password=True, password_toggle_button=True).props('outlined rounded-3xl autocomplete="new-password"').classes('w-full text-sm mb-8 bg-slate-50/50')
49                
50                login_btn = ui.button('INICIAR SESIÓN').props('unelevated color=primary size=lg rounded-3xl').classes('w-full py-4 font-black shadow-xl shadow-blue-200 text-xs tracking-widest mb-4')
51                
52                async def try_login(e=None):
53                    u, p = username.value, password.value
54                    login_btn.props('loading')
55                    try:
56                        user_data = await run.io_bound(authenticate, u, p)
57                        if user_data:
58                            app.storage.user.update(user_data)
59                            app.storage.user['authenticated'] = True
60                            app.storage.browser['auto_user'] = u
61                            app.storage.browser['auto_pass'] = p
62                            
63                            # Disparar evento de login para otros módulos (decopupling)
64                            await event_bus.emit("auth:login", {"username": u, "rol": user_data.get("rol")})
65                            logger.info(f"Usuario {u} autenticado exitosamente.")
66                            
67                            ui.navigate.to(return_to)
68                        else:
69                            login_btn.props(remove='loading')
70                            ui.notify('Credenciales incorrectas', type='negative', position='top', icon='error')
71                    except Exception as ex:
72                        login_btn.props(remove='loading')
73                        if 'already been built' in str(ex):
74                            ui.run_javascript(f'window.location.href = "{return_to}"')
75                        else:
76                            ui.notify(f'Error de acceso: {str(ex)}', type='negative')
77
78                login_btn.on_click(try_login)
79                username.on('keydown.enter', try_login)
80                password.on('keydown.enter', try_login)
81                ui.label('Acceso restringido a personal autorizado.').classes('text-[10px] text-center w-full text-slate-400 font-bold mb-8')
82
83        stored_user = app.storage.browser.get('auto_user')
84        stored_pass = app.storage.browser.get('auto_pass')
85        if stored_user and stored_pass:
86            async def background_auto_login():
87                try:
88                    user_data = await run.io_bound(authenticate, stored_user, stored_pass)
89                    if user_data:
90                        app.storage.user.update(user_data)
91                        app.storage.user['authenticated'] = True
92                        ui.navigate.to(return_to)
93                except: pass
94            ui.timer(0.5, background_auto_login, once=True)
95
96    @ui.page('/registro')
97    def registro_page():
98        ui.navigate.to('/login')
99        ui.notify('El registro manual está deshabilitado. Contacte al administrador.', type='warning')
100        return
101
102    @ui.page('/descargar/apk')
103    def descargar_apk_page():
104        with ui.column().classes('w-full h-screen items-center justify-center bg-slate-900 p-8 text-center text-white'):
105            ui.icon('android', size='80px', color='emerald-500').classes('mb-6')
106            ui.label('Selectos ProManager para Android').classes('text-3xl font-black mb-2')
107            ui.label('Versión 1.0.3 stable').classes('text-xs text-emerald-400 font-bold uppercase tracking-widest mb-6 p-2 bg-emerald-950 rounded-lg')
108            
109            with ui.card().classes('w-full max-w-sm p-8 rounded-[2.5rem] bg-white/5 border border-white/10 text-left'):
110                ui.label('INSTRUCCIONES DE INSTALACIÓN').classes('text-[10px] font-black text-slate-400 mb-6 tracking-widest')
111                steps = [
112                    '1. Descarga el archivo APK pulsando el botón abajo.',
113                    '2. Abre el archivo en tu celular.',
114                    '3. Si aparece "Bloqueado por Play Protect", selecciona "Instalar de todas formas".',
115                    '4. ¡Inicia sesión y disfruta de la potencia de Selectos!'
116                ]
117                for s in steps:
118                    ui.label(s).classes('text-sm mb-4 leading-relaxed font-medium')
119                ui.button('DESCARGAR AHORA (APK)', icon='download', on_click=lambda: ui.download('/static/apk/Selectos_ProManager.apk')).props('unelevated color=emerald-600 rounded-2xl h-14').classes('w-full font-black mt-6 shadow-2xl shadow-emerald-900/40')
120            ui.button('Volver al ERP', icon='arrow_back', on_click=lambda: ui.navigate.to('/')).props('flat color=white').classes('mt-8 opacity-50')
121