Selaa lähdekoodia

Categoria Productos

OscarGil03 1 vuosi sitten
vanhempi
commit
db179bdc59

+ 1 - 0
lib/main.dart

@@ -23,6 +23,7 @@ void main() {
       ChangeNotifierProvider(create: (_) => LoginViewModel()),
       ChangeNotifierProvider(create: (_) => UsuariosViewModel()),
       ChangeNotifierProvider(create: (_) => ProfileViewModel()),
+      ChangeNotifierProvider(create: (_) => CategoriaProductoViewModel()),
       // Agrega aquí cualquier otro provider que necesites
     ], child: const MyApp()));
   });

+ 23 - 0
lib/models/categoria_producto_model.dart

@@ -0,0 +1,23 @@
+import 'basico_model.dart';
+
+class CategoriaProducto extends Basico {
+  String? nombre;
+
+  CategoriaProducto({
+    super.id,
+    this.nombre,
+  });
+
+  @override
+  Map<String, dynamic> toJson() {
+    return {
+      'id': id,
+      'nombre': nombre,
+    }..addAll(super.toJson());
+  }
+
+  CategoriaProducto.fromJson(Map<String, dynamic> json) {
+    super.parseJson(json);
+    nombre = Basico.parseString(json['nombre']);
+  }
+}

+ 2 - 0
lib/models/models.dart

@@ -3,3 +3,5 @@ export '../models/login_model.dart';
 export '../models/usuario_model.dart';
 export '../models/media_model.dart';
 export '../models/profile_model.dart';
+export '../models/categoria_producto_model.dart';
+export '../models/pedido_model.dart';

+ 3 - 5
lib/models/pedido_model.dart

@@ -1,8 +1,6 @@
-import 'dart:ffi';
-
 import 'basico_model.dart';
 
