Bladeren bron

Categoría de topings y topings en pedido form

OscarGil03 1 jaar geleden
bovenliggende
commit
750f798fb5

+ 8 - 0
lib/models/producto_model.dart

@@ -15,6 +15,7 @@ class Producto extends Basico {
   String? codigo;
   String? descuento;
   String? venceDescuento;
+  int? toping;
 
   Producto({
     super.id,
@@ -29,6 +30,7 @@ class Producto extends Basico {
     this.codigo,
     this.descuento,
     this.venceDescuento,
+    this.toping,
   });
 
   @override
@@ -46,6 +48,7 @@ class Producto extends Basico {
       'codigo': codigo,
       'descuento': descuento,
       'venceDescuento': venceDescuento,
+      'toping': toping,
     }..addAll(super.toJson());
   }
 
@@ -65,5 +68,10 @@ class Producto extends Basico {
     codigo = Basico.parseString(json['codigo']);
     descuento = Basico.parseString(json['descuento']);
     venceDescuento = Basico.parseString(json['venceDescuento']);
+    if (json['toping'] is bool) {
+      toping = json['toping'] ? 1 : 0;
+    } else {
+      toping = Basico.parseInt(json['toping']);
+    }
   }
 }

+ 1 - 1
lib/viewmodels/toping_categoria_view_model.dart

