Procházet zdrojové kódy

Forms de topings y categoria topings actualizado

OscarGil03 před 1 rokem
rodič
revize
bf87d5e4e6

+ 2 - 6
lib/models/toping_model.dart

@@ -8,7 +8,7 @@ class Toping extends Basico {
   String? nombre;
   String? descripcion;
   String? costo;
-  int? activo;
+  bool? activo = false;
   String? imagenPrincipal;
   List<MediaToping> mediaToping = [];
 
@@ -44,11 +44,7 @@ class Toping extends Basico {
     nombre = Basico.parseString(json['nombre']);
     descripcion = Basico.parseString(json['descripcion']);
     costo = Basico.parseString(json['costo']);
-    if (json['activo'] is bool) {
-      activo = json['activo'] ? 1 : 0;
-    } else {
-      activo = Basico.parseInt(json['activo']);
-    }
+    activo = Basico.parseBolean(json['activo']);
     imagenPrincipal = Basico.parseString(json['imagenPrincipal']);
     if (json['mediaToping'] != null) {
       var mediaTCList = json['mediaToping'] as List;

+ 1 - 1
lib/services/general_service.dart

@@ -16,7 +16,7 @@ class GeneralService extends BaseService {
       String? etiquetaID}) async {
     //ejemplo: tipo = imagen, modulo = Visita
     final token = await SessionStorage().getToken();
-    String completo = "$base_url/admin/subir-archivo/guardar";
+    String completo = "$base_url/admin/media/guardar";
     String nombre = "generico";
     http.MultipartRequest request =
         http.MultipartRequest('POST', Uri.parse(completo));

+ 19 - 0
lib/viewmodels/toping_categoria_view_model.dart

@@ -1,3 +1,5 @@
+import 'dart:convert';
+
 import 'package:camera/camera.dart';
 import 'package:flutter/material.dart';
 import 'package:yoshi_papas_app/models/toping_categoria_model.dart';
@@ -28,6 +30,16 @@ class TopingCategoriaViewModel extends ChangeNotifier {
     notifyListeners();
   }
 
+  TopingCategoria? getById(int id) {
+    try {
+      return _registros
+          .firstWhere((topingCategoria) => topingCategoria.id == id);
+    } catch (e) {
+      // Si no se encuentra ninguna categoría con ese ID, regresa null.
+      return null;
+    }
+  }
+
   Future<List<TopingCategoria>> fetchRegistros(
       {String? q, bool segmentar = false}) async {
     Map<String, String> parametros = {
@@ -108,4 +120,11 @@ class TopingCategoriaViewModel extends ChangeNotifier {
     if (r.statusCode == 200) {}
     notifyListeners();
   }
+
+  Future<void> eliminar(TopingCategoria m) async {
+    var r = await BaseService()
+        .delete("admin/toping-categoria", body: {"id": m.id.toString()});
+    Map<String, dynamic> resJson = jsonDecode(r.body);
+    if (r.statusCode == 200) {}
+  }
 }

+ 28 - 4
lib/viewmodels/toping_view_model.dart

@@ -1,3 +1,5 @@
+import 'dart:convert';
+
 import 'package:camera/camera.dart';
 import 'package:flutter/material.dart';
 import 'package:yoshi_papas_app/models/toping_categoria_model.dart';
@@ -28,6 +30,15 @@ class TopingViewModel extends ChangeNotifier {
     notifyListeners();
   }
 
+  Toping? getById(int id) {
+    try {
+      return _registros.firstWhere((toping) => toping.id == id);
+    } catch (e) {
+      // Si no se encuentra ninguna categoría con ese ID, regresa null.
+      return null;
+    }
+  }
+
   Future<List<Toping>> fetchRegistros(
       {String? q, bool segmentar = false}) async {
     Map<String, String> parametros = {
@@ -85,21 +96,27 @@ class TopingViewModel extends ChangeNotifier {
     required String clave,
     required String nombre,
     required String descripcion,
+    required String costo,
+    required int categoria,
+    required bool activo,
     List<XFile>? fotos,
   }) async {
     modelo.clave = clave;
     modelo.nombre = nombre;
     modelo.descripcion = descripcion;
-    var r = ApiResponse(
-        await BaseService().post("admin/toping", body: modelo.toJson()));
+    modelo.idCategoria = categoria;
+    modelo.costo = costo;
+    modelo.activo = activo;
+    var r = ApiResponse(await BaseService()
+        .post("admin/toping/guardar", body: modelo.toJson()));
     int id = int.parse(r.detalle!["id"].toString());
     if (id > 0 && fotos != null && fotos.isNotEmpty) {
       for (var x in fotos) {
         await GeneralService().enviarMedia(
             id: id,
             media: x,
-            modulo: "Autotanque",
-            etiquetaID: "idInspeccion",
+            modulo: "Toping",
+            etiquetaID: "idToping",
             tipo: "imagen");
       }
     }
@@ -108,4 +125,11 @@ class TopingViewModel extends ChangeNotifier {
     if (r.statusCode == 200) {}
     notifyListeners();
   }
+
+  Future<void> eliminar(Toping m) async {
+    var r = await BaseService()
+        .delete("admin/toping", body: {"id": m.id.toString()});
+    Map<String, dynamic> resJson = jsonDecode(r.body);
+    if (r.statusCode == 200) {}
+  }
 }

+ 232 - 260
lib/views/pedido/pedido_form.dart

@@ -1,9 +1,11 @@
+import 'dart:async';
+
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:yoshi_papas_app/widgets/widgets_components.dart';
-import '../../themes/themes.dart'; // Asegúrate de tener este archivo con los temas correctos
-import '../../models/models.dart'; // Tus modelos de datos
-import '../../viewmodels/viewmodels.dart'; // Tus ViewModels
+import '../../themes/themes.dart';
+import '../../models/models.dart';
+import '../../viewmodels/viewmodels.dart';
 import 'package:collection/collection.dart';
 
 class PedidoForm extends StatefulWidget {
@@ -43,11 +45,12 @@ class _PedidoFormState extends State<PedidoForm> {
   Map<String, bool> baseOptions = {};
   Map<String, bool> sauceOptions = {};
   Map<String, bool> dressingOptions = {};
-  Map<String, bool> toppingOptions = {};
   List<TopingCategoria> categoriasDeTopings = []; // Añade esta línea
   TopingCategoria? _currentTopingCategoriaSeleccionada; // Añade esta línea
   List<Toping> _topingsFiltrados = []; // Añade esta línea
-  Map<int, List<String>> selectedToppingsByCategory = {};
+  Map<int, List<int>> selectedToppingsByCategory = {};
+  Map<String, bool> toppingOptions = {};
+  List<int> _selectedToppings = [];
 
   double calcularTotalPedido() {
     double total = 0;
@@ -104,37 +107,51 @@ class _PedidoFormState extends State<PedidoForm> {
     });
   }
 
-  void _onTopingSelected(Toping toping) {
-    print("Topping seleccionado antes de setState: ${toping.nombre}");
-    setState(() {
-      // Obtiene la lista actual de toppings seleccionados para la categoría
-      final currentSelections =
-          selectedToppingsByCategory[_currentTopingCategoriaSeleccionada!.id] ??
-              [];
-
-      if (currentSelections.contains(toping.nombre)) {
-        // Si el topping ya está seleccionado, lo deselecciona
-        currentSelections.remove(toping.nombre);
+  void _onToppingSelected(Toping topping) {
+    // Verifica si el topping ya está seleccionado
+    bool isSelected = _selectedToppings.contains(topping.id);
+
+    final List<int> currentSelectedToppings =
+        selectedToppingsByCategory[_currentTopingCategoriaSeleccionada!.id] ??
+            [];
+
+    if (isSelected) {
+      // Si ya está seleccionado, lo deseleccionamos
+      setState(() {
+        _selectedToppings.remove(topping.id);
+        currentSelectedToppings.remove(topping.id);
+        selectedToppingsByCategory[_currentTopingCategoriaSeleccionada!.id] =
+            List.from(currentSelectedToppings);
+      });
+    } else {
+      // Si no está seleccionado, verifica las restricciones antes de seleccionar
+      if (_currentTopingCategoriaSeleccionada!.id == 1 &&
+          currentSelectedToppings.isNotEmpty) {
+        // Si es la categoría 'Base', solo permite una selección
+        setState(() {
+          currentSelectedToppings.clear();
+          currentSelectedToppings.add(topping.id);
+          _selectedToppings.clear();
+          _selectedToppings.add(topping.id);
+        });
       } else {
-        // Comprobar las reglas de selección
-        if (_currentTopingCategoriaSeleccionada!.nombre == 'Base' ||
-            currentSelections.length < 2) {
-          // Si es la categoría 'Base' o hay menos de 2 selecciones, selecciona el topping
-          if (_currentTopingCategoriaSeleccionada!.nombre == 'Base') {
-            // Si es 'Base', solo permite una selección
-            currentSelections.clear();
-          }
-          currentSelections.add(toping.nombre!);
+        // Si la lista ya tiene dos toppings, quita el primero y agrega el nuevo
+        if (currentSelectedToppings.length >= 2) {
+          setState(() {
+            _selectedToppings.remove(currentSelectedToppings.first);
+            currentSelectedToppings
+                .removeAt(0); // Elimina el primer topping seleccionado
+          });
         }
-        // De lo contrario, no se permite la selección y puedes mostrar un mensaje o algo similar
+        // Ahora agrega el nuevo topping seleccionado
+        setState(() {
+          currentSelectedToppings.add(topping.id);
+          _selectedToppings.add(topping.id);
+        });
       }
-
-      // Actualiza el mapa con las nuevas selecciones
       selectedToppingsByCategory[_currentTopingCategoriaSeleccionada!.id] =
-          currentSelections;
-    });
-    print(
-        "Toppings seleccionados después de setState: ${selectedToppingsByCategory[_currentTopingCategoriaSeleccionada!.id]}");
+          List.from(currentSelectedToppings);
+    }
   }
 
   @override
@@ -197,13 +214,16 @@ class _PedidoFormState extends State<PedidoForm> {
   }
 
   void addToCart(Producto producto, {Map<String, dynamic>? customizations}) {
-    var existingIndex =
-        carrito.indexWhere((item) => item.producto.id == producto.id);
+    // Revisa si hay un producto en el carrito con las mismas customizaciones
+    var existingIndex = carrito.indexWhere((item) =>
+        item.producto.id == producto.id &&
+        mapEquals(item.customizaciones, customizations));
+
     if (existingIndex != -1) {
       carrito[existingIndex].cantidad++;
     } else {
-      carrito.add(
-          ItemCarrito(producto: producto, customizaciones: customizations));
+      carrito.add(ItemCarrito(
+          producto: producto, cantidad: 1, customizaciones: customizations));
     }
     setState(() {});
   }
@@ -228,17 +248,43 @@ class _PedidoFormState extends State<PedidoForm> {
 
   void finalizeCustomization() {
     if (_productoActual != null) {
-      Map<String, dynamic> customizations = {
-        'base': selectedBase,
-        'sauce': selectedSauce,
-        'dressing': selectedDressing,
-        'toppings': selectedToppings,
+      // Prepara el diccionario de customizaciones con listas vacías para cada categoría
+      Map<String, List<String>> customizations = {
+        'BASE': [],
+        'SALSA': [],
+        'ADEREZO': [],
+        'TOPPINGS': [],
       };
-      addToCart(_productoActual!, customizations: customizations);
+
+      // Itera sobre los toppings seleccionados, clasificándolos por categoría
+      selectedToppingsByCategory.forEach((categoryId, toppingIds) {
+        var categoryName = _categoriaTopingViewModel.getById(categoryId)?.clave;
+        toppingIds.forEach((toppingId) {
+          var topping = _topingViewModel.getById(toppingId);
+          if (topping != null &&
+              categoryName != null &&
+              customizations.containsKey(categoryName.toUpperCase())) {
+            customizations[categoryName.toUpperCase()]!.add(topping.nombre!);
+          }
+        });
+      });
+
+      // Asegúrate de que la customización final se almacene como deseas, por ejemplo, como un solo mapa si es necesario
+      Map<String, dynamic> finalCustomizations = {};
+      customizations.forEach((key, value) {
+        finalCustomizations[key] = value.join(', ');
+      });
+
+      addToCart(_productoActual!, customizations: finalCustomizations);
+
+      // Limpia el estado para la siguiente personalización
       setState(() {
-        isCustomizingProduct = false; // Salir del modo de customización
-        _productoActual = null; // Resetear el producto actual
-        // También resetear las opciones de customización aquí si es necesario
+        isCustomizingProduct = false;
+        _productoActual = null;
+        _currentTopingCategoriaSeleccionada = null; // Agrega esta línea
+        _topingsFiltrados.clear(); // Agrega esta línea
+        selectedToppingsByCategory.clear(); // Agrega esta línea
+        _selectedToppings.clear(); // Ya existe, asegúrate de que se llama
       });
     }
   }
@@ -285,6 +331,23 @@ class _PedidoFormState extends State<PedidoForm> {
     );
   }
 
+  List<Widget> _buildToppingList(Map<String, dynamic>? customizations) {
+    List<Widget> list = [];
+    customizations?.forEach((category, toppingsAsString) {
+      if (toppingsAsString is String && toppingsAsString.isNotEmpty) {
+        // Divide la string por comas para obtener los nombres individuales de los toppings
+        List<String> toppingNames = toppingsAsString.split(', ');
+        for (var toppingName in toppingNames) {
+          list.add(ListTile(
+            title: Text(toppingName),
+            subtitle: Text(category), // Muestra la categoría como subtítulo
+          ));
+        }
+      }
+    });
+    return list;
+  }
+
   Widget _buildCartSection() {
     return Card(
       margin: const EdgeInsets.all(8.0),
@@ -309,34 +372,71 @@ class _PedidoFormState extends State<PedidoForm> {
               itemCount: carrito.length,
               itemBuilder: (context, index) {
                 final item = carrito[index];
-                return ListTile(
-                  title: Text(item.producto.nombre!),
-                  subtitle: Text('\$${item.producto.precio}'),
-                  trailing: Row(
-                    mainAxisSize: MainAxisSize.min,
-                    children: [
-                      IconButton(
-                        icon: const Icon(Icons.delete, color: Colors.red),
-                        onPressed: () => eliminarProductoDelCarrito(index),
+                // Crear una lista de Widgets para las customizaciones.
+                List<Widget> customizationWidgets = [];
+                item.customizaciones?.forEach((category, toppingsAsString) {
+                  if (toppingsAsString is String &&
+                      toppingsAsString.isNotEmpty) {
+                    // Si hay más de un topping en la cadena, sepáralos y muéstralos en la misma línea
+                    customizationWidgets.add(
+                      Align(
+                        alignment: Alignment.centerLeft,
+                        child: Padding(
+                          padding: const EdgeInsets.only(left: 16.0, top: 4.0),
+                          child: Text(
+                              '- $category: $toppingsAsString', // Cambio clave aquí
+                              style: const TextStyle(
+                                fontWeight: FontWeight.w500,
+                                fontSize: 14.0,
+                              )),
+                        ),
                       ),
-                      IconButton(
-                        icon: const Icon(Icons.remove),
-                        onPressed: () => quitarDelCarrito(item.producto),
+                    );
+                  }
+                });
+
+                return Column(
+                  children: [
+                    ListTile(
+                      title: Text(
+                        item.producto.nombre!,
+                        style: const TextStyle(fontWeight: FontWeight.w600),
                       ),
-                      const SizedBox(width: 5),
-                      Text(
-                        '${item.cantidad}',
+                      subtitle: Text(
+                        '\$${item.producto.precio}',
                         style: const TextStyle(
-                            fontWeight: FontWeight.bold, fontSize: 14),
+                            fontWeight: FontWeight.bold,
+                            color: Color(0xFF008000)),
                       ),
-                      const SizedBox(width: 5),
-                      IconButton(
-                        icon: const Icon(Icons.add),
-                        onPressed: () => agregarAlCarrito(item.producto),
+                      trailing: Row(
+                        mainAxisSize: MainAxisSize.min,
+                        children: [
+                          IconButton(
+                            icon: const Icon(Icons.delete, color: Colors.red),
+                            onPressed: () => eliminarProductoDelCarrito(index),
+                          ),
+                          IconButton(
+                            icon: const Icon(Icons.remove),
+                            onPressed: () => quitarDelCarrito(item.producto),
+                          ),
+                          const SizedBox(width: 5),
+                          Text(
+                            '${item.cantidad}',
+                            style: const TextStyle(
+                                fontWeight: FontWeight.bold, fontSize: 14),
+                          ),
+                          const SizedBox(width: 5),
+                          IconButton(
+                            icon: const Icon(Icons.add),
+                            onPressed: () => agregarAlCarrito(item.producto),
+                          ),
+                        ],
                       ),
-                      // Botón para eliminar el producto del carrito
-                    ],
-                  ),
+                    ),
+                    // Coloca aquí las customizaciones directamente.
+                    ...customizationWidgets,
+                    Divider(), // Opcional: Un divisor visual entre los elementos.
+                  ],
                 );
               },
             ),
@@ -493,7 +593,7 @@ class _PedidoFormState extends State<PedidoForm> {
                         return ListTile(
                           title: Text(
                             categoria.nombre ?? 'N/A',
-                            style: TextStyle(
+                            style: const TextStyle(
                                 fontSize: 16, fontWeight: FontWeight.bold),
                           ),
                           selected:
@@ -501,6 +601,27 @@ class _PedidoFormState extends State<PedidoForm> {
                           trailing: Image.network(
                             categoria.mediaTopingCategoria[0].media!.ruta!,
                             width: 80,
+                            loadingBuilder: (BuildContext context, Widget child,
+                                ImageChunkEvent? loadingProgress) {
+                              if (loadingProgress == null) return child;
+                              return Center(
+                                child: CircularProgressIndicator(
+                                  value: loadingProgress.expectedTotalBytes !=
+                                          null
+                                      ? loadingProgress.cumulativeBytesLoaded /
+                                          loadingProgress.expectedTotalBytes!
+                                      : null,
+                                ),
+                              );
+                            },
+                            errorBuilder: (BuildContext context,
+                                Object exception, StackTrace? stackTrace) {
+                              // Aquí puedes devolver un widget como un ícono o imagen local para indicar el error
+                              return const Icon(
+                                Icons.menu_book,
+                                size: 30,
+                              );
+                            },
                           ),
                           onTap: () {
                             setState(() {
@@ -522,21 +643,57 @@ class _PedidoFormState extends State<PedidoForm> {
                           const SliverGridDelegateWithFixedCrossAxisCount(
                         crossAxisCount: 3,
                       ),
-                      itemCount: _topingsFiltrados.length,
+                      itemCount: _topingsFiltrados
+                          .length, // Usando _topingsFiltrados directamente
                       itemBuilder: (context, index) {
-                        final toping = _topingsFiltrados[index];
+                        final topping = _topingsFiltrados[index];
+                        bool isSelected =
+                            _selectedToppings.contains(topping.id);
+
                         return Card(
+                          color: isSelected
+                              ? const Color(0xFFFC8178)
+                              : Colors.white,
                           child: InkWell(
-                            onTap: () => _onTopingSelected(toping),
+                            onTap: () => _onToppingSelected(topping),
                             child: Column(
                               mainAxisAlignment: MainAxisAlignment.center,
                               children: [
-                                // Aquí iría la imagen del toping, asegúrate de manejar errores de red.
                                 Image.network(
-                                  toping.mediaToping[0].media!.ruta!,
+                                  topping.mediaToping[0].media!.ruta!,
                                   width: 80,
+                                  loadingBuilder: (BuildContext context,
+                                      Widget child,
+                                      ImageChunkEvent? loadingProgress) {
+                                    if (loadingProgress == null) return child;
+                                    return Center(
+                                      child: CircularProgressIndicator(
+                                        value: loadingProgress
+                                                    .expectedTotalBytes !=
+                                                null
+                                            ? loadingProgress
+                                                    .cumulativeBytesLoaded /
+                                                loadingProgress
+                                                    .expectedTotalBytes!
+                                            : null,
+                                      ),
+                                    );
+                                  },
+                                  errorBuilder: (BuildContext context,
+                                      Object exception,
+                                      StackTrace? stackTrace) {
+                                    // Aquí puedes devolver un widget como un ícono o imagen local para indicar el error
+                                    return const Icon(
+                                      Icons.lunch_dining,
+                                      size: 60,
+                                    );
+                                  },
                                 ),
-                                Text(toping.nombre!),
+                                Text(topping.nombre!,
+                                    style: TextStyle(
+                                        color: isSelected
+                                            ? Colors.white
+                                            : Colors.black)),
                               ],
                             ),
                           ),
@@ -559,191 +716,6 @@ class _PedidoFormState extends State<PedidoForm> {
     );
   }
 
-  Widget _buildBaseOptions() {
-    return GridView.count(
-      crossAxisCount: 3,
-      children: baseOptions.keys.map((String key) {
-        bool isSelected =
-            baseOptions[key] ?? false; // Determina si está seleccionado
-        return GestureDetector(
-          onTap: () {
-            setState(() {
-              baseOptions.keys.forEach(
-                  (k) => baseOptions[k] = false); // Desmarca todos primero
-              baseOptions[key] = true; // Marca el seleccionado
-            });
-          },
-          child: Container(
-            decoration: BoxDecoration(
-              color: isSelected ? AppTheme.primary : const Color(0xFFF4F4F4),
-              border: Border.all(color: isSelected ? Colors.red : Colors.grey),
-            ),
-            child: Center(
-              child: Text(key,
-                  style: TextStyle(
-                      color: isSelected ? Colors.white : Colors.black)),
-            ),
-          ),
-        );
-      }).toList(),
-    );
-  }
-
-  Widget _buildSauceOptions() {
-    return GridView.count(
-      crossAxisCount: 3,
-      children: sauceOptions.keys.map((String key) {
-        bool isSelected = sauceOptions[key] ?? false;
-        return GestureDetector(
-          onTap: () {
-            int selectedCount = sauceOptions.values.where((b) => b).length;
-            setState(() {
-              // Si la salsa ya está seleccionada, la deselecciona.
-              if (isSelected) {
-                sauceOptions[key] = false;
-              } else {
-                // Si se están seleccionando menos de 2 salsas, permite esta selección.
-                if (selectedCount < 2) {
-                  sauceOptions[key] = true;
-                } else {
-                  // Si ya hay 2 salsas seleccionadas, primero deselecciona la primera seleccionada.
-                  final firstSelected = sauceOptions.keys.firstWhere(
-                    (k) => sauceOptions[k]!,
-                    orElse: () => '',
-                  );
-                  if (firstSelected.isNotEmpty) {
-                    sauceOptions[firstSelected] = false;
-                    sauceOptions[key] = true;
-                  }
-                }
-              }
-            });
-          },
-          child: Container(
-            decoration: BoxDecoration(
-              color: isSelected ? AppTheme.primary : const Color(0xFFF4F4F4),
-              border: Border.all(color: Colors.grey),
-            ),
-            child: Center(
-              child: Text(key,
-                  style: TextStyle(
-                      color: isSelected ? Colors.white : Colors.black)),
-            ),
-          ),
-        );
-      }).toList(),
-    );
-  }
-
-  Widget _buildDressingOptions() {
-    return GridView.count(
-      crossAxisCount: 3,
-      children: dressingOptions.keys.map((String key) {
-        bool isSelected = dressingOptions[key] ?? false;
-        return GestureDetector(
-          onTap: () {
-            int selectedCount = dressingOptions.values.where((b) => b).length;
-            setState(() {
-              // Si la salsa ya está seleccionada, la deselecciona.
-              if (isSelected) {
-                dressingOptions[key] = false;
-              } else {
-                // Si se están seleccionando menos de 2 salsas, permite esta selección.
-                if (selectedCount < 2) {
-                  dressingOptions[key] = true;
-                } else {
-                  // Si ya hay 2 salsas seleccionadas, primero deselecciona la primera seleccionada.
-                  final firstSelected = dressingOptions.keys.firstWhere(
-                    (k) => dressingOptions[k]!,
-                    orElse: () => '',
-                  );
-                  if (firstSelected.isNotEmpty) {
-                    dressingOptions[firstSelected] = false;
-                    dressingOptions[key] = true;
-                  }
-                }
-              }
-            });
-          },
-          child: Container(
-            decoration: BoxDecoration(
-              color: isSelected ? AppTheme.primary : const Color(0xFFF4F4F4),
-              border: Border.all(color: Colors.grey),
-            ),
-            child: Center(
-              child: Text(key,
-                  style: TextStyle(
-                      color: isSelected ? Colors.white : Colors.black)),
-            ),
-          ),
-        );
-      }).toList(),
-    );
-  }
-
-  Widget _buildToppingsOptions() {
-    return GridView.builder(
-      key: ValueKey(_topingsFiltrados.length),
-      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
-        crossAxisCount: 3,
-      ),
-      itemCount: _topingsFiltrados.length,
-      itemBuilder: (context, index) {
-        final toping = _topingsFiltrados[index];
-        // Verifica si el topping está seleccionado consultando el mapa
-        final isSelected =
-            selectedToppingsByCategory[_currentTopingCategoriaSeleccionada!.id]
-                    ?.contains(toping.nombre) ??
-                false;
-
-        return Card(
-          color: isSelected
-              ? Colors.red
-              : Colors.white, // Cambia el color si está seleccionado
-          child: InkWell(
-            onTap: () => _onTopingSelected(toping),
-            child: Column(
-              mainAxisAlignment: MainAxisAlignment.center,
-              children: [
-                Image.network(
-                  toping.mediaToping[0].media!.ruta!,
-                  fit: BoxFit.cover,
-                  width: 80,
-                  height: 80,
-                ),
-                Text(
-                  toping.nombre!,
-                  style: TextStyle(
-                    color: isSelected ? Colors.white : Colors.black,
-                  ),
-                ),
-              ],
-            ),
-          ),
-        );
-      },
-    );
-  }
-
-  Widget _buildToppingOptionsForCategory(int idCategoria) {
-    var toppingsDeCategoria =
-        _topingsDisponibles.where((t) => t.idCategoria == idCategoria).toList();
-
-    return GridView.count(
-      crossAxisCount: 3,
-      children: toppingsDeCategoria.map((topping) {
-        return GestureDetector(
-          onTap: () {
-            // Lógica para seleccionar/deseleccionar topping
-          },
-          child: Container(
-              // Construye tu widget de topping aquí, podrías incluir la imagen y el nombre
-              ),
-        );
-      }).toList(),
-    );
-  }
-
   Widget _buildConfirmButton() {
     return ElevatedButton(
       style: ElevatedButton.styleFrom(

+ 114 - 28
lib/views/toping/toping_form.dart

@@ -23,26 +23,49 @@ class Formulario extends State<TopingForm> {
   final _descripcion = TextEditingController();
   final _costo = TextEditingController();
   final _categoria = TextEditingController();
+  int? _selectedCategoriaId;
+  TopingCategoria? _selectedCategoria;
   final _imagen = TextEditingController();
+  bool activo = false;
 
   @override
   void initState() {
     super.initState();
-    Future(() async {
-      Provider.of<TopingViewModel>(context, listen: false).setIsLoading(true);
-      final mvm = Provider.of<TopingViewModel>(context, listen: false);
-      Toping? modelo = mvm.selectedToping;
-      if (modelo != null && modelo.id > 0) {
+    WidgetsBinding.instance.addPostFrameCallback((_) {
+      _initializeForm();
+    });
+  }
+
+  Future<void> _initializeForm() async {
+    Provider.of<TopingViewModel>(context, listen: false).setIsLoading(true);
+    final mvm = Provider.of<TopingViewModel>(context, listen: false);
+    final tcv = Provider.of<TopingCategoriaViewModel>(context, listen: false);
+    Toping? modelo = mvm.selectedToping;
+
+    if (modelo != null && modelo.id > 0) {
+      activo = modelo.activo!;
+      _clave.text = modelo.clave.toString();
+      _nombre.text = modelo.nombre.toString();
+      _descripcion.text = modelo.descripcion.toString();
+      _costo.text = modelo.costo.toString();
+      _selectedCategoriaId = modelo.idCategoria;
+
+      // Asegurarse de que las categorías están cargadas
+      if (tcv.registros.isEmpty) {
+        await tcv.fetchRegistros();
+      }
+
+      // Buscar la categoría seleccionada
+      final TopingCategoria? categoriaSeleccionada =
+          tcv.getById(_selectedCategoriaId!);
+      if (categoriaSeleccionada != null) {
         setState(() {
-          _clave.text = modelo.clave.toString();
-          _nombre.text = modelo.nombre.toString();
-          _descripcion.text = modelo.descripcion.toString();
-          _costo.text = modelo.costo.toString();
-          _categoria.text = modelo.idCategoria.toString();
+          _selectedCategoria = categoriaSeleccionada;
+          _categoria.text = categoriaSeleccionada.nombre!;
         });
       }
-      Provider.of<TopingViewModel>(context, listen: false).setIsLoading(false);
-    });
+    }
+    Provider.of<TopingViewModel>(context, listen: false).setIsLoading(false);
   }
 
   @override
@@ -57,6 +80,7 @@ class Formulario extends State<TopingForm> {
     final mvm = Provider.of<TopingViewModel>(context);
     final modelo = mvm.selectedToping;
     final mediavm = Provider.of<MediaViewModel>(context);
+    final tcv = Provider.of<TopingCategoriaViewModel>(context, listen: false);
     if (mvm.isLoading) return const Cargando();
     final Toping = mvm.selectedToping;
 
@@ -70,7 +94,7 @@ class Formulario extends State<TopingForm> {
     int axiscount = obtenerAxiscount(s);
 
     return Scaffold(
-      appBar: encabezado(titulo: "Toping Categoria"),
+      appBar: encabezado(titulo: "Toping"),
       body: SingleChildScrollView(
         padding: const EdgeInsets.all(8),
         child: Column(
@@ -107,13 +131,43 @@ class Formulario extends State<TopingForm> {
                     Row(
                       children: [
                         Expanded(
-                          child: AppTextField(
+                            child: AppDropdownSearch(
+                          etiqueta: 'Categoría',
+                          controller: _categoria,
+                          selectedItem: _selectedCategoria,
+                          onPressedClear: () {
+                            setState(() {
+                              _selectedCategoria = null;
+                              _categoria.text = "";
+                            });
+                          },
+                          itemAsString: (u) => '${u.clave}',
+                          asyncItems: (text) async {
+                            final r = await tcv.fetchRegistros(
+                                q: text, segmentar: true);
+                            return r;
+                          },
+                          onChanged: (e) {
+                            setState(() {
+                              _selectedCategoria = e;
+                              _selectedCategoriaId = e?.id;
+                              _categoria.text = e?.nombre ?? "";
+                            });
+                          },
+                          validator: (value) {
+                            if (value == null) {
+                              return 'Seleccione';
+                            }
+                            return null;
+                          },
+                        )
+                            /*AppTextField(
                             maxLength: 100,
                             etiqueta: 'Categoria',
                             controller: _categoria,
                             hintText: 'Nombre Categoria',
-                          ),
-                        ),
+                          ),*/
+                            ),
                       ],
                     ),
                     Row(
@@ -141,6 +195,26 @@ class Formulario extends State<TopingForm> {
                         ),
                       ],
                     ),
+                    Row(
+                      children: [
+                        Expanded(
+                            flex: 0,
+                            child: Column(children: [
+                              const Text("ACTIVO",
+                                  style:
+                                      TextStyle(fontWeight: FontWeight.bold)),
+                              Switch(
+                                activeColor: Colors.green,
+                                value: activo,
+                                onChanged: (value) {
+                                  setState(() {
+                                    activo = value;
+                                  });
+                                },
+                              )
+                            ]))
+                      ],
+                    ),
                   ],
                 ),
               ),
@@ -157,13 +231,22 @@ class Formulario extends State<TopingForm> {
                 mainAxisSpacing: 1,
                 children: [
                   ..._registros,
-                  agregarMedia(accion: () async {
-                    Navigator.push(
-                      context,
-                      MaterialPageRoute(
-                          builder: (context) => const HomeScreen()),
-                    );
-                  }),
+                  agregarMedia(
+                      accion: () async {
+                        FilePickerResult? r = await FilePicker.platform
+                            .pickFiles(
+                                type: FileType.custom,
+                                allowMultiple: true,
+                                allowedExtensions: ['jpg', 'png']);
+                        if (r == null) return;
+                        if (r.files.isEmpty) return;
+                        for (PlatformFile xf in r.files) {
+                          XFile? x =
+                              await mediavm.convertirPlatformFileAXFile(xf);
+                          mediavm.agregarArchivo(x);
+                        }
+                      },
+                      titulo: "Agregar Media"),
                 ],
               ),
               const SizedBox(height: 30),
@@ -172,11 +255,14 @@ class Formulario extends State<TopingForm> {
               Provider.of<TopingViewModel>(context, listen: false)
                   .setIsLoading(true);
               await mvm.guardarModelo(
-                modelo: Toping!,
-                clave: _clave.text,
-                nombre: _nombre.text,
-                descripcion: _descripcion.text,
-              );
+                  modelo: Toping!,
+                  clave: _clave.text,
+                  nombre: _nombre.text,
+                  descripcion: _descripcion.text,
+                  categoria: _selectedCategoriaId!,
+                  costo: _costo.text,
+                  activo: activo,
+                  fotos: mediavm.archivos);
               Provider.of<TopingViewModel>(context, listen: false)
                   .setIsLoading(false);
               if (context.mounted) {

+ 5 - 1
lib/views/toping/toping_screen.dart

@@ -97,7 +97,11 @@ class Formulario extends State<TopingScreen> {
                               Expanded(
                                   child: TextButton(
                                 onPressed: () async {
-                                  Navigator.pop(context);
+                                  await mvm.eliminar(item);
+                                  await mvm.fetchRegistros();
+                                  if (context.mounted) {
+                                    Navigator.pop(context);
+                                  }
                                 },
                                 child: const Text('Continuar'),
                               ))

+ 5 - 1
lib/views/toping_categoria/toping_categoria_screen.dart

@@ -98,7 +98,11 @@ class Formulario extends State<TopingCategoriaScreen> {
                               Expanded(
                                   child: TextButton(
                                 onPressed: () async {
-                                  Navigator.pop(context);
+                                  await mvm.eliminar(item);
+                                  await mvm.fetchRegistros();
+                                  if (context.mounted) {
+                                    Navigator.pop(context);
+                                  }
                                 },
                                 child: const Text('Continuar'),
                               ))