-class Usuario extends Basico {
+class Pedido extends Basico {
   int? folio;
   int? estatus;
   String? comentarios;
@@ -16,7 +14,7 @@ class Usuario extends Basico {
   int? idModificador;
   int? idCancelado;
 
-  Usuario({
+  Pedido({
     super.id,
     this.folio,
     this.estatus,
@@ -51,7 +49,7 @@ class Usuario extends Basico {
     }..addAll(super.toJson());
   }
 
-  Usuario.fromJson(Map<String, dynamic> json) {
+  Pedido.fromJson(Map<String, dynamic> json) {
     super.parseJson(json);
     folio = Basico.parseInt(json['folio']);
     estatus = Basico.parseInt(json['estatus']);

+ 71 - 0
lib/services/categoria_producto_service.dart

@@ -0,0 +1,71 @@
+import 'package:yoshi_papas_app/data/api_response.dart';
+import 'package:yoshi_papas_app/models/models.dart';
+
+import '../services/base_service.dart';
+
+class CategoriaProductoService extends BaseService {
+  String endPoint = '/admin/categoria';
+  //Consulta de la lista de Sala
+  Map<String, String> defaultQueryParameters = {
+    "limite": "10",
+    "ordenar": "id-desc",
+    "pagina": "1"
+  };
+  Future<List<CategoriaProducto>> fetchList() async {
+    final response =
+        await get(endPoint, queryParameters: defaultQueryParameters);
+
+    final respuesta = ApiResponse(response);
+
+    List<CategoriaProducto> categoriaProductos = [];
+
+    if (respuesta.isOk && respuesta.resultados!.isNotEmpty) {
+      for (var x in respuesta.resultados!) {
+        final categoriaProducto = CategoriaProducto.fromJson(x);
+
+        categoriaProductos.add(categoriaProducto);
+      }
+    }
+
+    return categoriaProductos;
+  }
+
+  Future<ApiResponse> postCategoriaProducto({
+    required String nombre,
+  }) async {
+    var response = await post(
+      '$endPoint/guardar',
+      body: {
+        'nombre': nombre,
+      },
+      withAuth: true,
+    );
+    return ApiResponse(response);
+  }
+
+  Future<ApiResponse> editCategoriaProducto({
+    required int id,
+    required String nombre,
+  }) async {
+    var response = await post(
+      endPoint,
+      body: {
+        'id': id,
+        'nombre': nombre,
+      },
+      withAuth: true,
+    );
+    return ApiResponse(response);
+  }
+
+  Future<ApiResponse> deleteCategoriaProducto({required int id}) async {
+    var response = await delete(
+      '$endPoint/eliminar',
+      body: {
+        'id': id,
+      },
+      withAuth: true,
+    );
+    return ApiResponse(response);
+  }
+}

+ 1 - 0
lib/services/services.dart

@@ -2,3 +2,4 @@ export '../services/base_service.dart';
 export '../services/login_service.dart';
 export '../services/profile_service.dart';
 export '../services/general_service.dart';
+export '../services/categoria_producto_service.dart';

+ 2 - 1
lib/themes/themes.dart

@@ -5,10 +5,11 @@ class AppTheme {
   static Color primary = Color.fromRGBO(242, 75, 89, 1.000);
   static Color secondary = Colors.black;
   static Color tertiary = const Color(0xFF060000);
+  static Color quaternary = const Color(0xFFF1F1F3);
   static ThemeData lightTheme = ThemeData.light().copyWith(
     useMaterial3: true,
     //Scaffold
-    scaffoldBackgroundColor: Colors.grey[300],
+    scaffoldBackgroundColor: Color(0xFFE0E0E0),
     //Tema de AppBar
     appBarTheme: AppBarTheme(
       color: primary,

+ 82 - 0
lib/viewmodels/categoria_producto_view_model.dart

@@ -0,0 +1,82 @@
+import 'package:flutter/material.dart';
+import '../services/services.dart';
+
+import '../models/models.dart';
+
+class CategoriaProductoViewModel extends ChangeNotifier {
+  List<CategoriaProducto> _categoriaProductos = [];
+  bool _isLoading = true;
+
+  String _busqueda = "";
+  String get busqueda => _busqueda;
+
+  CategoriaProducto? selectedCategoriaProducto;
+
+  List<CategoriaProducto> get categoriaProductos => _categoriaProductos;
+  bool get isLoading => _isLoading;
+
+  //Metodo para obtener lista de CategoriaProducto
+  Future<void> fetchCategoriaProductos() async {
+    _categoriaProductos = await CategoriaProductoService().fetchList();
+
+    notifyListeners();
+  }
+
+  int pagina = 1;
+  int totalPaginas = 1;
+  int limite = 10;
+
+  void setLoading(bool loading) {
+    _isLoading = loading;
+    notifyListeners();
+  }
+
+  setBusqueda(String value) {
+    _busqueda = value;
+    notifyListeners();
+  }
+
+  CategoriaProducto selectCategoriaProducto(
+      CategoriaProducto categoriaProducto) {
+    selectedCategoriaProducto = categoriaProducto;
+    return categoriaProducto;
+  }
+
+  cambiarPagina(int nuevaPagina, {bool segmentar = false}) {
+    pagina = nuevaPagina;
+    fetchCategoriaProductos();
+  }
+
+  Future<void> addCategoriaProducto({
+    required String nombre,
+  }) async {
+    await CategoriaProductoService().postCategoriaProducto(
+      nombre: nombre,
+    );
+    await fetchCategoriaProductos();
+    notifyListeners();
+  }
+
+  Future<void> editCategoriaProducto({
+    required int id,
+    required String nombre,
+  }) async {
+    await CategoriaProductoService().editCategoriaProducto(
+      id: id,
+      nombre: nombre,
+    );
+    await fetchCategoriaProductos();
+    notifyListeners();
+  }
+
+  setIsLoading(bool loading) {
+    _isLoading = loading;
+    notifyListeners();
+  }
+
+  Future<void> deleteCategoriaProducto(int id) async {
+    await CategoriaProductoService().deleteCategoriaProducto(id: id);
+    await fetchCategoriaProductos();
+    notifyListeners();
+  }
+}

+ 1 - 0
lib/viewmodels/viewmodels.dart

@@ -1,3 +1,4 @@
 export '../viewmodels/login_view_model.dart';
 export '../viewmodels/usuarios_view_model.dart';
 export '../viewmodels/profile_view_model.dart';
+export '../viewmodels/categoria_producto_view_model.dart';

+ 97 - 0
lib/views/categoria_producto/categoria_producto_form.dart

@@ -0,0 +1,97 @@
+// ignore_for_file: use_build_context_synchronously
+
+import 'package:flutter/material.dart';
+import 'package:yoshi_papas_app/widgets/widgets.dart';
+import 'package:provider/provider.dart';
+
+import '../../models/models.dart';
+import '../../viewmodels/viewmodels.dart';
+
+class CategoriaProductoForm extends StatefulWidget {
+  const CategoriaProductoForm({Key? key}) : super(key: key);
+
+  @override
+  State<CategoriaProductoForm> createState() => Formulario();
+}
+
+class Formulario extends State<CategoriaProductoForm> {
+  final _nombre = TextEditingController();
+  final _busquedaNombre = TextEditingController();
+
+  @override
+  void initState() {
+    super.initState();
+    Future(() async {
+      Provider.of<CategoriaProductoViewModel>(context, listen: false)
+          .setIsLoading(true);
+      final mvm =
+          Provider.of<CategoriaProductoViewModel>(context, listen: false);
+      CategoriaProducto? modelo = mvm.selectedCategoriaProducto;
+      if (modelo != null && modelo.id > 0) {
+        setState(() {
+          _nombre.text = modelo.nombre.toString();
+        });
+      }
+      Provider.of<CategoriaProductoViewModel>(context, listen: false)
+          .setIsLoading(false);
+    });
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+    _nombre.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final mvm = Provider.of<CategoriaProductoViewModel>(context);
+    if (mvm.isLoading) return const Cargando();
+    final segmento = mvm.selectedCategoriaProducto;
+
+    return Scaffold(
+      appBar: encabezado(titulo: "Agregar categoría producto"),
+      body: SingleChildScrollView(
+        padding: const EdgeInsets.all(8),
+        child: Column(
+          children: [
+            tarjeta(
+              Padding(
+                padding: const EdgeInsets.all(8),
+                child: Column(
+                  children: [
+                    Row(
+                      children: [
+                        Expanded(
+                          child: AppTextField(
+                            maxLength: 100,
+                            etiqueta: 'Nombre',
+                            controller: _nombre,
+                            hintText: 'Nombre de categoría producto',
+                          ),
+                        ),
+                      ],
+                    ),
+                  ],
+                ),
+              ),
+            ),
+            const SizedBox(height: 15),
+            boton("Guardar", () async {
+              Provider.of<CategoriaProductoViewModel>(context, listen: false)
+                  .setIsLoading(true);
+              await mvm.addCategoriaProducto(
+                nombre: _nombre.text,
+              );
+              Provider.of<CategoriaProductoViewModel>(context, listen: false)
+                  .setIsLoading(false);
+              if (context.mounted) {
+                Navigator.pop(context);
+              }
+            }),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 152 - 0
lib/views/categoria_producto/categoria_producto_screen copy.dart

@@ -0,0 +1,152 @@
+import 'package:yoshi_papas_app/views/categoria_producto/categoria_producto_form.dart';
+import 'package:flutter/material.dart';
+import '../../themes/themes.dart';
+import '../../viewmodels/viewmodels.dart';
+import 'package:provider/provider.dart';
+
+class CategoriaProductoScreen extends StatefulWidget {
+  const CategoriaProductoScreen({Key? key}) : super(key: key);
+
+  @override
+  State<CategoriaProductoScreen> createState() => _CategoriaProductoState();
+}
+
+class _CategoriaProductoState extends State<CategoriaProductoScreen> {
+  @override
+  void initState() {
+    super.initState();
+    final viewModel =
+        Provider.of<CategoriaProductoViewModel>(context, listen: false);
+    Future(() async {
+      viewModel.setLoading(true);
+      await viewModel.fetchCategoriaProductos();
+      viewModel.setLoading(false);
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final viewModel = Provider.of<CategoriaProductoViewModel>(context);
+    final categoriaProductos = viewModel.categoriaProductos;
+    final isLoading = viewModel.isLoading;
+
+    return Scaffold(
+      floatingActionButton: Padding(
+        padding: const EdgeInsets.only(right: 50.0),
+        child: FloatingActionButton(
+            onPressed: () async {
+              Navigator.push(
+                context,
+                MaterialPageRoute(
+                  builder: (context) => const CategoriaProductoForm(),
+                ),
+              ).then((result) async {
+                if (result != null) {
+                  viewModel.setLoading(true);
+                  await viewModel.addCategoriaProducto(
+                    nombre: result.nombre,
+                  );
+                  viewModel.setLoading(false);
+                }
+              });
+            },
+            child: const Icon(
+              Icons.add,
+              size: 30,
+            )),
+      ),
+      appBar: AppBar(
+        title: const Text('Categoria Producto'),
+      ),
+      body: Center(
+        child: isLoading
+            ? const Center(
+                child: CircularProgressIndicator(),
+              )
+            : ListView.separated(
+                itemCount: categoriaProductos.length,
+                itemBuilder: (context, index) {
+                  var categoriaProducto = categoriaProductos[index];
+                  return ListTile(
+                    onTap: () {
+                      viewModel.selectCategoriaProducto(categoriaProducto);
+                    },
+                    title: Text(categoriaProducto.nombre!),
+                    trailing: PopupMenuButton(
+                      surfaceTintColor: AppTheme.quaternary,
+                      itemBuilder: (context) => [
+                        PopupMenuItem(
+                          child: const Text('Editar'),
+                          onTap: () {
+                            Navigator.push(
+                              context,
+                              MaterialPageRoute(
+                                builder: (context) =>
+                                    const CategoriaProductoForm(),
+                              ),
+                            ).then((result) async {
+                              if (result != null) {
+                                viewModel.setLoading(true);
+                                await viewModel.editCategoriaProducto(
+                                  id: categoriaProducto.id,
+                                  nombre: result.nombre,
+                                );
+                                viewModel.setLoading(false);
+                              }
+                            });
+                          },
+                        ),
+                        PopupMenuItem(
+                          child: const Text(
+                            'Eliminar',
+                            style: TextStyle(color: Colors.red),
+                          ),
+                          onTap: () {
+                            showDialog(
+                              context: context,
+                              builder: (context) {
+                                return AlertDialog(
+                                  title:
+                                      const Text('Eliminar CategoriaProducto'),
+                                  content: const Text(
+                                      '¿Estás seguro de que quieres eliminar esta categoriaProducto?'),
+                                  actions: [
+                                    TextButton(
+                                      onPressed: () {
+                                        Navigator.pop(context);
+                                      },
+                                      child: const Text('Cancelar'),
+                                    ),
+                                    TextButton(
+                                      onPressed: () async {
+                                        Navigator.pop(context);
+                                        viewModel.setLoading(true);
+                                        await viewModel.deleteCategoriaProducto(
+                                            categoriaProducto.id);
+                                        viewModel.setLoading(false);
+                                      },
+                                      child: const Text('Eliminar'),
+                                    ),
+                                  ],
+                                );
+                              },
+                            );
+                          },
+                        )
+                      ],
+                      icon: const Icon(Icons.more_vert),
+                      shape: RoundedRectangleBorder(
+                          borderRadius: BorderRadius.circular(15)),
+                    ),
+                  );
+                },
+                separatorBuilder: (context, index) => const Divider(
+                  height: 0,
+                  thickness: 0,
+                  color: Colors.grey,
+                ),
+              ),
+      ),
+    );
+  }
+}

+ 215 - 0
lib/views/categoria_producto/categoria_producto_screen.dart

@@ -0,0 +1,215 @@
+// ignore_for_file: use_build_context_synchronously
+
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+import '../../models/models.dart';
+import '../../themes/themes.dart';
+import '../../viewmodels/viewmodels.dart';
+import '../../widgets/app_textfield.dart';
+import '../../widgets/pagination_buttons.dart';
+import '../../widgets/widgets_components.dart';
+import 'categoria_producto_form.dart';
+
+class CategoriaProductoScreen extends StatefulWidget {
+  const CategoriaProductoScreen({Key? key}) : super(key: key);
+
+  @override
+  State<CategoriaProductoScreen> createState() => Formulario();
+}
+
+class Formulario extends State<CategoriaProductoScreen> {
+  final _busqueda = TextEditingController(text: '');
+
+  @override
+  void initState() {
+    super.initState();
+    Future(() async {
+      await Provider.of<CategoriaProductoViewModel>(context, listen: false)
+          .fetchCategoriaProductos();
+    });
+  }
+
+  go(CategoriaProducto item) async {
+    Provider.of<CategoriaProductoViewModel>(context, listen: false)
+        .selectCategoriaProducto(item);
+    Navigator.push(
+      context,
+      MaterialPageRoute(
+        builder: (context) => const CategoriaProductoForm(),
+      ),
+    ).then((value) async {
+      await Provider.of<CategoriaProductoViewModel>(context, listen: false)
+          .fetchCategoriaProductos();
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final mvm = Provider.of<CategoriaProductoViewModel>(context);
+    TextStyle estilo = const TextStyle(fontWeight: FontWeight.bold);
+    int vuelta = 0;
+    List<DataRow> categoriaProductos = [];
+    if (mvm.categoriaProductos.isNotEmpty) {
+      for (CategoriaProducto item in mvm.categoriaProductos) {
+        var _tipo = vuelta % 2;
+        vuelta++;
+        categoriaProductos.add(DataRow(selected: _tipo > 0, cells: [
+          DataCell(
+            Text(item.nombre.toString()),
+            onTap: () => go(item),
+          ),
+          DataCell(
+              Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
+            PopupMenuButton(
+              surfaceTintColor: AppTheme.primary,
+              itemBuilder: (context) => [
+                PopupMenuItem(
+                  child: const Text('Editar'),
+                  onTap: () => go(item),
+                ),
+                PopupMenuItem(
+                  child: const Text(
+                    'Eliminar',
+                  ),
+                  onTap: () async {
+                    return showDialog(
+                      context: context,
+                      builder: (context) {
+                        return AlertDialog(
+                          title: const Text("Eliminar registro"),
+                          content: const Text('¿Desea eliminar el registro?'),
+                          actions: [
+                            Row(children: [
+                              Expanded(
+                                  child: TextButton(
+                                onPressed: () {
+                                  Navigator.pop(context);
+                                },
+                                child: const Text('Cancelar'),
+                              )),
+                              Expanded(
+                                  child: TextButton(
+                                onPressed: () async {
+                                  Navigator.pop(context);
+                                },
+                                child: const Text('Continuar'),
+                              ))
+                            ])
+                          ],
+                        );
+                      },
+                    );
+                  },
+                )
+              ],
+              icon: const Icon(Icons.more_vert),
+              shape: RoundedRectangleBorder(
+                  borderRadius: BorderRadius.circular(15)),
+            )
+          ]))
+        ]));
+      }
+    }
+
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Categoría Productos'),
+        actions: [
+          IconButton(
+              onPressed: () async {
+                Provider.of<CategoriaProductoViewModel>(context, listen: false)
+                    .fetchCategoriaProductos();
+              },
+              icon: const Icon(Icons.download))
+        ],
+      ),
+      floatingActionButton: FloatingActionButton(
+        onPressed: () {
+          mvm.selectCategoriaProducto(CategoriaProducto());
+          Navigator.push(
+            context,
+            MaterialPageRoute(
+              builder: (context) => const CategoriaProductoForm(),
+            ),
+          ).then((value) async {
+            await Provider.of<CategoriaProductoViewModel>(context,
+                    listen: false)
+                .fetchCategoriaProductos();
+          });
+        },
+        child: const Icon(Icons.add),
+      ),
+      body: Column(
+        children: [
+          Expanded(
+            child: ListView(
+              padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
+              children: [
+                const SizedBox(height: 8),
+                tarjeta(Padding(
+                    padding: const EdgeInsets.all(10),
+                    child: Row(
+                      children: [
+                        Expanded(
+                          flex: 8,
+                          child: AppTextField(
+                            prefixIcon: const Icon(Icons.search),
+                            maxLength: 100,
+                            etiqueta: 'Búsqueda por nombre...',
+                            controller: _busqueda,
+                            hintText: 'Búsqueda por nombre...',
+                          ),
+                        ),
+                        const SizedBox(width: 5),
+                        Expanded(
+                          flex: 2,
+                          child: botonElevated(
+                            accion: () async {
+                              _busqueda.text = _busqueda.text.trim();
+                              await Provider.of<CategoriaProductoViewModel>(
+                                      context,
+                                      listen: false)
+                                  .setIsLoading(true);
+                              await Provider.of<CategoriaProductoViewModel>(
+                                      context,
+                                      listen: false)
+                                  .setBusqueda(_busqueda.text);
+                              await Provider.of<CategoriaProductoViewModel>(
+                                      context,
+                                      listen: false)
+                                  .fetchCategoriaProductos();
+                              await Provider.of<CategoriaProductoViewModel>(
+                                      context,
+                                      listen: false)
+                                  .setBusqueda("");
+                              await Provider.of<CategoriaProductoViewModel>(
+                                      context,
+                                      listen: false)
+                                  .setIsLoading(false);
+                            },
+                          ),
+                        ),
+                      ],
+                    ))),
+                const SizedBox(height: 8),
+                tarjeta(DataTable(
+                    sortAscending: true,
+                    sortColumnIndex: 1,
+                    columns: [
+                      DataColumn(label: Text("NOMBRE", style: estilo)),
+                      DataColumn(label: Text("", style: estilo)),
+                    ],
+                    rows: categoriaProductos)),
+                PaginationButtons(
+                  currentPage: mvm.pagina,
+                  totalPages: mvm.totalPaginas,
+                  onPageChanged: (i) => mvm.cambiarPagina(i),
+                )
+              ],
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 2 - 1
lib/widgets/app_drawer.dart

@@ -1,6 +1,7 @@
 // ignore_for_file: must_be_immutable
 
 import 'package:flutter/material.dart';
+import 'package:yoshi_papas_app/views/categoria_producto/categoria_producto_screen.dart';
 import 'package:yoshi_papas_app/views/home/home_screen.dart';
 import 'package:yoshi_papas_app/views/perfil/perfil_screen.dart';
 import 'package:yoshi_papas_app/views/pedido/pedido_screen.dart';
@@ -154,7 +155,7 @@ class AppDrawer extends StatelessWidget {
                 Navigator.pop(context),
                 Navigator.of(context).push(
                   MaterialPageRoute(
-                    builder: (context) => const HomeScreen(),
+                    builder: (context) => const CategoriaProductoScreen(),
                   ),
                 ),
               },

+ 1 - 0
lib/widgets/widgets.dart

@@ -6,3 +6,4 @@ export '../widgets/app_text_span.dart';
 export '../widgets/app_drawer.dart';
 export '../widgets/widgets_components.dart';
 export '../widgets/pagination_buttons.dart';
+export '../widgets/app_form_button.dart';

+ 2 - 2
lib/widgets/widgets_components.dart

@@ -89,12 +89,12 @@ class Cargando extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return Scaffold(
-        backgroundColor: const Color(0xFFffe000),
+        backgroundColor: const Color(0xFFE0E0E0),
         body: Center(
           child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
             Padding(
                 padding: const EdgeInsets.fromLTRB(16, 0, 16, 10),
-                child: Image.asset("assets/supergascontigo.png", height: 200)),
+                child: Image.asset("assets/JoshiLogo.png", height: 200)),
             const CircularProgressIndicator(backgroundColor: Colors.grey),
             Container(margin: const EdgeInsets.only(bottom: 8.0)),
             const Text("Cargando contenido...",