Procházet zdrojové kódy

Login Local con correo y selector de sucursal

OscarGil03 před 6 měsíci
rodič
revize
d78be1be14

+ 3 - 4
lib/main.dart

@@ -5,7 +5,6 @@ import 'package:sqflite_common_ffi/sqflite_ffi.dart';
 import 'dart:io'; // Importa esto para usar Platform.isWindows
 import 'dart:io'; // Importa esto para usar Platform.isWindows
 
 
 import 'services/productos_service.dart';
 import 'services/productos_service.dart';
-import 'views/main/main_screen.dart';
 import 'views/home/home_screen.dart';
 import 'views/home/home_screen.dart';
 import 'views/login/login_screen.dart';
 import 'views/login/login_screen.dart';
 import 'views/perfil/perfil_screen.dart';
 import 'views/perfil/perfil_screen.dart';
@@ -35,7 +34,7 @@ void main() async {
   ]).then((_) {
   ]).then((_) {
     runApp(MultiProvider(providers: [
     runApp(MultiProvider(providers: [
       ChangeNotifierProvider(create: (_) => LoginViewModel()),
       ChangeNotifierProvider(create: (_) => LoginViewModel()),
-      ChangeNotifierProvider(create: (_) => UsuariosViewModel()),
+      ChangeNotifierProvider(create: (_) => UsuarioViewModel()),
       ChangeNotifierProvider(create: (_) => ProfileViewModel()),
       ChangeNotifierProvider(create: (_) => ProfileViewModel()),
       ChangeNotifierProvider(create: (_) => CategoriaProductoViewModel()),
       ChangeNotifierProvider(create: (_) => CategoriaProductoViewModel()),
       ChangeNotifierProvider(create: (_) => ProductoViewModel()),
       ChangeNotifierProvider(create: (_) => ProductoViewModel()),
@@ -46,6 +45,7 @@ void main() async {
       ChangeNotifierProvider(create: (_) => DescuentoViewModel()),
       ChangeNotifierProvider(create: (_) => DescuentoViewModel()),
       ChangeNotifierProvider(create: (_) => VariableViewModel()),
       ChangeNotifierProvider(create: (_) => VariableViewModel()),
       ChangeNotifierProvider(create: (_) => SucursalViewModel()),
       ChangeNotifierProvider(create: (_) => SucursalViewModel()),
+      ChangeNotifierProvider(create: (_) => PermisoViewModel()),
       // Agrega aquí cualquier otro provider que necesites
       // Agrega aquí cualquier otro provider que necesites
     ], child: const MyApp()));
     ], child: const MyApp()));
   });
   });
