Procházet zdrojové kódy

Estructura abtstract factory y Cierre de sesion

OscarGil03 před 2 měsíci
rodič
revize
fd8f487ac1

binární
assets/Turquessa.png


+ 27 - 0
lib/core/models/mesa_model.dart

@@ -44,6 +44,33 @@ class Mesa extends Basico {
     }..addAll(super.toJson());
   }
 
+  Map<String, dynamic> toMap() {
+    return {
+      'id': id,
+      'idSucursal': idSucursal ?? 0,
+      'nombre': nombre ?? '',
+      'clave': clave ?? '',
+      'posicion': posicion ?? '',
+      'activa': (activa == true) ? 1 : 0,
+      'creado': creado?.toIso8601String(),
+      'modificado': modificado?.toIso8601String(),
+      'eliminado': eliminado?.toIso8601String(),
+    };
+  }
+
+  static Mesa fromMap(Map<String, dynamic> map) {
+    // Ejemplo de parseo, usando tu Basico.parseInt, etc. si gustas:
+    final mesa = Mesa(
+      id: map['id'] as int,
+      idSucursal: map['idSucursal'] as int?,
+      nombre: map['nombre'] as String?,
+      clave: map['clave'] as String?,
+      posicion: map['posicion'] as String?,
+      activa: (map['activa'] == 1),
+    );
+    return mesa;
+  }
+
   Mesa.fromJson(Map<String, dynamic> json) {
     super.parseJson(json);
     idSucursal = Basico.parseInt(json['idSucursal']);

+ 32 - 0
lib/core/services/reposit_factory.dart

@@ -0,0 +1,32 @@
+import 'package:sqflite/sqflite.dart';
+
+import 'repository.dart';
+import 'sqlite_repository.dart';
+
+abstract class RepositoryFactory {
+  Repository<T> getRepository<T>({
+    required String tableName,
+    required T Function(Map<String, dynamic>) fromMap,
+    required Map<String, dynamic> Function(T) toMap,
+  });
+}
+
+class SQLiteRepositoryFactory implements RepositoryFactory {
+  final Database database;
+
+  SQLiteRepositoryFactory(this.database);
+
+  @override
+  Repository<T> getRepository<T>({
+    required String tableName,
+    required T Function(Map<String, dynamic>) fromMap,
+    required Map<String, dynamic> Function(T) toMap,
+  }) {
+    return SQLiteRepository<T>(
+      database: database,
+      tableName: tableName,
+      fromMap: fromMap,
+      toMap: toMap,
+    );
+  }
+}

+ 12 - 0
lib/core/services/repository.dart

@@ -0,0 +1,12 @@
+abstract class Repository<T> {
+  Future<T?> consultarPorId(int id);
+  Future<List<T>> consultarPorCondicion(bool Function(T) condicion);
+  Future<void> guardar(T entidad);
+  Future<void> eliminar(int id);
+  Future<List<T>> consultarConOrden(
+      {String? orderBy,
+      int? limit,
+      int? offset,
+      String? where,
+      List<Object?>? whereArgs});
+}

+ 3 - 0
lib/core/services/services.dart

@@ -4,3 +4,6 @@ export 'profile_service.dart';
 export 'database_service.dart';
 export 'api_response.dart';
 export 'session_storage.dart';
+export 'reposit_factory.dart';
+export 'repository.dart';
+export 'sqlite_repository.dart';

+ 67 - 0
lib/core/services/sqlite_repository.dart

@@ -0,0 +1,67 @@
+import 'repository.dart';
+import 'package:sqflite/sqflite.dart';
+
+class SQLiteRepository<T> implements Repository<T> {
+  final Database database;
+  final String tableName;
+  final T Function(Map<String, dynamic>) fromMap;
+  final Map<String, dynamic> Function(T) toMap;
+
+  SQLiteRepository({
+    required this.database,
+    required this.tableName,
+    required this.fromMap,
+    required this.toMap,
+  });
+
+  @override
+  Future<T?> consultarPorId(int id) async {
+    final maps = await database.query(
+      tableName,
+      where: 'id = ?',
+      whereArgs: [id],
+    );
+
+    if (maps.isNotEmpty) {
+      return fromMap(maps.first);
+    }
+    return null;
+  }
+
+  @override
+  Future<List<T>> consultarPorCondicion(bool Function(T) condicion) async {
+    final maps = await database.query(tableName);
+    return maps.map((map) => fromMap(map)).where(condicion).toList();
+  }
+
+  @override
+  Future<void> guardar(T entidad) async {
+    final map = toMap(entidad);
+    await database.insert(tableName, map,
+        conflictAlgorithm: ConflictAlgorithm.replace);
+  }
+
+  @override
+  Future<void> eliminar(int id) async {
+    await database.delete(tableName, where: 'id = ?', whereArgs: [id]);
+  }
+
+  @override
+  Future<List<T>> consultarConOrden(
+      {String? orderBy,
+      int? limit,
+      int? offset,
+      String? where,
+      List<Object?>? whereArgs}) async {
+    final maps = await database.query(
+      tableName,
+      orderBy: orderBy,
+      limit: limit,
+      offset: offset,
+      where: where,
+      whereArgs: whereArgs,
+    );
+
+    return maps.map(fromMap).toList();
+  }
+}

+ 15 - 1
lib/main.dart

@@ -4,6 +4,8 @@ import 'package:provider/provider.dart';
 import 'package:sqflite_common_ffi/sqflite_ffi.dart';
 import 'dart:io';
 
+import 'core/models/models.dart';
+import 'core/services/services.dart';
 import 'mvvm/views/home/home_screen.dart';
 import 'mvvm/views/login/login_screen.dart';
 import '/utils/themes.dart';
@@ -20,6 +22,17 @@ void main() async {
     databaseFactory = databaseFactoryFfi;
   }
 
+  Database? db = await DatabaseService().db;
+
+  final repositoryFactory = SQLiteRepositoryFactory(db!);
+
+  // 3) Obtener el repositorio de Mesa
+  final mesaRepository = repositoryFactory.getRepository<Mesa>(
+    tableName: 'Mesa',
+    fromMap: Mesa.fromMap,
+    toMap: (m) => m.toMap(),
+  );
+
   SystemChrome.setPreferredOrientations([
     DeviceOrientation.landscapeRight,
     DeviceOrientation.landscapeLeft,
@@ -30,7 +43,8 @@ void main() async {
       ChangeNotifierProvider(create: (_) => PermisoViewModel()),
       ChangeNotifierProvider(create: (_) => UsuarioViewModel()),
       ChangeNotifierProvider(create: (_) => ProductoViewModel()),
-      ChangeNotifierProvider(create: (_) => MesaViewModel()),
+      ChangeNotifierProvider(
+          create: (_) => MesaViewModel(mesaRepository: mesaRepository)),
       ChangeNotifierProvider(create: (_) => CategoriaProductoViewModel()),
       // Agrega aquí cualquier otro provider que necesites
     ], child: const MyApp()));

+ 0 - 11
lib/mvvm/viewmodels/home_view_model.dart

@@ -1,11 +0,0 @@
-import 'package:flutter/material.dart';
-
-class HomeViewModel extends ChangeNotifier {
-  int _selectedIndex = 0;
-  int get selectedIndex => _selectedIndex;
-
-  void setIndex(int index) {
-    _selectedIndex = index;
-    notifyListeners();
-  }
-}

+ 27 - 40
lib/mvvm/viewmodels/mesa_view_model.dart

@@ -1,9 +1,14 @@
 import 'package:flutter/material.dart';
 import 'package:sqflite/sqflite.dart';
+import '../../core/services/reposit_factory.dart';
 import '../../core/services/services.dart';
 import '../../core/models/models.dart';
 
 class MesaViewModel extends ChangeNotifier {
+  final Repository<Mesa> mesaRepository;
+
+  MesaViewModel({required this.mesaRepository});
+
   String _busqueda = "";
   String get busqueda => _busqueda;
 
@@ -33,33 +38,15 @@ class MesaViewModel extends ChangeNotifier {
     notifyListeners();
   }
 
-  Future<void> fetchLocalAll({
-    int page = 1,
-    bool sinLimite = false,
-    String orderBy = 'id ASC',
-  }) async {
-    _currentPage = page;
-    var db = await DatabaseService().db;
-
-    if (!sinLimite) {
-      int? count = Sqflite.firstIntValue(
-          await db!.rawQuery('SELECT COUNT(*) FROM Mesa'));
-      _totalMesas = count ?? 0;
-    }
-
-    String? limitOffsetClause;
-    if (!sinLimite) {
-      int offset = (_limit * (page - 1));
-      limitOffsetClause = 'LIMIT $_limit OFFSET $offset';
-    } else {
-      limitOffsetClause = '';
-    }
-
-    var query = await db!
-        .rawQuery('SELECT * FROM Mesa ORDER BY $orderBy $limitOffsetClause');
+  Future<void> fetchLocal() async {
+    _isLoading = true;
+    notifyListeners();
 
-    _mesas = query.map((element) => Mesa.fromJson(element)).toList();
+    final resultado =
+        await mesaRepository.consultarConOrden(orderBy: 'nombre ASC');
 
+    _mesas = resultado;
+    _isLoading = false;
     notifyListeners();
   }
 
@@ -88,7 +75,7 @@ class MesaViewModel extends ChangeNotifier {
   Future<void> addMesa(Mesa mesa) async {
     mesa.creado = DateTime.now().toUtc();
     await DatabaseService().guardar(mesa);
-    await fetchLocalAll();
+    await fetchLocal();
   }
 
   Future<void> updateMesa(Mesa mesa) async {
@@ -96,7 +83,7 @@ class MesaViewModel extends ChangeNotifier {
     try {
       mesa.modificado = DateTime.now().toUtc();
       await DatabaseService().guardar(mesa);
-      await fetchLocalAll();
+      await fetchLocal();
     } catch (e) {
       debugPrint('Error updating mesa: $e');
     }
@@ -105,7 +92,7 @@ class MesaViewModel extends ChangeNotifier {
 
   Future<void> deleteMesa(int id) async {
     await DatabaseService().eliminar<Mesa>(id);
-    fetchLocalAll();
+    fetchLocal();
   }
 
   void setIsLoading(bool loading) {
@@ -113,17 +100,17 @@ class MesaViewModel extends ChangeNotifier {
     notifyListeners();
   }
 
-  void nextPage() {
-    if (_currentPage < totalPages) {
-      fetchLocalAll(page: _currentPage + 1);
-    }
-  }
+  // void nextPage() {
+  //   if (_currentPage < totalPages) {
+  //     fetchLocal(page: _currentPage + 1);
+  //   }
+  // }
 
-  void previousPage() {
-    if (_currentPage > 1) {
-      fetchLocalAll(page: _currentPage - 1);
-    }
-  }
+  // void previousPage() {
+  //   if (_currentPage > 1) {
+  //     fetchLocal(page: _currentPage - 1);
+  //   }
+  // }
 
   Future<bool> isMesaActive(String clave) async {
     var db = await DatabaseService().db;
@@ -149,7 +136,7 @@ class MesaViewModel extends ChangeNotifier {
   //     where: 'id = ?',
   //     whereArgs: [mesa.id],
   //   );
-  //   fetchLocalAll();
+  //   fetchLocal();
   // }
 
   //todo: Cambio de estado de mesa provisional
@@ -169,7 +156,7 @@ class MesaViewModel extends ChangeNotifier {
         "claveSucursal": claveSucursal!,
         "limite": "-1"
       };
-
+      //? peticion de Mesa
       final response = ApiResponse(await BaseService()
           .get('/pos/mesa', queryParameters: parametros, withAuth: true));
 

+ 83 - 0
lib/mvvm/viewmodels/pedido_view_model.dart

@@ -0,0 +1,83 @@
+import 'package:flutter/material.dart';
+import 'package:turquessa_mesas_hoster/core/services/database_service.dart';
+
+import '../../core/models/pedido_model.dart';
+import '../../core/services/api_response.dart';
+import '../../core/services/base_service.dart';
+
+class PedidoViewModel extends ChangeNotifier {
+  List<Pedido> _pedidos = [];
+  bool _isLoading = false;
+
+  List<Pedido> get pedidos => _pedidos;
+  bool get isLoading => _isLoading;
+
+  Future<void> fetchLocalPedidos() async {
+    var db = await DatabaseService().db;
+    var query = await db!
+        .query('Pedido', where: 'eliminado IS NULL', orderBy: 'id asc');
+    _pedidos = query.map((element) => Pedido.fromJson(element)).toList();
+    notifyListeners();
+  }
+
+  // Future<void> setSelectedPedido(Pedido pedido) async {
+  //   var db = await DatabaseService().db;
+  //   await db!.update(
+  //     'Pedido',
+  //     {'seleccionado': 0},
+  //   );
+  //   pedido.seleccionado = 1;
+  //   await DatabaseService().guardar(pedido);
+
+  //   await fetchLocalPedidos();
+  // }
+
+  Future<bool> sincronizarPedidos() async {
+    try {
+      final response =
+          ApiResponse(await BaseService().get('/pos/mesa?expand=pedidos'));
+      if (response.isOk && response.resultados != null) {
+        List<Pedido> pedidosApi =
+            response.resultados!.map((json) => Pedido.fromJson(json)).toList();
+
+        // if (pedidosApi.isNotEmpty) {
+        //   await DatabaseService().sincronizarPedidos(pedidosApi);
+        //   await fetchLocalPedidos();
+        //   notifyListeners();
+        //   return true;
+        // }
+      }
+      return false;
+    } catch (e) {
+      print('Error al sincronizar pedidos: $e');
+      return false;
+    }
+  }
+
+  // // Future<void> sincronizarPedidosDesdeApi() async {
+  //   setIsLoading(true);
+  //   try {
+  //     final response = ApiResponse(await BaseService().get('/pos/pedido'));
+
+  //     if (response.isOk && response.resultados != null) {
+  //       List<Pedido> pedidosApi =
+  //           response.resultados!.map((json) => Pedido.fromApi(json)).toList();
+
+  //       if (pedidosApi.isNotEmpty) {
+  //         await DatabaseService().sincronizarPedidos(pedidosApi);
+  //         await fetchLocalPedidos();
+  //         notifyListeners();
+  //       }
+  //     }
+  //   } catch (e) {
+  //     print('Error al sincronizar pedidos: $e');
+  //   } finally {
+  //     setIsLoading(false);
+  //   }
+  // }
+
+  void setIsLoading(bool value) {
+    _isLoading = value;
+    notifyListeners();
+  }
+}

+ 0 - 1
lib/mvvm/viewmodels/viewmodels.dart

@@ -1,7 +1,6 @@
 export '../viewmodels/login_view_model.dart';
 export '../viewmodels/sucursal_view_model.dart';
 export '../viewmodels/permiso_view_model.dart';
-export '../viewmodels/home_view_model.dart';
 export '../viewmodels/usuarios_view_model.dart';
 export '../viewmodels/mesa_view_model.dart';
 export '../viewmodels/producto_view_model.dart';

+ 78 - 112
lib/mvvm/views/home/home_screen.dart

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 import 'package:turquessa_mesas_hoster/core/models/mesa_model.dart';
 import 'package:turquessa_mesas_hoster/utils/widgets/custom_appbar.dart';
+import 'package:turquessa_mesas_hoster/utils/widgets/navigation_rail.dart';
 
 import '../../../utils/widgets/ordenes_card.dart';
 import '../../viewmodels/viewmodels.dart';
@@ -25,7 +26,7 @@ class Formulario extends State<HomeScreen> {
           .sincronizarProductosYCategorias();
 
       await mesaViewModel.sincronizarMesas();
-      await mesaViewModel.fetchLocalAll(sinLimite: true, orderBy: 'nombre ASC');
+      await mesaViewModel.fetchLocal();
     });
   }
 
@@ -41,45 +42,7 @@ class Formulario extends State<HomeScreen> {
       ),
       body: Row(
         children: [
-          NavigationRail(
-            backgroundColor: Color.fromARGB(255, 25, 30, 41),
-            selectedIndex: _selectedIndex,
-            onDestinationSelected: (int index) {
-              setState(() {
-                _selectedIndex = index;
-              });
-
-              if (index == 3) {
-                loginViewModel.showExitConfirmationDialog(context);
-              }
-            },
-            labelType: NavigationRailLabelType.all,
-            destinations: const [
-              NavigationRailDestination(
-                icon: Icon(Icons.home, color: Colors.white),
-                selectedIcon: Icon(Icons.home_filled),
-                label: Text('Inicio'),
-              ),
-              NavigationRailDestination(
-                icon: Icon(Icons.search),
-                selectedIcon: Icon(
-                  Icons.search_rounded,
-                  color: Colors.white,
-                ),
-                label: Text('Buscar'),
-              ),
-              NavigationRailDestination(
-                icon: Icon(Icons.settings),
-                selectedIcon: Icon(Icons.settings_rounded, color: Colors.white),
-                label: Text('Ajustes'),
-              ),
-              NavigationRailDestination(
-                icon: Icon(Icons.logout),
-                selectedIcon: Icon(Icons.logout, color: Colors.white),
-                label: Text('Cerrar Sesión'),
-              ),
-            ],
-          ),
+          CustomNavigationRail(selectedIndex: _selectedIndex),
           Expanded(
             child: Center(
               child: GridView.builder(
@@ -100,10 +63,7 @@ class Formulario extends State<HomeScreen> {
                       });
                     },
                     child: TableCard(
-                      icon: Icons.table_chart,
-                      //TODO: Agregar campo de estatus de la mesa para definir los colores
-                      color: (mesa.activa == true) ? Colors.blue : Colors.grey,
-                      title: mesa.nombre ?? 'Mesa sin nombre',
+                      mesa: mesa,
                     ),
                   );
                 },
@@ -140,38 +100,6 @@ class Formulario extends State<HomeScreen> {
   }
 }
 
-class TableCard extends StatelessWidget {
-  final IconData icon;
-  final Color color;
-  final String title;
-  const TableCard(
-      {super.key,
-      required this.icon,
-      required this.color,
-      required this.title});
-
-  @override
-  Widget build(BuildContext context) {
-    return Card(
-      color: color,
-      child: Column(
-        mainAxisAlignment: MainAxisAlignment.center,
-        children: [
-          Icon(
-            icon,
-            size: 50,
-            color: Colors.white,
-          ),
-          Text(
-            title,
-            style: const TextStyle(color: Colors.white, fontSize: 20),
-          )
-        ],
-      ),
-    );
-  }
-}
-
 class TablaDetalles extends StatelessWidget {
   final Mesa table;
   final EstadoPedido status;
@@ -269,9 +197,9 @@ class TablaDetalles extends StatelessWidget {
                       ],
                     ),
                     const SizedBox(height: 16),
-                    IconDataByStatus(status: status),
+                    // IconDataByStatus(status: status),
                     const SizedBox(height: 16),
-                    const OrdenesScreen(),
+                    OrdenesScreen(),
                   ],
                 ),
               ),
@@ -283,49 +211,87 @@ class TablaDetalles extends StatelessWidget {
   }
 }
 
-class IconDataByStatus extends StatelessWidget {
-  final EstadoPedido status;
-  const IconDataByStatus({Key? key, required this.status});
+class TableCard extends StatelessWidget {
+  final Mesa mesa;
+  const TableCard({
+    super.key,
+    required this.mesa,
+  });
 
   @override
   Widget build(BuildContext context) {
+    final status = mesa.estado ?? EstadoPedido.disponible;
+
+    Color backgroundColor;
+    Color iconColor;
+    IconData icon;
+    Color cardColor;
+
     switch (status) {
       case EstadoPedido.disponible:
-        return IconButton(
-          onPressed: () {
-            final mesaViewModel =
-                Provider.of<MesaViewModel>(context, listen: false);
-            mesaViewModel.CambiarEstadoPedidoMesa(EstadoPedido.preparacion);
-          },
-          style: ButtonStyle(
-            backgroundColor: MaterialStateProperty.all(
-                const Color.fromARGB(255, 220, 252, 232)),
-          ),
-          icon: const Icon(Icons.table_restaurant_rounded, color: Colors.green),
-        );
+        backgroundColor = const Color.fromARGB(255, 220, 252, 232);
+        iconColor = Colors.green;
+        icon = Icons.table_restaurant_rounded;
+        cardColor = const Color.fromARGB(255, 220, 252, 232);
+        break;
       case EstadoPedido.surtida:
-        return IconButton(
-          onPressed: () {},
-          icon: const Icon(Icons.coffee_rounded,
-              color: Color.fromARGB(255, 220, 234, 254)),
-        );
+        backgroundColor = const Color.fromARGB(255, 220, 234, 254);
+        iconColor = Colors.blue;
+        icon = Icons.coffee_rounded;
+        cardColor = const Color.fromARGB(255, 220, 234, 254);
+        break;
       case EstadoPedido.preparacion:
-        return IconButton(
-          onPressed: () {},
-          icon: const Icon(Icons.kitchen_rounded,
-              color: Color.fromARGB(255, 243, 232, 255)),
-        );
+        backgroundColor = const Color.fromARGB(255, 243, 232, 255);
+        iconColor = Colors.deepPurple;
+        icon = Icons.kitchen_rounded;
+        cardColor = const Color.fromARGB(255, 243, 232, 255);
+        break;
       case EstadoPedido.cobrado:
-        return IconButton(
-          onPressed: () {},
-          icon: const Icon(Icons.money_rounded,
-              color: Color.fromARGB(255, 255, 238, 213)),
-        );
+        backgroundColor = const Color.fromARGB(255, 255, 238, 213);
+        iconColor = Colors.amber;
+        icon = Icons.attach_money_rounded;
+        cardColor = const Color.fromARGB(255, 255, 238, 213);
+        break;
       default:
-        return IconButton(
-          onPressed: () {},
-          icon: const Icon(Icons.check_circle, color: Colors.grey),
-        );
+        backgroundColor = Colors.grey.shade200;
+        iconColor = Colors.grey;
+        icon = Icons.settings;
+        cardColor = Colors.white;
+        break;
     }
+
+    return Card(
+      color: cardColor,
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: [
+          IconButton(
+            onPressed: () {
+              if (status == EstadoPedido.disponible) {
+                final mesaViewModel =
+                    Provider.of<MesaViewModel>(context, listen: false);
+                mesaViewModel.CambiarEstadoPedidoMesa(EstadoPedido.preparacion);
+              }
+            },
+            iconSize: 48,
+            style: ButtonStyle(
+              backgroundColor: MaterialStateProperty.all(backgroundColor),
+            ),
+            icon: Icon(icon, color: iconColor),
+          ),
+          const SizedBox(height: 8),
+          Text(
+            mesa.nombre ?? 'Mesa sin nombre',
+            style: TextStyle(
+              color: status == EstadoPedido.disponible
+                  ? Colors.black
+                  : Colors.black87,
+              fontSize: 20,
+              fontWeight: FontWeight.w500,
+            ),
+          )
+        ],
+      ),
+    );
   }
 }

+ 62 - 19
lib/utils/widgets/custom_appbar.dart

@@ -1,4 +1,8 @@
 import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+import 'package:turquessa_mesas_hoster/core/models/sucursal_model.dart';
+
+import '../../mvvm/viewmodels/sucursal_view_model.dart';
 
 class CustomAppbar extends StatefulWidget {
   const CustomAppbar({super.key});
@@ -9,33 +13,72 @@ class CustomAppbar extends StatefulWidget {
 
 class _CustomAppbarState extends State<CustomAppbar> {
   @override
+  void initState() {
+    super.initState();
+    final _sucursalViewModel =
+        Provider.of<SucursalViewModel>(context, listen: false);
+    _sucursalViewModel.fetchLocalSucursales();
+
+    WidgetsBinding.instance.addPostFrameCallback((_) async {
+      Provider.of<SucursalViewModel>(context, listen: false)
+          .sincronizarSucursalesDesdeApi();
+
+      await _sucursalViewModel.sincronizarSucursales();
+      await _sucursalViewModel.fetchLocalSucursales();
+    });
+  }
+
+  String? _selectedValue;
+
+  @override
   Widget build(BuildContext context) {
+    final sucursalViewModel = Provider.of<SucursalViewModel>(context);
+    final sucursales = sucursalViewModel.sucursales;
     return Row(
       children: [
         Image.asset(
-          'assets/logo.png',
-          height: 40,
+          'assets/Turquessa.png',
+          height: 100,
         ),
         const SizedBox(width: 10),
-        const SizedBox(width: 10),
-        DropdownButton(
-            hint: const Text(
-              "Mesas Interiores",
-              style:
-                  TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
-            ),
-            icon: const Icon(Icons.arrow_drop_down),
-            items: const [
-              DropdownMenuItem(
-                value: "ES",
-                child: Text("Palomeras"),
+        SizedBox(
+          width: 240,
+          child: Expanded(
+            child: DropdownButtonFormField<String>(
+              value: _selectedValue,
+              hint: Text(
+                _selectedValue?.isEmpty ?? true
+                    ? 'Selecciona una sucursal'
+                    : '',
+                style:
+                    TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
               ),
-              DropdownMenuItem(
-                value: "EN",
-                child: Text("Sombrillera"),
+              icon: const Icon(Icons.arrow_drop_down),
+              decoration: InputDecoration(
+                contentPadding:
+                    const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
+                border: OutlineInputBorder(
+                  borderRadius: BorderRadius.circular(15),
+                  borderSide: BorderSide.none,
+                ),
+                filled: true,
+                fillColor: Colors.white,
               ),
-            ],
-            onChanged: (e) => {})
+              items: sucursales
+                  .map((e) => DropdownMenuItem(
+                        value: e.nombre,
+                        child: Text(e.nombre ?? ""),
+                      ))
+                  .toList(),
+              onChanged: (value) {
+                setState(() {
+                  _selectedValue = value;
+                  // setSelectedSucursal(value as Sucursal);
+                });
+              },
+            ),
+          ),
+        )
       ],
     );
   }

+ 57 - 0
lib/utils/widgets/navigation_rail.dart

@@ -0,0 +1,57 @@
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+import '../../mvvm/viewmodels/viewmodels.dart';
+
+class CustomNavigationRail extends StatefulWidget {
+  const CustomNavigationRail({super.key, required selectedIndex});
+
+  @override
+  State<CustomNavigationRail> createState() => Custom_NavigationRailState();
+}
+
+class Custom_NavigationRailState extends State<CustomNavigationRail> {
+  int selectedIndex = 0;
+  @override
+  Widget build(BuildContext context) {
+    return NavigationRail(
+      backgroundColor: Color.fromARGB(255, 25, 30, 41),
+      selectedIndex: selectedIndex,
+      onDestinationSelected: (int index) {
+        setState(() {
+          selectedIndex = index;
+        });
+        if (index == 3) {
+          Provider.of<LoginViewModel>(context, listen: false)
+              .showExitConfirmationDialog(context);
+        }
+      },
+      labelType: NavigationRailLabelType.all,
+      destinations: const [
+        NavigationRailDestination(
+          icon: Icon(Icons.home, color: Colors.white),
+          selectedIcon: Icon(Icons.home_filled),
+          label: Text('Inicio'),
+        ),
+        NavigationRailDestination(
+          icon: Icon(Icons.search),
+          selectedIcon: Icon(
+            Icons.search_rounded,
+            color: Colors.white,
+          ),
+          label: Text('Buscar'),
+        ),
+        NavigationRailDestination(
+          icon: Icon(Icons.settings),
+          selectedIcon: Icon(Icons.settings_rounded, color: Colors.white),
+          label: Text('Ajustes'),
+        ),
+        NavigationRailDestination(
+          icon: Icon(Icons.logout),
+          selectedIcon: Icon(Icons.logout, color: Colors.white),
+          label: Text('Cerrar Sesión'),
+        ),
+      ],
+    );
+  }
+}

+ 1 - 0
pubspec.yaml

@@ -76,6 +76,7 @@ flutter:
 
   # To add assets to your application, add an assets section, like this:
   assets:
+    - assets/Turquessa.png
     - assets/logo.png
     - assets/igLogo.png
     - assets/logo-BN.png