From 2f23dbd833c5a5902665f532048fff950bce6571 Mon Sep 17 00:00:00 2001 From: yenisleydi <126813935+yenisleydi@users.noreply.github.com> Date: Thu, 1 Aug 2024 17:42:16 -0600 Subject: [PATCH] Tarea terminada --- lib/src/config/routes.dart | 4 +++- lib/src/controllers/articles_controller.dart | 41 +++++++++++++++++++++++++++++++++++++++++ lib/src/http_api/articles_api.dart | 21 +++++++++++++++++++++ lib/src/models/articles_model.dart | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/src/pages/articles_page.dart | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ lib/src/pages/category/lista_categorias.dart | 7 +++++++ lib/src/pages/formulario_categoria.dart | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/src/pages/home_page.dart | 6 +++--- 8 files changed, 271 insertions(+), 10 deletions(-) create mode 100644 lib/src/controllers/articles_controller.dart create mode 100644 lib/src/http_api/articles_api.dart create mode 100644 lib/src/models/articles_model.dart create mode 100644 lib/src/pages/formulario_categoria.dart diff --git a/lib/src/config/routes.dart b/lib/src/config/routes.dart index 200bdf5..0a158ba 100644 --- a/lib/src/config/routes.dart +++ b/lib/src/config/routes.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:primer_practica/src/pages/formulario_categoria.dart'; import 'package:primer_practica/src/pages/login_page.dart'; import 'package:primer_practica/src/pages/home_page.dart'; import 'package:primer_practica/src/pages/articles_page.dart'; @@ -7,6 +8,7 @@ Map getApplicationRoutes() { return { 'login': (BuildContext context) => const LoginPage(), 'home': (BuildContext context) => const HomePage(), - 'articles': (BuildContext context) => const ArticlesPage(), + //'articles': (BuildContext context) => const ArticlesPage(), + 'formulario': (BuildContext context) => const FormularioCategoria(), }; } diff --git a/lib/src/controllers/articles_controller.dart b/lib/src/controllers/articles_controller.dart new file mode 100644 index 0000000..f08bd67 --- /dev/null +++ b/lib/src/controllers/articles_controller.dart @@ -0,0 +1,41 @@ +import 'dart:convert'; +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:primer_practica/src/models/articles_model.dart'; + +import '../http_api/articles_api.dart'; + +class ArticleController { + final Connectivity _connectivity = Connectivity(); + final ArticlesApi _articleApi = ArticlesApi(); + + Future> getArticles(int categoryId) async { + Map mapResp = { + 'ok': false, + 'message': 'No hay artículos', + 'data': null + }; + + ConnectivityResult connectivityResult = await _connectivity.checkConnectivity(); + if (connectivityResult != ConnectivityResult.none) { + if (connectivityResult == ConnectivityResult.wifi || connectivityResult == ConnectivityResult.mobile) { + Map respGet = await _articleApi.getArticles(); + + if (respGet['statusCode'] == 200) { + try { + var decodeResp = json.decode(respGet['body']); + List listArticles = ArticlesModel.fromJsonArray(decodeResp['data']); + mapResp['ok'] = true; + mapResp['message'] = "${listArticles.length} artículos encontrados"; + mapResp['data'] = listArticles; + } catch (e) { + mapResp['message'] = "Error en el procesamiento de datos: $e"; + } + } else { + mapResp['message'] = "${respGet['body']}"; + } + } + } + + return mapResp; + } +} \ No newline at end of file diff --git a/lib/src/http_api/articles_api.dart b/lib/src/http_api/articles_api.dart new file mode 100644 index 0000000..ffa8da5 --- /dev/null +++ b/lib/src/http_api/articles_api.dart @@ -0,0 +1,21 @@ +import 'package:flutter/foundation.dart'; +import 'package:http/http.dart' as http; +import 'package:primer_practica/environments/urls.dart' as api; + +class ArticlesApi { + Future> getArticles() async { + // Usa la constante `apiBaseUrl` de `urls.dart` para construir la URL + String url = '${api.apiApp}/articulo?offset=0&max=100'; + + if (kDebugMode) { + print('Url -> $url'); + } + + try { + final resp = await http.get(Uri.parse(url)); + return {"statusCode": resp.statusCode, "body": resp.body}; + } catch (e) { + return {"statusCode": 501, "body": '$e'}; + } + } +} diff --git a/lib/src/models/articles_model.dart b/lib/src/models/articles_model.dart new file mode 100644 index 0000000..72915e4 --- /dev/null +++ b/lib/src/models/articles_model.dart @@ -0,0 +1,52 @@ +class ArticlesModel{ + final int id; + final String clave; + final String nombre; + final int categoriaId; + final List precios; + final bool activo; + + ArticlesModel({ + required this.id, + required this.clave, + required this.nombre, + required this.categoriaId, + required this.precios, + required this.activo, + }); + + factory ArticlesModel.fromJson(Map json) { + var list = json['precios'] as List; + List preciosList = list.map((i) => Price.fromJson(i)).toList(); + + return ArticlesModel( + id: json['id'], + clave: json['clave'], + nombre: json['nombre'], + categoriaId: json['categoria']['id'], + precios: preciosList, + activo: json['activo'], + ); + } + + static List fromJsonArray(List jsonArray) { + return jsonArray.map((json) => ArticlesModel.fromJson(json)).toList(); + } +} + +class Price { + final int id; + final double precio; + + Price({ + required this.id, + required this.precio, + }); + + factory Price.fromJson(Map json) { + return Price( + id: json['id'], + precio: json['precio'].toDouble(), + ); + } +} \ No newline at end of file diff --git a/lib/src/pages/articles_page.dart b/lib/src/pages/articles_page.dart index 5fafccf..05b3404 100644 --- a/lib/src/pages/articles_page.dart +++ b/lib/src/pages/articles_page.dart @@ -1,17 +1,75 @@ import 'package:flutter/material.dart'; +import 'package:primer_practica/src/controllers/articles_controller.dart'; +import 'package:primer_practica/src/models/articles_model.dart'; -class ArticlesPage extends StatelessWidget { - const ArticlesPage({Key? key}) : super(key: key); +class ArticlePage extends StatefulWidget { + + final int categoryId; + + // creamos un constructor de ArticlePage que recibe categoryId como parámetro. + const ArticlePage({Key? key, required this.categoryId}) : super(key: key); + @override + _ArticlePageState createState() => _ArticlePageState(); +} + +class _ArticlePageState extends State { + final ArticleController _articleController = ArticleController(); + + late Future> _articlesFuture; + + @override + void initState() { + super.initState(); + // Inicializa _articlesFuture llamando al método getArticles del controlador de + // artículos con el categoryId. + _articlesFuture = _articleController.getArticles(widget.categoryId); + } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Articles Page'), + title: const Text('Artículos'), + backgroundColor: Colors.indigoAccent, + foregroundColor: Colors.white, ), - body: const Center( - child: Text('Hola articles page'), + + body: FutureBuilder>( + future: _articlesFuture, + builder: (context, snapshot) { + if (snapshot.hasData) { + if (snapshot.data!['ok']) { + if (snapshot.data!['data'] != null && (snapshot.data!['data'] as List).isNotEmpty) { + List articles = snapshot.data!['data'] as List; + //Lista de articulos + return ListView.builder( + // Definimos el número de elementos en la lista. + itemCount: articles.length, + itemBuilder: (context, index) { + final article = articles[index]; + return Card( + child: ListTile( + title: Text(article.nombre), + subtitle: Text('id :${article.categoriaId}'), + //subtitle: Text('Clave: ${article.clave}'), + trailing: Text('\$${article.precios.isNotEmpty ? article.precios.first.precio.toStringAsFixed(2) : 'N/A'}'), + ), + ); + }, + ); + } else { + return Center(child: Text('No se encontraron artículos.')); + } + } else { + return Center(child: Text('Error: ${snapshot.data!['message']}')); + } + } else if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, ), ); } -} +} \ No newline at end of file diff --git a/lib/src/pages/category/lista_categorias.dart b/lib/src/pages/category/lista_categorias.dart index 15f0e3d..adbf832 100644 --- a/lib/src/pages/category/lista_categorias.dart +++ b/lib/src/pages/category/lista_categorias.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:primer_practica/src/controllers/categoty_controller.dart'; +import 'package:primer_practica/src/pages/articles_page.dart'; import 'package:primer_practica/src/shared_widgets/widget_mensajes.dart' as msg_shared; import 'package:primer_practica/src/models/categoryModel.dart'; @@ -25,6 +26,12 @@ class ListaCategorias extends StatelessWidget { title: Text(category.name?? 'No Name'), // Muestra el nombre de la categoría leading: const Icon(Icons.touch_app), onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ArticlePage(categoryId: category.id), + ), + ); // Navegar al listado de servicios }, ), diff --git a/lib/src/pages/formulario_categoria.dart b/lib/src/pages/formulario_categoria.dart new file mode 100644 index 0000000..616ceb0 --- /dev/null +++ b/lib/src/pages/formulario_categoria.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; + +class FormularioCategoria extends StatefulWidget { + const FormularioCategoria({super.key}); + + @override + _FormularioCategoriaState createState() => _FormularioCategoriaState(); +} + +class _FormularioCategoriaState extends State { + final _nombreController = TextEditingController(); + final _claveController = TextEditingController(); + + @override + void dispose() { + _nombreController.dispose(); + _claveController.dispose(); + super.dispose(); + } + + void _guardarCategoria() { + final nombre = _nombreController.text; + final clave = _claveController.text; + + if (nombre.isEmpty || clave.isEmpty) { + // Mostrar un mensaje de error si algún campo está vacío + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Por favor, complete todos los campos')), + ); + return; + } + + final fechaCreado = DateTime.now().millisecondsSinceEpoch; + + final categoria = { + "clave": clave, + "fechaCreado": fechaCreado, + "nombre": nombre, + }; + + print(categoria); // Imprime el mensaje en consola + + // Aquí puedes agregar lógica adicional para guardar los datos en una base de datos o enviar a una API + + // Volver a la pantalla anterior + Navigator.pop(context); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Agregar Categoría'), + ), + body: Padding( + padding: const EdgeInsets.all(180.0), + child: Column( + children: [ + TextField( + controller: _claveController, + decoration: const InputDecoration(labelText: 'Clave'), + keyboardType: TextInputType.text, + ), + const SizedBox(height:40), + TextField( + controller: _nombreController, + decoration: const InputDecoration(labelText: 'Nombre'), + keyboardType: TextInputType.text, + ), + const SizedBox(height: 30), + ElevatedButton( + onPressed: _guardarCategoria, + child: const Text('GUARDAR'), + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/pages/home_page.dart b/lib/src/pages/home_page.dart index 6cce8ae..23aec7c 100644 --- a/lib/src/pages/home_page.dart +++ b/lib/src/pages/home_page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:primer_practica/src/pages/category/lista_categorias.dart'; // Importa el widget de la lista de categorías +import 'package:primer_practica/src/pages/category/lista_categorias.dart'; class HomePage extends StatelessWidget { const HomePage({Key? key}) : super(key: key); @@ -41,10 +41,10 @@ class HomePage extends StatelessWidget { ], ), ), - body: ListaCategorias(), // Usa el widget de la lista de categorías en el body + body: ListaCategorias(), floatingActionButton: FloatingActionButton( onPressed: () { - Navigator.pushNamed(context, 'login'); + Navigator.pushNamed(context, 'formulario'); }, child: const Icon(Icons.add), tooltip: 'Agregar categoria', -- libgit2 0.27.1