@@ -31,7 +31,7 @@ class TopingCategoriaViewModel extends ChangeNotifier {
   Future<List<TopingCategoria>> fetchRegistros(
       {String? q, bool segmentar = false}) async {
     Map<String, String> parametros = {
-      "ordenar": "id-desc",
+      "ordenar": "id-asc",
       "pagina": "$pagina",
       "limite": "$limite",
       "expand": "media,mediaTopingCategoria"

+ 1 - 1
lib/viewmodels/toping_view_model.dart

@@ -21,7 +21,7 @@ class TopingViewModel extends ChangeNotifier {
 
   int pagina = 1;
   int totalPaginas = 1;
-  int limite = 10;
+  int limite = 25;
 
   setBusqueda(String value) {
     _busqueda = value;

+ 439 - 5
lib/views/pedido/pedido_form.dart

@@ -1,7 +1,10 @@
+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 'package:collection/collection.dart';
 
 class PedidoForm extends StatefulWidget {
   @override
@@ -11,8 +14,10 @@ class PedidoForm extends StatefulWidget {
 class ItemCarrito {
   Producto producto;
   int cantidad;
+  Map<String, dynamic>? customizaciones;
 
-  ItemCarrito({required this.producto, this.cantidad = 1});
+  ItemCarrito(
+      {required this.producto, this.cantidad = 1, this.customizaciones});
 }
 
 class _PedidoFormState extends State<PedidoForm> {
@@ -24,6 +29,25 @@ class _PedidoFormState extends State<PedidoForm> {
   List<CategoriaProducto> categorias = [];
   List<Producto> productos = [];
   List<ItemCarrito> carrito = [];
+  TopingViewModel _topingViewModel = TopingViewModel();
+  TopingCategoriaViewModel _categoriaTopingViewModel =
+      TopingCategoriaViewModel();
+  List<Toping> _topingsDisponibles = []; // Lista de topings disponibles
+  Producto? _productoActual; // Producto actual seleccionado para customización
+  bool isCustomizingProduct = false;
+  Map<String, dynamic>? currentProductForCustomization;
+  String? selectedBase;
+  List<String> selectedSauce = [];
+  List<String> selectedDressing = [];
+  List<String> selectedToppings = [];
+  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 = {};
 
   double calcularTotalPedido() {
     double total = 0;
@@ -37,6 +61,80 @@ class _PedidoFormState extends State<PedidoForm> {
   void initState() {
     super.initState();
     cargarCategoriasIniciales();
+    _cargarCategoriasDeTopings();
+    _cargarTopingsDisponibles();
+  }
+
+  void _cargarCategoriasDeTopings() async {
+    var categoriasObtenidas = await _categoriaTopingViewModel.fetchRegistros();
+    setState(() {
+      categoriasDeTopings = categoriasObtenidas;
+    });
+  }
+
+// Función para cargar topings disponibles
+  void _cargarTopingsDisponibles() async {
+    _topingsDisponibles = await _topingViewModel.fetchRegistros();
+
+    var categorias = await _categoriaTopingViewModel.fetchRegistros();
+    var categoriaBase = categorias.firstWhereOrNull(
+      (cat) => cat.nombre == 'Base',
+    );
+
+    if (categoriaBase != null) {
+      var topingsBase = _topingsDisponibles.where((toping) {
+        return toping.idCategoria == categoriaBase.id;
+      }).toList();
+
+      setState(() {
+        baseOptions = {
+          for (var toping in topingsBase) toping.nombre!: false,
+        };
+      });
+    }
+  }
+
+  void _onTopingCategoriaSelected(TopingCategoria categoriaSeleccionada) {
+    setState(() {
+      _currentTopingCategoriaSeleccionada = categoriaSeleccionada;
+      // Filtrar y actualizar la lista de toppings para mostrar basada en la categoría seleccionada
+      _topingsFiltrados = _topingsDisponibles
+          .where((t) => t.idCategoria == categoriaSeleccionada.id)
+          .toList();
+    });
+  }
+
+  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);
+      } 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!);
+        }
+        // De lo contrario, no se permite la selección y puedes mostrar un mensaje o algo similar
+      }
+
+      // Actualiza el mapa con las nuevas selecciones
+      selectedToppingsByCategory[_currentTopingCategoriaSeleccionada!.id] =
+          currentSelections;
+    });
+    print(
+        "Toppings seleccionados después de setState: ${selectedToppingsByCategory[_currentTopingCategoriaSeleccionada!.id]}");
   }
 
   @override
@@ -98,6 +196,53 @@ class _PedidoFormState extends State<PedidoForm> {
     });
   }
 
+  void addToCart(Producto producto, {Map<String, dynamic>? customizations}) {
+    var existingIndex =
+        carrito.indexWhere((item) => item.producto.id == producto.id);
+    if (existingIndex != -1) {
+      carrito[existingIndex].cantidad++;
+    } else {
+      carrito.add(
+          ItemCarrito(producto: producto, customizaciones: customizations));
+    }
+    setState(() {});
+  }
+
+  void customizeProduct(Producto producto) {
+    // Asumimos que este producto requiere customización
+    setState(() {
+      _productoActual = producto;
+      isCustomizingProduct = true;
+
+      if (categoriasDeTopings.isNotEmpty) {
+        // Selecciona la primera categoría de toppings automáticamente
+        _currentTopingCategoriaSeleccionada = categoriasDeTopings.first;
+        // Filtra los toppings basado en la primera categoría
+        _topingsFiltrados = _topingsDisponibles
+            .where((toping) =>
+                toping.idCategoria == _currentTopingCategoriaSeleccionada!.id)
+            .toList();
+      }
+    });
+  }
+
+  void finalizeCustomization() {
+    if (_productoActual != null) {
+      Map<String, dynamic> customizations = {
+        'base': selectedBase,
+        'sauce': selectedSauce,
+        'dressing': selectedDressing,
+        'toppings': selectedToppings,
+      };
+      addToCart(_productoActual!, customizations: customizations);
+      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
+      });
+    }
+  }
+
   @override
   Widget build(BuildContext context) {
     return Scaffold(
@@ -113,7 +258,9 @@ class _PedidoFormState extends State<PedidoForm> {
           SizedBox(width: 35),
           Flexible(
             flex: 7,
-            child: _buildProductsSection(),
+            child: isCustomizingProduct
+                ? buildCustomizationOptions()
+                : _buildProductsSection(),
           ),
         ],
       ),
@@ -242,10 +389,14 @@ class _PedidoFormState extends State<PedidoForm> {
               return Card(
                 child: InkWell(
                   onTap: () {
-                    setState(() {
-                      // Aquí manejas la adición de productos al carrito
+                    // Modifica esta parte para verificar si el producto necesita customización
+                    if (producto.toping == 1) {
+                      // Asume que `toping` es 1 si necesita customización
+                      customizeProduct(producto);
+                    } else {
+                      // Si no requiere customización, agrega directamente al carrito
                       agregarAlCarrito(producto);
-                    });
+                    }
                   },
                   child: Column(
                     mainAxisAlignment: MainAxisAlignment.center,
@@ -323,4 +474,287 @@ class _PedidoFormState extends State<PedidoForm> {
       ],
     );
   }
+
+  Widget buildCustomizationOptions() {
+    return Container(
+      margin: const EdgeInsets.all(8.0),
+      child: Card(
+        child: Column(
+          children: [
+            Expanded(
+              child: Row(
+                children: [
+                  Expanded(
+                    flex: 4,
+                    child: ListView.builder(
+                      itemCount: categoriasDeTopings.length,
+                      itemBuilder: (context, index) {
+                        final categoria = categoriasDeTopings[index];
+                        return ListTile(
+                          title: Text(
+                            categoria.nombre ?? 'N/A',
+                            style: TextStyle(
+                                fontSize: 16, fontWeight: FontWeight.bold),
+                          ),
+                          selected:
+                              _currentTopingCategoriaSeleccionada == categoria,
+                          trailing: Image.network(
+                            categoria.mediaTopingCategoria[0].media!.ruta!,
+                            width: 80,
+                          ),
+                          onTap: () {
+                            setState(() {
+                              _currentTopingCategoriaSeleccionada = categoria;
+                              _topingsFiltrados = _topingsDisponibles
+                                  .where((t) => t.idCategoria == categoria.id)
+                                  .toList();
+                            });
+                          },
+                        );
+                      },
+                    ),
+                  ),
+                  const VerticalDivider(width: 0),
+                  Expanded(
+                    flex: 6,
+                    child: GridView.builder(
+                      gridDelegate:
+                          const SliverGridDelegateWithFixedCrossAxisCount(
+                        crossAxisCount: 3,
+                      ),
+                      itemCount: _topingsFiltrados.length,
+                      itemBuilder: (context, index) {
+                        final toping = _topingsFiltrados[index];
+                        return Card(
+                          child: InkWell(
+                            onTap: () => _onTopingSelected(toping),
+                            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!,
+                                  width: 80,
+                                ),
+                                Text(toping.nombre!),
+                              ],
+                            ),
+                          ),
+                        );
+                      },
+                    ),
+                  ),
+                ],
+              ),
+            ),
+            Container(
+              padding: EdgeInsets.symmetric(vertical: 8.0),
+              alignment: Alignment.centerRight,
+              // El botón para finalizar la personalización
+              child: _buildConfirmButton(),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  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(
+          primary: AppTheme.primary, // Color del botón
+          onPrimary: Colors.black,
+          textStyle: const TextStyle(fontSize: 22),
+          padding: const EdgeInsets.fromLTRB(80, 20, 80, 20)),
+      onPressed: () {
+        finalizeCustomization(); // Este método creará el pedido personalizado
+      },
+      child: Text('Confirmar Pedido'),
+    );
+  }
 }

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

@@ -120,7 +120,7 @@ class Formulario extends State<TopingScreen> {
 
     return Scaffold(
       appBar: AppBar(
-        title: const Text('Toping Categorias'),
+        title: const Text('Toping'),
       ),
       floatingActionButton: FloatingActionButton(
         onPressed: () {