@@ -61,9 +61,8 @@ class MyApp extends StatelessWidget {
       debugShowCheckedModeBanner: false,
       debugShowCheckedModeBanner: false,
       title: 'Yoshi Papas',
       title: 'Yoshi Papas',
       theme: AppTheme.lightTheme,
       theme: AppTheme.lightTheme,
-      initialRoute: 'main',
+      initialRoute: 'login',
       routes: {
       routes: {
-        'main': (context) => const MainScreen(),
         'login': (context) => const LoginScreen(),
         'login': (context) => const LoginScreen(),
         'home': (context) => const HomeScreen(),
         'home': (context) => const HomeScreen(),
         'perfil': (context) => const PerfilScreen(),
         'perfil': (context) => const PerfilScreen(),

+ 2 - 2
lib/models/login_model.dart

@@ -1,10 +1,10 @@
 class Login {
 class Login {
   String username;
   String username;
-  String password;
+  String? password;
 
 
   Login({
   Login({
     required this.username,
     required this.username,
-    required this.password,
+    this.password,
   });
   });
 
 
   Map<String, dynamic> toJson() {
   Map<String, dynamic> toJson() {

+ 11 - 12
lib/services/login_service.dart

@@ -1,17 +1,16 @@
-import '../data/api_response.dart';
+import '../models/models.dart';
+import '../services/repo_service.dart';
 
 
-import 'base_service.dart';
-import '../models/login_model.dart';
+class LoginService {
+  final RepoService<Usuario> _usuarioRepo = RepoService<Usuario>();
 
 
-class LoginService extends BaseService {
-  final endPoint = "/admin/iniciar-sesion";
+  Future<Usuario?> logIn(String email) async {
+    List<Usuario> usuarios = await _usuarioRepo.obtenerTodos();
+    Usuario? usuario = usuarios.firstWhere(
+      (user) => user.correo?.toLowerCase() == email.toLowerCase(),
+      orElse: () => null as Usuario,
+    );
 
 
-  Future<ApiResponse> logIn(String username, String password) async {
-    final login = Login(username: username, password: password);
-    var response = await post(endPoint,
-        withAuth: false,
-        body: login.toJson(),
-        headers: {'Content-Type': 'application/json'});
-    return ApiResponse(response);
+    return usuario;
   }
   }
 }
 }

+ 32 - 55
lib/viewmodels/login_view_model.dart

@@ -1,7 +1,7 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
-import '../data/api_response.dart';
-import '../data/session/session_storage.dart';
+import '../models/models.dart';
 import '../services/login_service.dart';
 import '../services/login_service.dart';
+import '../services/services.dart';
 
 
 enum Status { uninitialized, authenticated, authenticating, unauthenticated }
 enum Status { uninitialized, authenticated, authenticating, unauthenticated }
 
 
@@ -11,80 +11,57 @@ class LoginViewModel extends ChangeNotifier {
   bool hasErrors = false;
   bool hasErrors = false;
   Map<String, dynamic>? _errores = {};
   Map<String, dynamic>? _errores = {};
   bool _obscureText = true;
   bool _obscureText = true;
+  Usuario? _usuario;
   int? _idUsuario;
   int? _idUsuario;
+
+  // Getters
+  Usuario? get usuario => _usuario;
   Map<String, dynamic>? get errores => _errores;
   Map<String, dynamic>? get errores => _errores;
-  bool get obscureText => _obscureText;
-  int? get idUsuario => _idUsuario;
-  //List<String> _permisos = [];
-  //List<String> get permisos => _permisos;
 
 
-  String _nombre = "";
-  String get nombre => _nombre;
+  Future<void> login(String correo) async {
+    try {
+      print("Iniciando proceso de login para: $correo");
 
 
-  String _correo = "";
-  String get correo => _correo;
+      List<Usuario> usuarios = await RepoService<Usuario>().obtenerTodos();
+      print("Usuarios obtenidos de la base de datos: ${usuarios.length}");
 
 
-  String _error = "";
-  String get error => _error;
+      Usuario? usuario = usuarios.firstWhere(
+        (usuario) => usuario.correo?.toLowerCase() == correo.toLowerCase(),
+        orElse: () => Usuario(),
+      );
 
 
-  Future login(String username, String password) async {
-    try {
-      ApiResponse apiResponse = await LoginService().logIn(username, password);
-      _errores = {};
-      if (apiResponse.isOk) {
-        _idUsuario = apiResponse.detalle?['id'];
-        String token = apiResponse.detalle?['token'];
-        if (token.isNotEmpty) {
-          SessionStorage().saveToken(apiResponse.detalle?['token']);
-          SessionStorage().saveId(apiResponse.detalle?['id']);
-          SessionStorage().saveCorreo(apiResponse.detalle!['correo']);
-          SessionStorage().saveNombre(apiResponse.detalle!['nombre']);
-          _status = Status.authenticated;
-          notifyListeners();
-        }
-      }
-      if (apiResponse.isError) {
+      // Si el correo es válido
+      if (usuario.id != 0) {
+        print("Usuario encontrado: ${usuario.correo}, ID: ${usuario.id}");
+        _status = Status.authenticated;
+        _errores = null;
+        _idUsuario = usuario.id;
+        notifyListeners();
+      } else {
+        print("Correo no válido: $correo");
         hasErrors = true;
         hasErrors = true;
-        _errores = apiResponse.errores;
+        _errores = {'correo': 'El correo no existe en el sistema'};
         _status = Status.unauthenticated;
         _status = Status.unauthenticated;
         notifyListeners();
         notifyListeners();
       }
       }
     } catch (e) {
     } catch (e) {
+      print("Error durante el proceso de login: $e");
       _status = Status.unauthenticated;
       _status = Status.unauthenticated;
       notifyListeners();
       notifyListeners();
     }
     }
   }
   }
 
 
-  void checkSession() async {
-    var token = await SessionStorage().getToken();
-    var id = await SessionStorage().getId();
-    if (token != null && token.isNotEmpty) {
-      _status = Status.authenticated;
-      _idUsuario = id;
-    } else {
-      _status = Status.unauthenticated;
-    }
-    notifyListeners();
-  }
-
-  logOut() async {
-    await SessionStorage().clearToken();
-    _status = Status.unauthenticated;
-    notifyListeners();
-  }
-
   void showPassword() {
   void showPassword() {
     _obscureText = !_obscureText;
     _obscureText = !_obscureText;
     notifyListeners();
     notifyListeners();
   }
   }
 
 
-  setValores() async {
-    _nombre = (await SessionStorage().getNombre()).toString();
-    _correo = (await SessionStorage().getCorreo()).toString();
+  void logOut() {
+    print("Cerrando sesión...");
+    _usuario = null;
+    _idUsuario = null;
+    _status = Status.unauthenticated;
+    _errores = {};
     notifyListeners();
     notifyListeners();
   }
   }
-
-  bool validarEmail(String email) =>
-      RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
-          .hasMatch(email.trim());
 }
 }

+ 0 - 2
lib/views/home/home_screen.dart

@@ -27,8 +27,6 @@ class Formulario extends State<HomeScreen> {
     WidgetsBinding.instance.addPostFrameCallback((_) {
     WidgetsBinding.instance.addPostFrameCallback((_) {
       Provider.of<ProductoViewModel>(context, listen: false)
       Provider.of<ProductoViewModel>(context, listen: false)
           .sincronizarProductosYCategorias();
           .sincronizarProductosYCategorias();
-      Provider.of<SucursalViewModel>(context, listen: false)
-          .sincronizarSucursales();
     });
     });
   }
   }
 
 

+ 92 - 59
lib/views/login/login_screen.dart

@@ -1,9 +1,11 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 import 'package:provider/provider.dart';
 
 
+import '../../models/models.dart';
 import '../../themes/themes.dart';
 import '../../themes/themes.dart';
 import '../../viewmodels/viewmodels.dart';
 import '../../viewmodels/viewmodels.dart';
 import '../../widgets/widgets.dart';
 import '../../widgets/widgets.dart';
+import '../home/home_screen.dart';
 
 
 class LoginScreen extends StatefulWidget {
 class LoginScreen extends StatefulWidget {
   const LoginScreen({Key? key}) : super(key: key);
   const LoginScreen({Key? key}) : super(key: key);
@@ -14,24 +16,48 @@ class LoginScreen extends StatefulWidget {
 
 
 class _LoginScreenState extends State<LoginScreen> {
 class _LoginScreenState extends State<LoginScreen> {
   final _correo = TextEditingController();
   final _correo = TextEditingController();
-  final _pass = TextEditingController();
+  Sucursal? _selectedSucursal;
 
 
   @override
   @override
   void dispose() {
   void dispose() {
     super.dispose();
     super.dispose();
     _correo.dispose();
     _correo.dispose();
-    _pass.dispose();
+  }
+
+  @override
+  void initState() {
+    super.initState();
+
+    WidgetsBinding.instance.addPostFrameCallback((_) {
+      final sucursalViewModel =
+          Provider.of<SucursalViewModel>(context, listen: false);
+      sucursalViewModel.sincronizarSucursales().then((_) {
+        setState(() {
+          final sucursales = sucursalViewModel.sucursales;
+          _selectedSucursal = sucursales.firstWhere(
+            (sucursal) => sucursal.seleccionado == 1,
+            orElse: () => sucursales.isNotEmpty ? sucursales[0] : Sucursal(),
+          );
+        });
+      });
+
+      Provider.of<PermisoViewModel>(context, listen: false)
+          .sincronizarPermisos();
+      Provider.of<UsuarioViewModel>(context, listen: false)
+          .sincronizarUsuarios();
+    });
   }
   }
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     final size = MediaQuery.sizeOf(context);
     final size = MediaQuery.sizeOf(context);
     final loginViewModel = Provider.of<LoginViewModel>(context);
     final loginViewModel = Provider.of<LoginViewModel>(context);
-    final obscureText = loginViewModel.obscureText;
+    final sucursalViewModel = Provider.of<SucursalViewModel>(context);
+    final sucursales = sucursalViewModel.sucursales;
     final errores = loginViewModel.errores;
     final errores = loginViewModel.errores;
 
 
     return Scaffold(
     return Scaffold(
-      backgroundColor: Color.fromRGBO(245, 245, 245, 1),
+      backgroundColor: const Color.fromRGBO(245, 245, 245, 1),
       body: SingleChildScrollView(
       body: SingleChildScrollView(
         child: Padding(
         child: Padding(
           padding: const EdgeInsets.all(8.0),
           padding: const EdgeInsets.all(8.0),
@@ -49,6 +75,7 @@ class _LoginScreenState extends State<LoginScreen> {
                 textAlign: TextAlign.center,
                 textAlign: TextAlign.center,
               ),
               ),
               const SizedBox(height: 20),
               const SizedBox(height: 20),
+              // Campo de correo
               SizedBox(
               SizedBox(
                 width: size.width < 1200 ? size.width : size.width * .35,
                 width: size.width < 1200 ? size.width : size.width * .35,
                 child: Card(
                 child: Card(
@@ -59,7 +86,26 @@ class _LoginScreenState extends State<LoginScreen> {
                     padding: const EdgeInsets.all(15),
                     padding: const EdgeInsets.all(15),
                     child: Column(
                     child: Column(
                       children: [
                       children: [
-                        //CORREO ELECTRONICO
+                        AppDropdownModel<Sucursal>(
+                          etiqueta: 'Seleccione una sucursal',
+                          hint: 'Elija una sucursal',
+                          selectedValue: _selectedSucursal,
+                          onChanged: (Sucursal? newValue) {
+                            setState(() {
+                              _selectedSucursal = newValue;
+                            });
+                          },
+                          items: sucursales.map((Sucursal sucursal) {
+                            return DropdownMenuItem<Sucursal>(
+                              value: sucursal,
+                              child: Text(
+                                sucursal.nombre ?? '',
+                                style: const TextStyle(color: Colors.black),
+                              ),
+                            );
+                          }).toList(),
+                        ),
+                        const SizedBox(height: 5),
                         AppTextField(
                         AppTextField(
                           autofillHints: [AutofillHints.username],
                           autofillHints: [AutofillHints.username],
                           prefixIcon: const Icon(Icons.mail),
                           prefixIcon: const Icon(Icons.mail),
@@ -69,38 +115,13 @@ class _LoginScreenState extends State<LoginScreen> {
                           controller: _correo,
                           controller: _correo,
                           keyboardType: TextInputType.emailAddress,
                           keyboardType: TextInputType.emailAddress,
                         ),
                         ),
-                        const SizedBox(height: 15),
-                        //CONTRASEÑA
-                        AppTextField(
-                          autofillHints: [AutofillHints.newPassword],
-                          maxLines: 1,
-                          obscureText: obscureText,
-                          prefixIcon: const Icon(Icons.lock),
-                          suffixIcon: IconButton(
-                            onPressed: () {
-                              loginViewModel.showPassword();
-                            },
-                            icon: obscureText
-                                ? const Icon(Icons.remove_red_eye_outlined)
-                                : const Icon(Icons.remove_red_eye),
-                          ),
-                          onSubmitted: (v) async {
-                            if (v.isEmpty) return;
-                            await loginViewModel.login(
-                                _correo.text, _pass.text);
-                          },
-                          etiqueta: 'Contraseña',
-                          hintText: 'Introduzca su contraseña',
-                          errorText: errores?['clave'],
-                          controller: _pass,
-                          keyboardType: TextInputType.visiblePassword,
-                        ),
                       ],
                       ],
                     ),
                     ),
                   ),
                   ),
                 ),
                 ),
               ),
               ),
               const SizedBox(height: 20),
               const SizedBox(height: 20),
+              // Botón de login
               Align(
               Align(
                 alignment: Alignment.center,
                 alignment: Alignment.center,
                 child: SizedBox(
                 child: SizedBox(
@@ -117,35 +138,47 @@ class _LoginScreenState extends State<LoginScreen> {
                           MaterialStatePropertyAll(AppTheme.secondary),
                           MaterialStatePropertyAll(AppTheme.secondary),
                     ),
                     ),
                     onPressed: () async {
                     onPressed: () async {
-                      await loginViewModel.login(_correo.text, _pass.text);
-                      String mensaje = "";
-                      if (loginViewModel.errores!["correo"] != null) {
-                        mensaje += "\n${loginViewModel.errores!["correo"]}";
-                      }
-                      if (loginViewModel.errores!["pass"] != null) {
-                        mensaje += "\n${loginViewModel.errores!["pass"]}";
-                      }
-                      if (mensaje.isNotEmpty && context.mounted) {
-                        return showDialog(
-                          context: context,
-                          builder: (context) {
-                            return AlertDialog(
-                              title: const Text("Alerta"),
-                              content: Text(mensaje),
-                              actions: [
-                                Row(children: [
-                                  Expanded(
-                                      child: TextButton(
-                                    onPressed: () async {
-                                      Navigator.pop(context);
-                                    },
-                                    child: const Text('Continuar'),
-                                  ))
-                                ])
-                              ],
-                            );
-                          },
+                      await loginViewModel.login(_correo.text);
+
+                      if (loginViewModel.status == Status.authenticated) {
+                        // Actualizar la sucursal seleccionada si el login es exitoso
+                        if (_selectedSucursal != null) {
+                          await sucursalViewModel
+                              .setSelectedSucursal(_selectedSucursal!);
+                        }
+
+                        Navigator.pushReplacement(
+                          context,
+                          MaterialPageRoute(
+                              builder: (context) => const HomeScreen()),
                         );
                         );
+                      } else {
+                        String mensaje = "";
+                        if (loginViewModel.errores?["correo"] != null) {
+                          mensaje += "\n${loginViewModel.errores!["correo"]}";
+                        }
+                        if (mensaje.isNotEmpty && context.mounted) {
+                          return showDialog(
+                            context: context,
+                            builder: (context) {
+                              return AlertDialog(
+                                title: const Text("Alerta"),
+                                content: Text(mensaje),
+                                actions: [
+                                  Row(children: [
+                                    Expanded(
+                                        child: TextButton(
+                                      onPressed: () async {
+                                        Navigator.pop(context);
+                                      },
+                                      child: const Text('Continuar'),
+                                    ))
+                                  ])
+                                ],
+                              );
+                            },
+                          );
+                        }
                       }
                       }
                     },
                     },
                     child: const Row(
                     child: const Row(

+ 12 - 5
lib/widgets/app_drawer.dart

@@ -30,12 +30,12 @@ class AppDrawer extends StatelessWidget {
             child: const Text('Cancelar', style: TextStyle(color: Colors.red)),
             child: const Text('Cancelar', style: TextStyle(color: Colors.red)),
           ),
           ),
           TextButton(
           TextButton(
-            onPressed: () {
-              Navigator.pop(context);
-              Navigator.pop(context);
+            onPressed: () async {
               Provider.of<LoginViewModel>(context, listen: false).logOut();
               Provider.of<LoginViewModel>(context, listen: false).logOut();
-              Navigator.of(context)
-                  .pushNamedAndRemoveUntil('main', (route) => false);
+              Navigator.of(context).pushNamedAndRemoveUntil(
+                'login',
+                (route) => false,
+              );
             },
             },
             child: const Text('Aceptar'),
             child: const Text('Aceptar'),
           ),
           ),
@@ -173,6 +173,13 @@ class AppDrawer extends StatelessWidget {
                     ),
                     ),
                   ],
                   ],
                 ),
                 ),
+                ListTile(
+                  leading: const Icon(Icons.logout),
+                  title: const Text('Cerrar sesión'),
+                  onTap: () {
+                    _showExitConfirmationDialog(context);
+                  },
+                ),
               ],
               ],
             ),
             ),
           ),
           ),