From a7a2577a4f7ef16d95dbb64d8d1e6c50e135e016 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 9 Jul 2025 16:13:41 +0200 Subject: [PATCH] search mzansi --- .../mih_personal_profile_preview.dart | 37 ++-- .../mih_loading_circle.dart | 28 ++- .../build_business_search_resultsList.dart | 44 +++++ .../build_user_search_results_list.dart | 41 ++++ .../mzansi_directory/mzansi_directory.dart | 10 +- .../package_tools/mih_search_mzansi.dart | 182 +++++++++++++++++- .../mih_business_details_services.dart | 20 ++ 7 files changed, 336 insertions(+), 26 deletions(-) create mode 100644 Frontend/lib/mih_packages/mzansi_directory/builders/build_business_search_resultsList.dart create mode 100644 Frontend/lib/mih_packages/mzansi_directory/builders/build_user_search_results_list.dart diff --git a/Frontend/lib/mih_components/mih_package_components/mih_personal_profile_preview.dart b/Frontend/lib/mih_components/mih_package_components/mih_personal_profile_preview.dart index f8cb958f..cdc8850a 100644 --- a/Frontend/lib/mih_components/mih_package_components/mih_personal_profile_preview.dart +++ b/Frontend/lib/mih_components/mih_package_components/mih_personal_profile_preview.dart @@ -40,22 +40,33 @@ class _MihPersonalProfilePreviewState extends State { builder: (context, asyncSnapshot) { if (asyncSnapshot.connectionState == ConnectionState.done && asyncSnapshot.hasData) { - return MihCircleAvatar( - imageFile: NetworkImage(asyncSnapshot.requireData), - width: profilePictureWidth, - editable: false, - fileNameController: TextEditingController(), - userSelectedfile: file, - frameColor: - MzanziInnovationHub.of(context)!.theme.secondaryColor(), - backgroundColor: - MzanziInnovationHub.of(context)!.theme.primaryColor(), - onChange: () {}, - ); + if (asyncSnapshot.requireData != "") { + return MihCircleAvatar( + imageFile: NetworkImage(asyncSnapshot.requireData), + width: profilePictureWidth, + editable: false, + fileNameController: TextEditingController(), + userSelectedfile: file, + frameColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + backgroundColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + onChange: () {}, + ); + } else { + return Icon( + MihIcons.iDontKnow, + size: profilePictureWidth, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ); + } } else { return Icon( - MihIcons.mihLogo, + MihIcons.mihRing, size: profilePictureWidth, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), ); } }), diff --git a/Frontend/lib/mih_components/mih_pop_up_messages/mih_loading_circle.dart b/Frontend/lib/mih_components/mih_pop_up_messages/mih_loading_circle.dart index 8d61a660..22f1704d 100644 --- a/Frontend/lib/mih_components/mih_pop_up_messages/mih_loading_circle.dart +++ b/Frontend/lib/mih_components/mih_pop_up_messages/mih_loading_circle.dart @@ -3,7 +3,8 @@ import '../../main.dart'; import 'package:gif_view/gif_view.dart'; class Mihloadingcircle extends StatefulWidget { - const Mihloadingcircle({super.key}); + final String? message; + const Mihloadingcircle({super.key, this.message}); @override State createState() => _MihloadingcircleState(); @@ -53,7 +54,7 @@ class _MihloadingcircleState extends State { child: Container( padding: EdgeInsets.all(popUpPaddingSize), width: 250, - height: 250, + height: 275, decoration: BoxDecoration( color: MzanziInnovationHub.of(context)!.theme.primaryColor(), borderRadius: BorderRadius.circular(25.0), @@ -61,11 +62,24 @@ class _MihloadingcircleState extends State { color: MzanziInnovationHub.of(context)!.theme.primaryColor(), width: 5.0), ), - child: GifView.asset( - MzanziInnovationHub.of(context)!.theme.loadingImageLocation(), - height: 200, - width: 200, - frameRate: 30, + child: Column( + children: [ + GifView.asset( + MzanziInnovationHub.of(context)!.theme.loadingImageLocation(), + height: 200, + width: 200, + frameRate: 30, + ), + widget.message != null + ? Text( + widget.message!, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ) + : SizedBox(), + ], )), ); } diff --git a/Frontend/lib/mih_packages/mzansi_directory/builders/build_business_search_resultsList.dart b/Frontend/lib/mih_packages/mzansi_directory/builders/build_business_search_resultsList.dart new file mode 100644 index 00000000..60806ff0 --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_directory/builders/build_business_search_resultsList.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/business.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_business_profile_preview.dart'; + +class BuildBusinessSearchResultsList extends StatefulWidget { + final List businessList; + final String myLocation; + const BuildBusinessSearchResultsList({ + super.key, + required this.businessList, + required this.myLocation, + }); + + @override + State createState() => + _BuildBusinessSearchResultsListState(); +} + +class _BuildBusinessSearchResultsListState + extends State { + @override + Widget build(BuildContext context) { + return ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: widget.businessList.length, + separatorBuilder: (BuildContext context, index) { + return Divider( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ); + }, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 15.0), + child: MihBusinessProfilePreview( + business: widget.businessList[index], + myLocation: widget.myLocation, + ), + ); + }, + ); + } +} diff --git a/Frontend/lib/mih_packages/mzansi_directory/builders/build_user_search_results_list.dart b/Frontend/lib/mih_packages/mzansi_directory/builders/build_user_search_results_list.dart new file mode 100644 index 00000000..acab3150 --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_directory/builders/build_user_search_results_list.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/app_user.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_personal_profile_preview.dart'; + +class BuildUserSearchResultsList extends StatefulWidget { + final List userList; + const BuildUserSearchResultsList({ + super.key, + required this.userList, + }); + + @override + State createState() => + _BuildUserSearchResultsListState(); +} + +class _BuildUserSearchResultsListState + extends State { + @override + Widget build(BuildContext context) { + return ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: widget.userList.length, + separatorBuilder: (BuildContext context, index) { + return Divider( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ); + }, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 15.0), + child: MihPersonalProfilePreview( + user: widget.userList[index], + ), + ); + }, + ); + } +} diff --git a/Frontend/lib/mih_packages/mzansi_directory/mzansi_directory.dart b/Frontend/lib/mih_packages/mzansi_directory/mzansi_directory.dart index 3bdfc329..f872e271 100644 --- a/Frontend/lib/mih_packages/mzansi_directory/mzansi_directory.dart +++ b/Frontend/lib/mih_packages/mzansi_directory/mzansi_directory.dart @@ -34,9 +34,9 @@ class _MzansiDirectoryState extends State { List getToolBody() { List toolBodies = [ + MihSearchMzansi(), MihContacts(), MihFavouriteBusinesses(), - MihSearchMzansi(), ]; return toolBodies; } @@ -54,17 +54,17 @@ class _MzansiDirectoryState extends State { MihPackageTools getTools() { Map temp = {}; - temp[const Icon(Icons.person)] = () { + temp[const Icon(Icons.search)] = () { setState(() { _selcetedIndex = 0; }); }; - temp[const Icon(Icons.business_center)] = () { + temp[const Icon(Icons.person)] = () { setState(() { _selcetedIndex = 1; }); }; - temp[const Icon(Icons.search)] = () { + temp[const Icon(Icons.business_center)] = () { setState(() { _selcetedIndex = 2; }); @@ -77,9 +77,9 @@ class _MzansiDirectoryState extends State { List getToolTitle() { List toolTitles = [ + "Search Mzansi", "Contacts", "Favourite Businesses", - "Search Mzansi", ]; return toolTitles; } diff --git a/Frontend/lib/mih_packages/mzansi_directory/package_tools/mih_search_mzansi.dart b/Frontend/lib/mih_packages/mzansi_directory/package_tools/mih_search_mzansi.dart index 24d18acc..b7cc477a 100644 --- a/Frontend/lib/mih_packages/mzansi_directory/package_tools/mih_search_mzansi.dart +++ b/Frontend/lib/mih_packages/mzansi_directory/package_tools/mih_search_mzansi.dart @@ -1,8 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/app_user.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/business.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tool_body.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_search_bar.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_directory/builders/build_business_search_resultsList.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_directory/builders/build_user_search_results_list.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_business_details_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_location_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_user_services.dart'; class MihSearchMzansi extends StatefulWidget { const MihSearchMzansi({super.key}); @@ -14,6 +23,24 @@ class MihSearchMzansi extends StatefulWidget { class _MihSearchMzansiState extends State { final TextEditingController mzansiSearchController = TextEditingController(); final FocusNode searchFocusNode = FocusNode(); + bool userSearch = true; + Future?> futureUserSearchResults = Future.value(); + Future?> futureBusinessSearchResults = Future.value(); + late Future futurePosition = + MIHLocationAPI().getGPSPosition(context); + List userSearchResults = []; + List businessSearchResults = []; + + @override + void dispose() { + super.dispose(); + mzansiSearchController.dispose(); + } + + @override + void initState() { + super.initState(); + } @override Widget build(BuildContext context) { @@ -35,16 +62,169 @@ class _MihSearchMzansiState extends State { controller: mzansiSearchController, hintText: "Search Mzansi", prefixIcon: Icons.search, + suffixTools: [ + IconButton( + onPressed: () { + setState(() { + // searchTypeVisibility = !searchTypeVisibility; + userSearch = !userSearch; + }); + }, + icon: Icon( + Icons.display_settings, + size: 35, + color: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + )) + ], fillColor: MzanziInnovationHub.of(context)!.theme.secondaryColor(), hintColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), - onPrefixIconTap: () {}, + onPrefixIconTap: () { + if (userSearch) { + setState(() { + futureUserSearchResults = MihUserServices() + .searchUsers(mzansiSearchController.text, context); + }); + } else { + setState(() { + futureBusinessSearchResults = MihBusinessDetailsServices() + .searchBusinesses(mzansiSearchController.text, context); + }); + } + }, + onClearIconTap: () { + setState(() { + futureUserSearchResults = Future.value(); + futureBusinessSearchResults = Future.value(); + mzansiSearchController.clear(); + }); + }, searchFocusNode: searchFocusNode, ), ), const SizedBox(height: 10), + FutureBuilder( + future: futurePosition, + builder: (context, asyncSnapshot) { + if (asyncSnapshot.connectionState == ConnectionState.waiting) { + return const Mihloadingcircle( + message: "Getting things ready for you", + ); + } else { + final myLocation = asyncSnapshot.data + .toString() + .replaceAll("Latitude: ", "") + .replaceAll("Longitude: ", ""); + print("My Location is : $myLocation"); + return displaySearchResults(userSearch, myLocation); + } + }), ], ), ); } + + Widget displaySearchResults(bool userSearch, String myLocation) { + if (userSearch) { + return FutureBuilder( + future: futureUserSearchResults, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Mihloadingcircle(); + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData) { + // return Text("Pulled Data successfully"); + return BuildUserSearchResultsList(userList: snapshot.requireData!); + } else if (!snapshot.hasData) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.search, + size: 165, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + const SizedBox(height: 10), + Text( + "People Of Mzansi!", + textAlign: TextAlign.center, + overflow: TextOverflow.visible, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ], + ); + } else { + return Center( + child: Text( + "Error pulling Patients Data\n/users/search/${mzansiSearchController.text}", + style: TextStyle( + fontSize: 25, + color: MzanziInnovationHub.of(context)!.theme.errorColor()), + textAlign: TextAlign.center, + ), + ); + } + }, + ); + } else { + return FutureBuilder( + future: futureBusinessSearchResults, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Mihloadingcircle(); + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData) { + // return Text("Pulled Data successfully"); + return BuildBusinessSearchResultsList( + businessList: snapshot.requireData!, + myLocation: myLocation, + ); + } else if (!snapshot.hasData) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.search, + size: 165, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + const SizedBox(height: 10), + Text( + "Businesses Of Mzansi!", + textAlign: TextAlign.center, + overflow: TextOverflow.visible, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ], + ); + } else { + return Center( + child: Text( + "Error pulling Patients Data\n/users/search/${mzansiSearchController.text}", + style: TextStyle( + fontSize: 25, + color: MzanziInnovationHub.of(context)!.theme.errorColor()), + textAlign: TextAlign.center, + ), + ); + } + }, + ); + } + } } diff --git a/Frontend/lib/mih_services/mih_business_details_services.dart b/Frontend/lib/mih_services/mih_business_details_services.dart index aa469d5f..5049c148 100644 --- a/Frontend/lib/mih_services/mih_business_details_services.dart +++ b/Frontend/lib/mih_services/mih_business_details_services.dart @@ -9,6 +9,26 @@ import '../mih_components/mih_pop_up_messages/mih_error_message.dart'; import 'package:supertokens_flutter/http.dart' as http; class MihBusinessDetailsServices { + Future> searchBusinesses( + String searchText, + BuildContext context, + ) async { + var response = await http.get( + Uri.parse("${AppEnviroment.baseApiUrl}/businesses/search/$searchText"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + ); + if (response.statusCode == 200) { + Iterable l = jsonDecode(response.body); + List businesses = + List.from(l.map((model) => Business.fromJson(model))); + return businesses; + } else { + throw Exception('failed to load users'); + } + } + Future getBusinessDetails( String app_id, ) async {