enhanced search by gemini

This commit is contained in:
2025-07-30 14:02:52 +02:00
parent 98381800fc
commit 911f4e82d9
2 changed files with 165 additions and 116 deletions

View File

@@ -1,20 +1,16 @@
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_components/mih_objects/arguments.dart';
import 'package:mzansi_innovation_hub/mih_components/mih_objects/bookmarked_business.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';
import 'package:mzansi_innovation_hub/mih_services/mih_business_details_services.dart';
class BuildFavouriteBusinessesList extends StatefulWidget {
final List<BookmarkedBusiness> favouriteBusinesses;
final List<Business?> favouriteBusinesses;
final String? myLocation;
final String? searchQuery;
const BuildFavouriteBusinessesList({
super.key,
required this.favouriteBusinesses,
required this.myLocation,
required this.searchQuery,
});
@override
@@ -24,28 +20,6 @@ class BuildFavouriteBusinessesList extends StatefulWidget {
class _BuildFavouriteBusinessesListState
extends State<BuildFavouriteBusinessesList> {
List<Business?> businesses = [];
List<Business?> getListOfBusinesses() {
List<Business?> businesses = [];
for (var item in widget.favouriteBusinesses) {
MihBusinessDetailsServices()
.getBusinessDetailsByBusinessId(item.business_id)
.then((business) {
if (business != null) {
businesses.add(business);
}
});
}
return businesses;
}
@override
void initState() {
super.initState();
businesses = getListOfBusinesses();
}
@override
Widget build(BuildContext context) {
return ListView.separated(
@@ -58,63 +32,37 @@ class _BuildFavouriteBusinessesListState
);
},
itemBuilder: (context, index) {
Future<Business?> businessDetails =
MihBusinessDetailsServices().getBusinessDetailsByBusinessId(
widget.favouriteBusinesses[index].business_id,
);
return FutureBuilder<Business?>(
future: businessDetails,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Row(
children: [
Padding(
padding: const EdgeInsets.only(left: 25.0),
child: CircularProgressIndicator(),
),
],
);
} else if (snapshot.hasError) {
return Center(
child: Text(
'Error: ${snapshot.error}',
style: TextStyle(color: Colors.red),
final Business? business = widget.favouriteBusinesses[index];
if (business == null) {
return const SizedBox(); // Or a placeholder if a business couldn't be loaded
}
return Material(
color: MzansiInnovationHub.of(context)!.theme.primaryColor(),
child: InkWell(
onTap: () {
Navigator.of(context).pushNamed(
'/business-profile/view',
arguments: BusinessViewArguments(
business,
business.Name,
),
);
} else if (snapshot.hasData && snapshot.data != null) {
Business business = snapshot.data!;
return Material(
color: MzansiInnovationHub.of(context)!.theme.primaryColor(),
child: InkWell(
onTap: () {
Navigator.of(context).pushNamed(
'/business-profile/view',
arguments: BusinessViewArguments(
business,
business.Name,
),
);
},
splashColor: MzansiInnovationHub.of(context)!
.theme
.secondaryColor()
.withOpacity(0.2),
borderRadius: BorderRadius.circular(15),
child: Padding(
padding: EdgeInsetsGeometry.symmetric(
// vertical: 5,
horizontal: 25,
),
child: MihBusinessProfilePreview(
business: business, myLocation: widget.myLocation),
),
),
);
} else {
print(snapshot.data);
return SizedBox();
}
},
},
splashColor: MzansiInnovationHub.of(context)!
.theme
.secondaryColor()
.withOpacity(0.2),
borderRadius: BorderRadius.circular(15),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 25,
),
child: MihBusinessProfilePreview(
business: business, myLocation: widget.myLocation),
),
),
);
},
);

View File

@@ -1,11 +1,15 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:mzansi_innovation_hub/main.dart';
import 'package:mzansi_innovation_hub/mih_components/mih_objects/bookmarked_business.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_favourite_businesses_list.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_business_details_services.dart';
import 'package:mzansi_innovation_hub/mih_services/mih_mzansi_directory_services.dart';
import 'package:supertokens_flutter/supertokens.dart';
@@ -26,33 +30,55 @@ class _MihFavouriteBusinessesState extends State<MihFavouriteBusinesses> {
final FocusNode searchFocusNode = FocusNode();
late Future<List<BookmarkedBusiness>> boookmarkedBusinessListFuture;
List<BookmarkedBusiness> listBookmarkedBusinesses = [];
final ValueNotifier<List<BookmarkedBusiness>> searchBookmarkedBusinesses =
final ValueNotifier<List<Business?>> searchBookmarkedBusinesses =
ValueNotifier([]);
Future<List<BookmarkedBusiness>> getAllBookmarkedBusinessesForUser() async {
late Future<Map<String, Business?>> businessDetailsMapFuture;
Map<String, Business?> _businessDetailsMap = {};
Timer? _debounce;
Future<Map<String, Business?>>
getAndMapAllBusinessDetailsForBookmarkedBusinesses() async {
String user_id = await SuperTokens.getUserId();
return MihMzansiDirectoryServices().getAllUserBookmarkedBusiness(user_id);
List<BookmarkedBusiness> bookmarked = await MihMzansiDirectoryServices()
.getAllUserBookmarkedBusiness(user_id);
// Store the bookmarked list for search filtering
listBookmarkedBusinesses = bookmarked;
Map<String, Business?> businessMap = {};
List<Future<Business?>> detailFutures = [];
for (var item in bookmarked) {
detailFutures.add(MihBusinessDetailsServices()
.getBusinessDetailsByBusinessId(item.business_id));
}
List<Business?> details = await Future.wait(detailFutures);
for (int i = 0; i < bookmarked.length; i++) {
businessMap[bookmarked[i].business_id] = details[i];
}
_businessDetailsMap = businessMap;
_filterAndSetBusinesses();
return businessMap;
}
void searchBookmarkedBusinessByName() {
if (businessSearchController.text.isEmpty) {
searchBookmarkedBusinesses.value = listBookmarkedBusinesses;
} else {
List<BookmarkedBusiness> temp = [];
for (var item in listBookmarkedBusinesses) {
if (item.business_name
.toLowerCase()
.contains(businessSearchController.text.toLowerCase())) {
temp.add(item);
void _filterAndSetBusinesses() {
List<Business?> businessesToDisplay = [];
String query = businessSearchController.text.toLowerCase();
for (var bookmarked in listBookmarkedBusinesses) {
if (bookmarked.business_name.toLowerCase().contains(query)) {
if (_businessDetailsMap.containsKey(bookmarked.business_id)) {
businessesToDisplay.add(_businessDetailsMap[bookmarked.business_id]);
}
}
searchBookmarkedBusinesses.value = temp;
}
searchBookmarkedBusinesses.value = businessesToDisplay;
}
@override
void dispose() {
super.dispose();
businessSearchController.removeListener(searchBookmarkedBusinessByName);
businessSearchController.dispose();
searchFocusNode.dispose();
searchBookmarkedBusinesses.dispose();
@@ -61,8 +87,16 @@ class _MihFavouriteBusinessesState extends State<MihFavouriteBusinesses> {
@override
void initState() {
super.initState();
boookmarkedBusinessListFuture = getAllBookmarkedBusinessesForUser();
businessSearchController.addListener(searchBookmarkedBusinessByName);
businessDetailsMapFuture =
getAndMapAllBusinessDetailsForBookmarkedBusinesses();
businessSearchController.addListener(() {
if (_debounce?.isActive ?? false) {
_debounce!.cancel();
}
_debounce = Timer(const Duration(milliseconds: 200), () {
_filterAndSetBusinesses();
});
});
}
@override
@@ -93,8 +127,8 @@ class _MihFavouriteBusinessesState extends State<MihFavouriteBusinesses> {
),
),
const SizedBox(height: 10),
FutureBuilder(
future: boookmarkedBusinessListFuture,
FutureBuilder<Map<String, Business?>>(
future: businessDetailsMapFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Mihloadingcircle(
@@ -102,18 +136,82 @@ class _MihFavouriteBusinessesState extends State<MihFavouriteBusinesses> {
);
} else if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData && snapshot.data!.isNotEmpty) {
listBookmarkedBusinesses = snapshot.data!;
searchBookmarkedBusinessByName();
return ValueListenableBuilder(
valueListenable: searchBookmarkedBusinesses,
builder: (context, value, child) {
return BuildFavouriteBusinessesList(
favouriteBusinesses: value,
myLocation: widget.myLocation,
searchQuery: businessSearchController.text,
// No need to re-filter here, _filterAndSetBusinesses is called in initState
// and by the text controller listener.
return ValueListenableBuilder<List<Business?>>(
valueListenable:
searchBookmarkedBusinesses, // Listen to changes in this
builder: (context, businesses, child) {
// Display message if no results after search
if (businesses.isEmpty &&
businessSearchController.text.isNotEmpty) {
return Column(
children: [
const SizedBox(height: 50),
Icon(
Icons
.search_off_rounded, // A different icon for "no results"
size: 150,
color: MzansiInnovationHub.of(context)!
.theme
.secondaryColor(),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0),
child: SizedBox(
width: 500,
child: Text(
"No businesses found for '${businessSearchController.text}'", // Specific message for no search results
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
),
],
);
});
} else if (businesses.isEmpty) {
// Initial empty state
return Column(
children: [
const SizedBox(height: 50),
Icon(
Icons.business_center_rounded,
size: 150,
color: MzansiInnovationHub.of(context)!
.theme
.secondaryColor(),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10.0),
child: SizedBox(
width: 500,
child: Text(
"No favourites yet, use Mzansi Search to find and bookmark businesses you like",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
),
],
);
}
return BuildFavouriteBusinessesList(
favouriteBusinesses:
businesses, // Pass the filtered list from ValueNotifier
myLocation: widget.myLocation,
);
},
);
} else {
// This block handles the case where there are no bookmarked businesses initially
return Column(
children: [
const SizedBox(height: 50),
@@ -140,13 +238,16 @@ class _MihFavouriteBusinessesState extends State<MihFavouriteBusinesses> {
),
],
);
// return Center(
// child: Text("No bookmarked businesses found"),
// );
}
} else {
} else if (snapshot.hasError) {
return Center(
child: Text("Error loading bookmarked businesses"),
child: Text(
"Error loading bookmarked businesses: ${snapshot.error}"), // Show specific error
);
} else {
// Fallback for unexpected states
return Center(
child: Text("An unknown error occurred."),
);
}
}),