diff --git a/lib/src/delegate/SearchDelegate.dart b/lib/src/delegate/SearchDelegate.dart new file mode 100644 index 0000000..a60f5e1 --- /dev/null +++ b/lib/src/delegate/SearchDelegate.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; + +import '../http_api/ArticleApi.dart'; +import '../models/ArticuloModel.dart'; + +class ArticleSearchDelegate extends SearchDelegate { + final ArticleApi articleApi = ArticleApi(); + + ArticleSearchDelegate() : super(searchFieldLabel: 'Buscar artículos'); + + @override + List buildActions(BuildContext context) { + return [ + IconButton( + icon: Icon(Icons.clear), + onPressed: () { + query = ''; + }, + ), + ]; + } + + @override + Widget buildLeading(BuildContext context) { + return IconButton( + icon: AnimatedIcon( + icon: AnimatedIcons.menu_arrow, + progress: transitionAnimation, + ), + onPressed: () { + close(context, 'home'); + }, + ); + } + + @override + Widget buildResults(BuildContext context) { + return FutureBuilder>( + future: articleApi.searchArticles(query), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } else if (!snapshot.hasData || snapshot.data!.isEmpty) { + return Center(child: Text('No articles found.')); + } else { + final articles = snapshot.data!; + return ListView.builder( + itemCount: articles.length, + itemBuilder: (context, index) { + final article = articles[index]; + return ListTile( + title: Text(article.nombre), + subtitle: Text(article.precios.isNotEmpty ? '\$${article.precios.first.precio}' : 'No price available'), + trailing: IconButton( + icon: Icon(Icons.add_shopping_cart), + onPressed: () { + + close(context, article.nombre); + }, + ), + ); + }, + ); + } + }, + ); + } + + @override + Widget buildSuggestions(BuildContext context) { + if (query.isEmpty) { + return Center(child: Text('Introduce un término de búsqueda')); + } + + return FutureBuilder>( + future: articleApi.searchArticles(query), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Center(child: CircularProgressIndicator()); + } else if (snapshot.hasError) { + return Center(child: Text('Error: ${snapshot.error}')); + } else if (!snapshot.hasData || snapshot.data!.isEmpty) { + return Center(child: Text('No articles found.')); + } else { + final articles = snapshot.data!; + return ListView.builder( + itemCount: articles.length, + itemBuilder: (context, index) { + final article = articles[index]; + return ListTile( + title: Text(article.nombre), + subtitle: Text(article.precios.isNotEmpty ? '\$${article.precios.first.precio}' : 'No price available'), + trailing: IconButton( + icon: Icon(Icons.add_shopping_cart), + onPressed: () { + + close(context, article.nombre); + }, + ), + ); + }, + ); + } + }, + ); + } +} diff --git a/lib/src/http_api/ArticleApi.dart b/lib/src/http_api/ArticleApi.dart index b9c53d3..c5dc717 100644 --- a/lib/src/http_api/ArticleApi.dart +++ b/lib/src/http_api/ArticleApi.dart @@ -2,20 +2,19 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:flutter/foundation.dart'; import 'package:miapp_flutter/environments/archivo.dart'; +import '../models/ArticuloModel.dart'; class ArticleApi { - final String apiUrl = 'articulo'; + final String apiUrl = '${apiApp}/articulo'; - // Método para obtener los artículos Future> getArticles(int categoryId) async { - String url = '${apiApp}/$apiUrl?categoria=$categoryId&offset=0&max=100'; + String url = '$apiUrl?categoria=$categoryId&offset=0&max=100'; if (kDebugMode) { print('Url -> $url'); } try { final response = await http.get(Uri.parse(url)); - // Verifica el estado de la respuesta if (response.statusCode == 200) { return { 'statusCode': response.statusCode, @@ -34,4 +33,26 @@ class ArticleApi { }; } } + + Future> searchArticles(String query) async { + try { + final response = await http.get(Uri.parse('$apiUrl?nombre=$query')); + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + + if (response.statusCode == 200) { + try { + final Map data = json.decode(response.body); + final List articlesJson = data['data']; + return ArticleModel.fromJsonArray(articlesJson); + } catch (e) { + throw Exception('Error en procesamiento de datos: $e'); + } + } else { + throw Exception('Error en la respuesta de la API: ${response.body}'); + } + } catch (e) { + throw Exception('Error en la solicitud a la API: $e'); + } + } } diff --git a/lib/src/models/ArticuloModel.dart b/lib/src/models/ArticuloModel.dart index 12e6def..3bb1114 100644 --- a/lib/src/models/ArticuloModel.dart +++ b/lib/src/models/ArticuloModel.dart @@ -34,6 +34,21 @@ class ArticleModel { static List fromJsonArray(List jsonArray) { return jsonArray.map((json) => ArticleModel.fromJson(json)).toList(); } + + Map toJson() { + return { + 'id': id, + 'clave': clave, + 'nombre': nombre, + 'categoria': {'id': categoriaId}, + 'precios': precios.map((p) => p.toJson()).toList(), + 'activo': activo, + }; + } + + static List> toJsonArray(List articles) { + return articles.map((article) => article.toJson()).toList(); + } } class Price { @@ -51,4 +66,11 @@ class Price { precio: json['precio'].toDouble(), ); } -} \ No newline at end of file + + Map toJson() { + return { + 'id': id, + 'precio': precio, + }; + } +} diff --git a/lib/src/models/CarritoModel.dart b/lib/src/models/CarritoModel.dart new file mode 100644 index 0000000..5a15bbc --- /dev/null +++ b/lib/src/models/CarritoModel.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import '../models/ArticuloModel.dart'; + +class CarritoModel { + final ArticleModel articulo; + int cantidad; + double precio; + + CarritoModel({ + required this.articulo, + this.cantidad = 1, + required this.precio, + }); +} + +class CarritoProvider with ChangeNotifier { + List _carrito = []; + + + void addToCart(ArticleModel article) { + final existingItemIndex = _carrito.indexWhere((item) => item.articulo.id == article.id); + + if (existingItemIndex >= 0) { + // Si el artículo ya está en el carrito, aumenta la cantidad + _carrito[existingItemIndex].cantidad += 1; + } else { + // Si el artículo no está en el carrito, agrégalo + final newItem = CarritoModel( + articulo: article, + precio: article.precios.isNotEmpty ? article.precios.first.precio : 0.0, + ); + _carrito.add(newItem); + } + + notifyListeners(); + } + + int get totalCarrito { + return _carrito.length; + } + + List get carrito { + return [..._carrito]; + } +} diff --git a/lib/src/pages/NewCategoryPage.dart b/lib/src/pages/NewCategoryPage.dart index 60d06a2..c133880 100644 --- a/lib/src/pages/NewCategoryPage.dart +++ b/lib/src/pages/NewCategoryPage.dart @@ -88,15 +88,17 @@ class _NewCategoryPageState extends State { print('Datos de la categoría a enviar: ${json.encode(newCategory.toJson())}'); print(response); - if (response['statusCode'] == 200 || response['statusCode'] == 201) { + if (response['ok'] ) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Error al crear categoría: ${response['body']}')), + SnackBar(content: Text('Categoría creada con éxito')), ); + Navigator.pushNamed(context,'home'); } else { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Categoría creada con éxito')), + SnackBar(content: Text('Error al crear categoría: ${response['body']}')), + ); - Navigator.pop(context); + } } }, diff --git a/lib/src/pages/home_page.dart b/lib/src/pages/home_page.dart index fa9d467..390f7c6 100644 --- a/lib/src/pages/home_page.dart +++ b/lib/src/pages/home_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:miapp_flutter/src/specific_widgets/menu.dart'; +import '../delegate/SearchDelegate.dart'; import 'NewCategoryPage.dart'; import 'category/CategoryListWidget.dart'; @@ -28,17 +29,23 @@ class _HomePageState extends State { String selectedPage = ''; void _handleNewCategory() { - // Navega a la página de nueva categoría Navigator.push( context, MaterialPageRoute(builder: (context) => const NewCategoryPage()), ); } + void _startSearch() async { + showSearch( + context: context, + delegate: ArticleSearchDelegate(), + ); + } + @override Widget build(BuildContext context) { return DefaultTabController( - length: 2, // Número de pestañas + length: 2, child: Scaffold( appBar: AppBar( title: const Text('Home Page'), @@ -53,6 +60,12 @@ class _HomePageState extends State { ); }, ), + actions: [ + IconButton( + icon: const Icon(Icons.search), + onPressed: _startSearch, + ), + ], bottom: const TabBar( tabs: [ Tab(icon: Icon(Icons.home), text: 'Categoria'), diff --git a/pubspec.lock b/pubspec.lock index 7ed2635..da9d801 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -200,6 +200,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.12.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" nm: dependency: transitive description: @@ -232,6 +240,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + provider: + dependency: "direct main" + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 1ceca89..d8b2f77 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: http: ^1.2.2 connectivity_plus: ^6.0.4 intl: ^0.18.0 + provider: ^6.1.2 dev_dependencies: flutter_test: