From 4a587ec2b6be0f6a8ac6863e41cbcffd3eb24406 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 16 Jul 2025 10:54:17 +0200 Subject: [PATCH 01/43] add new package for ratings --- Frontend/pubspec.lock | 8 ++++++++ Frontend/pubspec.yaml | 1 + 2 files changed, 9 insertions(+) diff --git a/Frontend/pubspec.lock b/Frontend/pubspec.lock index e08b9f6e..74f9c57b 100644 --- a/Frontend/pubspec.lock +++ b/Frontend/pubspec.lock @@ -265,6 +265,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + custom_rating_bar: + dependency: "direct main" + description: + name: custom_rating_bar + sha256: c2dafa488843b29f50ac2c2905b6a4cefd2cb603de31c6224adb047b6ed8593e + url: "https://pub.dev" + source: hosted + version: "3.0.0" dart_style: dependency: transitive description: diff --git a/Frontend/pubspec.yaml b/Frontend/pubspec.yaml index e07bea1f..ba69a30e 100644 --- a/Frontend/pubspec.yaml +++ b/Frontend/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: pwa_install: ^0.0.6 google_mobile_ads: ^6.0.0 redacted: ^1.0.13 + custom_rating_bar: ^3.0.0 dev_dependencies: flutter_test: From 19126d3996870e3618653f55a454b6172b079083 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 16 Jul 2025 10:54:30 +0200 Subject: [PATCH 02/43] test rating package --- .../Example/package_tools/package_tool_one.dart | 1 + .../Example/package_tools/package_tool_two.dart | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart b/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart index 2975b567..6c9b8d18 100644 --- a/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart +++ b/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart @@ -298,6 +298,7 @@ class _PackageToolOneState extends State { gpsLocation: "-26.1853611, 28.134664", website: "https://app.mzansi-innovation-hub.co.za/privacy.html", + rating: 3.25, ), const SizedBox(height: 10), Divider( diff --git a/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_two.dart b/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_two.dart index 1472fb95..f56ce322 100644 --- a/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_two.dart +++ b/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_two.dart @@ -1,3 +1,4 @@ +import 'package:custom_rating_bar/custom_rating_bar.dart'; import 'package:flutter/material.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; @@ -36,6 +37,14 @@ class _PackageToolTwoState extends State { ), ), const SizedBox(height: 10), + RatingBar( + filledIcon: Icons.star, + emptyIcon: Icons.star_border, + onRatingChanged: (value) => debugPrint('$value'), + initialRating: 3, + maxRating: 5, + ), + const SizedBox(height: 10), Container( color: Colors.black, width: 200, From 28f65e66961417e5e51551361993c0ea7356db0f Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 16 Jul 2025 10:55:02 +0200 Subject: [PATCH 03/43] add rating to business info card --- .../components/mih_business_info_card.dart | 35 +++++++++++++++++-- .../package_tools/mih_business_details.dart | 4 ++- .../mih_business_details_view.dart | 4 ++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart index e3d0c840..2272dbc2 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart @@ -1,3 +1,4 @@ +import 'package:custom_rating_bar/custom_rating_bar.dart'; import 'package:flutter/material.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_alert.dart'; @@ -9,12 +10,14 @@ class MihBusinessCard extends StatefulWidget { final String email; final String gpsLocation; final String? website; + final double rating; const MihBusinessCard({ super.key, required this.businessName, required this.cellNumber, required this.email, required this.gpsLocation, + required this.rating, this.website, }); @@ -336,6 +339,34 @@ class _MihBusinessCardState extends State { ), child: Column( children: [ + const SizedBox(height: 10), + RatingBar.readOnly( + size: 50, + alignment: Alignment.center, + filledIcon: Icons.star, + emptyIcon: Icons.star_border, + halfFilledIcon: Icons.star_half, + filledColor: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + emptyColor: MzansiInnovationHub.of(context)!.theme.primaryColor(), + halfFilledColor: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + isHalfAllowed: true, + initialRating: widget.rating, + maxRating: 5, + ), + // Text( + // "Rating: ${widget.rating}", + // style: TextStyle( + // fontSize: 15, + // fontWeight: FontWeight.bold, + // color: MzansiInnovationHub.of(context)!.theme.primaryColor(), + // height: 1.0, + // ), + // ), + // Divider( + // color: MzansiInnovationHub.of(context)!.theme.primaryColor(), + // ), const SizedBox(height: 10), _buildContactInfo( "Call", @@ -371,7 +402,7 @@ class _MihBusinessCardState extends State { "Location", "Come visit us.", Icons.location_on, - const Color(0xffe9e8a1), + const Color(0xffd69d7d), () { final latitude = double.parse(widget.gpsLocation.split(',')[0]); final longitude = @@ -410,7 +441,7 @@ class _MihBusinessCardState extends State { // "Rate Us", // "Let us know how we are doing.", // Icons.star_rate_rounded, - // const Color(0xffd69d7d), + // const Color(0xffe9e8a1), // () { // print("Opeining rating dialog"); // // _launchWebsite(widget.website); diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart index 82c23ea7..d9543c5b 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart @@ -684,7 +684,9 @@ class _MihBusinessDetailsState extends State { cellNumber: widget.arguments.business!.contact_no, email: widget.arguments.business!.bus_email, gpsLocation: widget.arguments.business!.gps_location, - //To-Do: Add the business Website + rating: widget.arguments.business!.rating.isNotEmpty + ? double.parse(widget.arguments.business!.rating) + : 0, website: widget.arguments.business!.website, ), ), diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart index b4d7271d..4a41c55e 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart @@ -166,7 +166,9 @@ class _MihBusinessDetailsViewState extends State { cellNumber: widget.business.contact_no, email: widget.business.bus_email, gpsLocation: widget.business.gps_location, - //To-Do: Add the business Website + rating: widget.business.rating.isNotEmpty + ? double.parse(widget.business.rating) + : 0, website: widget.business.website, ), ), From 50c04484998c817112cdc2f7a3584815fe43723e Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 16 Jul 2025 11:50:04 +0200 Subject: [PATCH 04/43] add Mzansi Directory Route to main --- backend/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/main.py b/backend/main.py index ec371cf4..f542a1f4 100644 --- a/backend/main.py +++ b/backend/main.py @@ -17,6 +17,7 @@ import routers.business as business import routers.access_request as access_request import routers.patient_access as patient_access import routers.mzansi_wallet as mzansi_wallet +import routers.mzansi_directory as mzansi_directory import routers.icd10_codes as icd10_codes from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware import Middleware @@ -92,6 +93,7 @@ app.include_router(business_user.router) app.include_router(business.router) app.include_router(notifications.router) app.include_router(mzansi_wallet.router) +app.include_router(mzansi_directory.router) app.include_router(icd10_codes.router) app.include_router(appointments.router) From 90a69d40ea730d8d9545270491fde6253deff327 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 16 Jul 2025 14:41:48 +0200 Subject: [PATCH 05/43] add python-dotenv to requirements --- backend/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/requirements.txt b/backend/requirements.txt index d0594fd5..26035ff9 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -6,5 +6,6 @@ reportlab requests watchfiles python-multipart +python-dotenv xlrd supertokens-python==0.29.2 \ No newline at end of file From 15bf13d5e987a1c759e7c3da6faaae57d9e03dbf Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 16 Jul 2025 14:42:20 +0200 Subject: [PATCH 06/43] exclude .env python file --- backend/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/.gitignore b/backend/.gitignore index 377f7991..f083cdbb 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,3 +1,4 @@ database/__pycache__/ +.env __pycache__/ temp*.pdf \ No newline at end of file From f131d13abeb612a0c0a04519f17a1c7e48271523 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 16 Jul 2025 14:44:35 +0200 Subject: [PATCH 07/43] remove DB file --- backend/.gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/.gitignore b/backend/.gitignore index f083cdbb..156d46ab 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,4 +1,3 @@ -database/__pycache__/ .env __pycache__/ temp*.pdf \ No newline at end of file From 2d120691c1612892c724c36de03a323b1f0432c2 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 16 Jul 2025 14:44:58 +0200 Subject: [PATCH 08/43] use env vars --- backend/database/dbConnection.py | 38 ++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/backend/database/dbConnection.py b/backend/database/dbConnection.py index fc16e946..04c8a118 100644 --- a/backend/database/dbConnection.py +++ b/backend/database/dbConnection.py @@ -1,48 +1,62 @@ import mysql.connector +import os +from dotenv import load_dotenv + +load_dotenv() +dbUser = os.getenv("DB_USER") +dbPass = os.getenv("DB_PASSWD") def dbPatientManagerConnect(): return mysql.connector.connect( host="mysqldb", - user="root", - passwd="C@rtoon1995", + user=dbUser, + passwd=dbPass, database="patient_manager" ) def dbAppDataConnect(): return mysql.connector.connect( host="mysqldb", - user="root", - passwd="C@rtoon1995", + user=dbUser, + passwd=dbPass, database="app_data" ) def dbDataAccessConnect(): return mysql.connector.connect( host="mysqldb", - user="root", - passwd="C@rtoon1995", + user=dbUser, + passwd=dbPass, database="data_access" ) def dbMzansiWalletConnect(): return mysql.connector.connect( host="mysqldb", - user="root", - passwd="C@rtoon1995", + user=dbUser, + passwd=dbPass, database="mzansi_wallet" ) +def dbMzansiDirectoryConnect(): + return mysql.connector.connect( + host="mysqldb", + user=dbUser, + passwd=dbPass, + database="mzansi_directory" + ) + def dbMzansiCalendarConnect(): return mysql.connector.connect( host="mysqldb", - user="root", - passwd="C@rtoon1995", + user=dbUser, + passwd=dbPass, database="mzansi_calendar" ) def dbAllConnect(): return mysql.connector.connect( host="mysqldb", - user="root", - passwd="C@rtoon1995", + user=dbUser, + passwd=dbPass, ) \ No newline at end of file From 55744d03f631a0e8c9407a280ce136fbfe0aed2b Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 16 Jul 2025 14:45:07 +0200 Subject: [PATCH 09/43] use env vars --- backend/Minio_Storage/minioConnection.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/backend/Minio_Storage/minioConnection.py b/backend/Minio_Storage/minioConnection.py index 0ec200a1..23e116aa 100644 --- a/backend/Minio_Storage/minioConnection.py +++ b/backend/Minio_Storage/minioConnection.py @@ -1,19 +1,25 @@ from minio import Minio +import os +from dotenv import load_dotenv + +load_dotenv() +minioAccess = os.getenv("MINIO_ACCESS_KEY") +minioSecret = os.getenv("MINIO_SECRET_KEY") def minioConnect(env): if(env == "Dev"): return Minio( "minio:9000", # "minio.mzansi-innovation-hub.co.za", - access_key="0RcgutfvcDq28lz7", - secret_key="nEED72ZlKYgDqH9Iy46fVGGT9TfabGWO", + access_key=minioAccess, + secret_key=minioSecret, secure=False ) else: return Minio( #"minio:9000", "minio.mzansi-innovation-hub.co.za", - access_key="0RcgutfvcDq28lz7", - secret_key="nEED72ZlKYgDqH9Iy46fVGGT9TfabGWO", + access_key=minioAccess, + secret_key=minioSecret, secure=True ) \ No newline at end of file From 46193a64a78032ef84d2e55d5f6b3ce6c4d32196 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 16 Jul 2025 14:45:20 +0200 Subject: [PATCH 10/43] add route to main --- backend/routers/mzansi_directory.py | 157 ++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 backend/routers/mzansi_directory.py diff --git a/backend/routers/mzansi_directory.py b/backend/routers/mzansi_directory.py new file mode 100644 index 00000000..c5969ff0 --- /dev/null +++ b/backend/routers/mzansi_directory.py @@ -0,0 +1,157 @@ +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel +from datetime import datetime +import database +from supertokens_python.recipe.session.framework.fastapi import verify_session +from supertokens_python.recipe.session import SessionContainer +from fastapi import Depends + +router = APIRouter() + +class BusinessRatingUserGet(BaseModel): + app_id: str + business_id: str + +class BusinessRatingInsertRequest(BaseModel): + app_id: str + business_id: str + rating_title: str + rating_description: str + rating_score: int + +class BusinessRatingDeleteRequest(BaseModel): + idbusiness_ratings: int + +class BusinessRatingUpdateRequest(BaseModel): + idbusiness_ratings: int + rating_title: str + rating_description: int + rating_score: str + +@router.get("/mzasni-directory/business-ratings/user/", tags=["Mzansi Directory"]) +async def read_all_ratings_by_business_id(itemRequest: BusinessRatingUserGet, session: SessionContainer = Depends(verify_session())): # , session: SessionContainer = Depends(verify_session()) + db = database.dbConnection.dbAllConnect() + cursor = db.cursor() + query = "" + query += "SELECT business_ratings.idbusiness_ratings, business_ratings.app_id, business_ratings.business_id, " + query += "business_ratings.rating_title, business_ratings.rating_description, business_ratings.rating_score, " + query += "business_ratings.date_time, users.username as 'reviewer' " + query += "FROM mzansi_directory.business_ratings " + query += "inner join app_data.users " + query += "on business_ratings.app_id = users.app_id " + query += "where business_ratings.business_id = %s and business_ratings.app_id = %s;" + cursor.execute(query, (itemRequest.business_id, + itemRequest.app_id,)) + items = [ + { + "idbusiness_ratings": item[0], + "app_id": item[1], + "business_id": item[2], + "rating_title": item[3], + "rating_description": item[4], + "rating_score": item[5], + "date_time": item[6], + "reviewer": item[7], + } + for item in cursor.fetchall() + ] + cursor.close() + db.close() + return items + +@router.get("/mzasni-directory/business-ratings/all/{business_id}", tags=["Mzansi Directory"]) +async def read_all_ratings_by_business_id(business_id: str, session: SessionContainer = Depends(verify_session())): # , session: SessionContainer = Depends(verify_session()) + db = database.dbConnection.dbAllConnect() + cursor = db.cursor() + query = "" + query += "SELECT business_ratings.idbusiness_ratings, business_ratings.app_id, business_ratings.business_id, " + query += "business_ratings.rating_title, business_ratings.rating_description, business_ratings.rating_score, " + query += "business_ratings.date_time, users.username as 'reviewer' " + query += "FROM mzansi_directory.business_ratings " + query += "inner join app_data.users " + query += "on business_ratings.app_id = users.app_id " + query += "where business_ratings.business_id = %s;" + cursor.execute(query, (business_id,)) + items = [ + { + "idbusiness_ratings": item[0], + "app_id": item[1], + "business_id": item[2], + "rating_title": item[3], + "rating_description": item[4], + "rating_score": item[5], + "date_time": item[6], + "reviewer": item[7], + } + for item in cursor.fetchall() + ] + cursor.close() + db.close() + return items + +@router.post("/mzasni-directory/business-rating/insert/", tags=["Mzansi Directory"], status_code=201) +async def insert_loyalty_card(itemRequest : BusinessRatingInsertRequest): #, session: SessionContainer = Depends(verify_session()) + db = database.dbConnection.dbMzansiDirectoryConnect() + nowDateTime = datetime.now() + formatedDateTime = nowDateTime.strftime("%Y-%m-%d %H:%M:%S") + cursor = db.cursor() + query = "insert into business_ratings " + query += "(app_id, business_id, rating_title, rating_description, rating_score, date_time) " + query += "values (%s, %s, %s, %s, %s, %s)" + notetData = (itemRequest.app_id, + itemRequest.business_id, + itemRequest.rating_title, + itemRequest.rating_description, + itemRequest.rating_score, + formatedDateTime, + ) + try: + cursor.execute(query, notetData) + except Exception as error: + print(error) + raise HTTPException(status_code=404, detail="Failed to Create Record") + # return {"message": error} + db.commit() + cursor.close() + db.close() + return {"message": "Successfully Created Record"} + +@router.delete("/mzasni-directory/business-ratng/delete/", tags=["Mzansi Directory"]) +async def Delete_loyalty_card(itemRequest : BusinessRatingDeleteRequest, session: SessionContainer = Depends(verify_session())): #, session: SessionContainer = Depends(verify_session()) + db = database.dbConnection.dbMzansiDirectoryConnect() + cursor = db.cursor() + query = "delete from business_ratings " + query += "where idbusiness_ratings=%s" + try: + cursor.execute(query, (str(itemRequest.idbusiness_ratings),)) + except Exception as error: + print(error) + raise HTTPException(status_code=404, detail="Failed to Delete Record") + db.commit() + cursor.close() + db.close() + return {"message": "Successfully deleted Record"} + +@router.put("/mzasni-directory/business-rating/update/", tags=["Mzansi Directory"]) +async def UpdatePatient(itemRequest : BusinessRatingUpdateRequest, session: SessionContainer = Depends(verify_session())): + db = database.dbConnection.dbMzansiDirectoryConnect() + cursor = db.cursor() + nowDateTime = datetime.now() + formatedDateTime = nowDateTime.strftime("%Y-%m-%d %H:%M:%S") + query = "update business_ratings " + query += "set rating_title=%s, rating_description=%s, rating_score=%s, date_time=%s " + query += "where idbusiness_ratings=%s" + notetData = (itemRequest.rating_title, + itemRequest.rating_description, + itemRequest.rating_score, + formatedDateTime, + itemRequest.idbusiness_ratings, + ) + try: + cursor.execute(query, notetData) + except Exception as error: + raise HTTPException(status_code=404, detail="Failed to Update Record") + db.commit() + cursor.close() + db.close() + return {"message": "Successfully Updated Record"} \ No newline at end of file From ad1ab3e2b207f466d629c5408f681366e4ea8832 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Thu, 17 Jul 2025 09:46:37 +0200 Subject: [PATCH 11/43] store --- .../package_tools/package_tool_one.dart | 19 +++-- .../components/mih_business_info_card.dart | 83 +++++++++++++++---- .../package_tools/mih_business_details.dart | 2 + .../mih_business_details_view.dart | 2 + 4 files changed, 81 insertions(+), 25 deletions(-) diff --git a/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart b/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart index 6c9b8d18..a7f5594c 100644 --- a/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart +++ b/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart @@ -291,15 +291,16 @@ class _PackageToolOneState extends State { } }), const SizedBox(height: 10), - MihBusinessCard( - businessName: "Mzansi Innovation Hub", - cellNumber: "0788300006", - email: "yasien.meth@mzansi-innovation-hub.co.za", - gpsLocation: "-26.1853611, 28.134664", - website: - "https://app.mzansi-innovation-hub.co.za/privacy.html", - rating: 3.25, - ), + // MihBusinessCard( + // businessid: "123456", + // businessName: "Mzansi Innovation Hub", + // cellNumber: "0788300006", + // email: "yasien.meth@mzansi-innovation-hub.co.za", + // gpsLocation: "-26.1853611, 28.134664", + // website: + // "https://app.mzansi-innovation-hub.co.za/privacy.html", + // rating: 3.25, + // ), const SizedBox(height: 10), Divider( color: diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart index 2272dbc2..4b7b30cd 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart @@ -1,24 +1,32 @@ import 'package:custom_rating_bar/custom_rating_bar.dart'; import 'package:flutter/material.dart'; import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_form.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_alert.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_window.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_text_form_field.dart'; import 'package:url_launcher/url_launcher.dart'; class MihBusinessCard extends StatefulWidget { + final String businessid; final String businessName; final String cellNumber; final String email; final String gpsLocation; final String? website; final double rating; + final double width; const MihBusinessCard({ super.key, + required this.businessid, required this.businessName, required this.cellNumber, required this.email, required this.gpsLocation, required this.rating, this.website, + required this.width, }); @override @@ -26,6 +34,9 @@ class MihBusinessCard extends StatefulWidget { } class _MihBusinessCardState extends State { + final _formKey = GlobalKey(); + final TextEditingController _reviewTitleController = TextEditingController(); + Future _makePhoneCall(String phoneNumber) async { final Uri url = Uri(scheme: 'tel', path: phoneNumber); if (await canLaunchUrl(url)) { @@ -324,6 +335,7 @@ class _MihBusinessCardState extends State { @override Widget build(BuildContext context) { + // double screenWidth = MediaQuery.of(context).size.width; return Material( color: MzansiInnovationHub.of(context)! .theme @@ -431,22 +443,21 @@ class _MihBusinessCardState extends State { }, ), ), - // Padding( - // padding: const EdgeInsets.symmetric(horizontal: 10.0), - // child: Divider( - // color: MzansiInnovationHub.of(context)!.theme.primaryColor(), - // ), - // ), - // _buildContactInfo( - // "Rate Us", - // "Let us know how we are doing.", - // Icons.star_rate_rounded, - // const Color(0xffe9e8a1), - // () { - // print("Opeining rating dialog"); - // // _launchWebsite(widget.website); - // }, - // ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: Divider( + color: MzansiInnovationHub.of(context)!.theme.primaryColor(), + ), + ), + _buildContactInfo( + "Rate Us", + "Let us know how we are doing.", + Icons.star_rate_rounded, + const Color(0xffe9e8a1), + () { + addBusinessReviewRatingWindow(widget.width); + }, + ), // Padding( // padding: const EdgeInsets.symmetric(horizontal: 10.0), // child: Divider( @@ -475,4 +486,44 @@ class _MihBusinessCardState extends State { ), ); } + + void addBusinessReviewRatingWindow(double width) { + showDialog( + context: context, + builder: (context) => MihPackageWindow( + fullscreen: false, + windowTitle: "Add Review", + onWindowTapClose: () { + Navigator.of(context).pop(); + }, + windowBody: MihSingleChildScroll( + child: Padding( + padding: + MzansiInnovationHub.of(context)!.theme.screenType == "desktop" + ? EdgeInsets.symmetric(horizontal: width * 0.05) + : EdgeInsets.symmetric(horizontal: width * 0), + child: MihForm( + formKey: _formKey, + formFields: [ + MihTextFormField( + width: 200, + fillColor: + MzansiInnovationHub.of(context)!.theme.secondaryColor(), + inputColor: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + controller: _reviewTitleController, + multiLineInput: false, + requiredText: false, + hintText: "Review Title", + validator: (value) { + return null; + }, + ), + ], + ), + ), + ), + ), + ); + } } diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart index d9543c5b..fa9fb039 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart @@ -680,6 +680,7 @@ class _MihBusinessDetailsState extends State { SizedBox( width: 700, child: MihBusinessCard( + businessid: widget.arguments.business!.business_id, businessName: widget.arguments.business!.Name, cellNumber: widget.arguments.business!.contact_no, email: widget.arguments.business!.bus_email, @@ -688,6 +689,7 @@ class _MihBusinessDetailsState extends State { ? double.parse(widget.arguments.business!.rating) : 0, website: widget.arguments.business!.website, + width: width, ), ), const SizedBox(height: 30.0), diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart index 4a41c55e..89ce09fd 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart @@ -162,6 +162,7 @@ class _MihBusinessDetailsViewState extends State { SizedBox( width: 700, child: MihBusinessCard( + businessid: widget.business.business_id, businessName: widget.business.Name, cellNumber: widget.business.contact_no, email: widget.business.bus_email, @@ -170,6 +171,7 @@ class _MihBusinessDetailsViewState extends State { ? double.parse(widget.business.rating) : 0, website: widget.business.website, + width: width, ), ), ], From 1aa077538a32ad477a195db9b2def09b5bf8bb94 Mon Sep 17 00:00:00 2001 From: yaso Date: Thu, 17 Jul 2025 12:06:12 +0200 Subject: [PATCH 12/43] Remove Sandbox Profiles in prod --- .../package_tools/mih_sign_in.dart | 81 ++++++++++--------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/Frontend/lib/mih_packages/mih_authentication/package_tools/mih_sign_in.dart b/Frontend/lib/mih_packages/mih_authentication/package_tools/mih_sign_in.dart index 7f75000a..7f7f9e56 100644 --- a/Frontend/lib/mih_packages/mih_authentication/package_tools/mih_sign_in.dart +++ b/Frontend/lib/mih_packages/mih_authentication/package_tools/mih_sign_in.dart @@ -394,48 +394,51 @@ class _MihSignInState extends State { //spacer const SizedBox(height: 35), - Center( - child: SizedBox( - width: width, - //height: 100.0, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Flexible( - flex: 1, - child: Padding( - padding: EdgeInsets.only(right: 10.0), - child: Divider(), - ), - ), - Flexible( - flex: 1, - child: GestureDetector( - child: Text( - 'Use Sandox Profile', - textAlign: TextAlign.center, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 15, - color: MzansiInnovationHub.of(context)! - .theme - .secondaryColor()), + Visibility( + visible: AppEnviroment.getEnv() == "Dev", + child: Center( + child: SizedBox( + width: width, + //height: 100.0, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Flexible( + flex: 1, + child: Padding( + padding: EdgeInsets.only(right: 10.0), + child: Divider(), ), - onTap: () { - setState(() { - showProfiles = !showProfiles; - }); - }, ), - ), - const Flexible( - flex: 1, - child: Padding( - padding: EdgeInsets.only(left: 10.0), - child: Divider(), + Flexible( + flex: 1, + child: GestureDetector( + child: Text( + 'Use Sandox Profile', + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 15, + color: MzansiInnovationHub.of(context)! + .theme + .secondaryColor()), + ), + onTap: () { + setState(() { + showProfiles = !showProfiles; + }); + }, + ), ), - ), - ], + const Flexible( + flex: 1, + child: Padding( + padding: EdgeInsets.only(left: 10.0), + child: Divider(), + ), + ), + ], + ), ), ), ), From 841b3e9e5e24acbc771a0c015e5e68449c64c11c Mon Sep 17 00:00:00 2001 From: yaso Date: Thu, 17 Jul 2025 14:21:58 +0200 Subject: [PATCH 13/43] remove unused import --- .../Example/package_tools/package_tool_one.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart b/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart index a7f5594c..6195fabd 100644 --- a/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart +++ b/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart @@ -9,7 +9,7 @@ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_business_profile_preview.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_personal_profile_preview.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; -import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart'; +// import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_location_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart'; From 621eec601aa02c44d3c079421e4c961c93891ffb Mon Sep 17 00:00:00 2001 From: yaso Date: Thu, 17 Jul 2025 14:22:16 +0200 Subject: [PATCH 14/43] create add review window --- .../components/mih_business_info_card.dart | 188 ++++++++++++++++-- 1 file changed, 172 insertions(+), 16 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart index 4b7b30cd..acd0401b 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart @@ -1,11 +1,14 @@ import 'package:custom_rating_bar/custom_rating_bar.dart'; import 'package:flutter/material.dart'; import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_form.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_alert.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_window.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_text_form_field.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart'; import 'package:url_launcher/url_launcher.dart'; class MihBusinessCard extends StatefulWidget { @@ -36,6 +39,10 @@ class MihBusinessCard extends StatefulWidget { class _MihBusinessCardState extends State { final _formKey = GlobalKey(); final TextEditingController _reviewTitleController = TextEditingController(); + final TextEditingController _reviewDescriptionController = + TextEditingController(); + late final VoidCallback _reviewDescriptionListener; + final ValueNotifier _counter = ValueNotifier(0); Future _makePhoneCall(String phoneNumber) async { final Uri url = Uri(scheme: 'tel', path: phoneNumber); @@ -333,6 +340,23 @@ class _MihBusinessCardState extends State { ); } + @override + void dispose() { + super.dispose(); + _reviewDescriptionController.removeListener(_reviewDescriptionListener); + } + + @override + void initState() { + super.initState(); + _reviewDescriptionListener = () { + setState(() { + _counter.value = _reviewDescriptionController.text.characters.length; + }); + }; + _reviewDescriptionController.addListener(_reviewDescriptionListener); + } + @override Widget build(BuildContext context) { // double screenWidth = MediaQuery.of(context).size.width; @@ -487,6 +511,14 @@ class _MihBusinessCardState extends State { ); } + Color getMissionVisionLimitColor(int limit) { + if (_counter.value <= limit) { + return MzansiInnovationHub.of(context)!.theme.secondaryColor(); + } else { + return MzansiInnovationHub.of(context)!.theme.errorColor(); + } + } + void addBusinessReviewRatingWindow(double width) { showDialog( context: context, @@ -502,22 +534,146 @@ class _MihBusinessCardState extends State { MzansiInnovationHub.of(context)!.theme.screenType == "desktop" ? EdgeInsets.symmetric(horizontal: width * 0.05) : EdgeInsets.symmetric(horizontal: width * 0), - child: MihForm( - formKey: _formKey, - formFields: [ - MihTextFormField( - width: 200, - fillColor: - MzansiInnovationHub.of(context)!.theme.secondaryColor(), - inputColor: - MzansiInnovationHub.of(context)!.theme.primaryColor(), - controller: _reviewTitleController, - multiLineInput: false, - requiredText: false, - hintText: "Review Title", - validator: (value) { - return null; - }, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + MihForm( + formKey: _formKey, + formFields: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Business Rating", + textAlign: TextAlign.left, + style: TextStyle( + color: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 4), + RatingBar( + size: 50, + alignment: Alignment.centerLeft, + filledIcon: Icons.star, + emptyIcon: Icons.star_border, + halfFilledIcon: Icons.star_half, + filledColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + emptyColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + halfFilledColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + isHalfAllowed: true, + initialRating: 1, + maxRating: 5, + onRatingChanged: (double) {}, + ), + const SizedBox(height: 10), + MihTextFormField( + // width: 200, + fillColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + controller: _reviewTitleController, + multiLineInput: false, + requiredText: true, + hintText: "Review Title", + validator: (value) { + return MihValidationServices() + .isEmpty(_reviewTitleController.text); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + height: 250, + fillColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + controller: _reviewDescriptionController, + multiLineInput: true, + requiredText: false, + hintText: "Review Description", + validator: (value) { + if (_reviewDescriptionController.text.isEmpty) { + return null; + } else { + return MihValidationServices().validateLength( + _reviewDescriptionController.text, 256); + } + }, + ), + SizedBox( + height: 15, + child: ValueListenableBuilder( + valueListenable: _counter, + builder: + (BuildContext context, int value, Widget? child) { + return Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "$value", + style: TextStyle( + color: getMissionVisionLimitColor(256), + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 5), + Text( + "/256", + style: TextStyle( + color: getMissionVisionLimitColor(256), + fontWeight: FontWeight.bold, + ), + ), + ], + ); + }, + ), + ), + const SizedBox(height: 25), + Center( + child: MihButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + // submitForm(); + } else { + MihAlertServices().formNotFilledCompletely(context); + } + }, + buttonColor: MzansiInnovationHub.of(context)! + .theme + .successColor(), + width: 300, + child: Text( + "Add Review", + style: TextStyle( + color: MzansiInnovationHub.of(context)! + .theme + .primaryColor(), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], ), ], ), From 0e4230c4a4326ba0de6d10aaa0e6162f7440c45a Mon Sep 17 00:00:00 2001 From: yaso Date: Thu, 17 Jul 2025 14:49:27 +0200 Subject: [PATCH 15/43] update search criteria --- backend/routers/business.py | 5 +++-- backend/routers/users.py | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/routers/business.py b/backend/routers/business.py index 232c523d..7ea28092 100644 --- a/backend/routers/business.py +++ b/backend/routers/business.py @@ -72,9 +72,10 @@ async def read_all_businesses(search: str, session: SessionContainer = Depends(v query += "practice_no, vat_no, " query += "website, rating, mission_vision " query += "FROM business " - query += "WHERE LOWER(business.Name) LIKE %s OR LOWER(business.type) LIKE %s" + query += "WHERE LOWER(business.Name) LIKE %s OR LOWER(business.type) LIKE %s " + query += "OR LOWER(business.bus_email) LIKE %s OR LOWER(business.mission_vision) LIKE %s" search_term = f"%{search.lower()}%" # Add wildcards and lowercase - cursor.execute(query, (search_term, search_term)) + cursor.execute(query, (search_term, search_term, search_term, search_term)) items = [ { "business_id": item[0], diff --git a/backend/routers/users.py b/backend/routers/users.py index b4938efb..9d9a44c0 100644 --- a/backend/routers/users.py +++ b/backend/routers/users.py @@ -74,10 +74,12 @@ async def read_all_users(search: str, session: SessionContainer = Depends(verify cursor = db.cursor() query = "" query += "SELECT * FROM users " - query += "WHERE (LOWER(email) LIKE %s OR LOWER(username) LIKE %s) " + query += "WHERE (LOWER(email) LIKE %s OR LOWER(username) LIKE %s " + query += "OR LOWER(fname) LIKE %s OR LOWER(lname) LIKE %s " + query += "OR LOWER(purpose) LIKE %s) " query += "AND username != ''" search_term = f"%{search.lower()}%" # Add wildcards and lowercase - cursor.execute(query, (search_term, search_term)) + cursor.execute(query, (search_term, search_term,search_term, search_term, search_term)) items = [ { "idUser": item[0], From a642201bb3b4fa14f92a1ed51e5738ab8c6e99b7 Mon Sep 17 00:00:00 2001 From: yaso Date: Thu, 17 Jul 2025 15:40:26 +0200 Subject: [PATCH 16/43] Change search Switch Icon --- .../mzansi_directory/package_tools/mih_search_mzansi.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b36bbadf..846c9b43 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 @@ -82,7 +82,7 @@ class _MihSearchMzansiState extends State { }); }, icon: Icon( - Icons.display_settings, + Icons.swap_horiz_rounded, size: 35, color: MzansiInnovationHub.of(context)!.theme.primaryColor(), From f23e8dccb3cc76866a51a69136240cb76ccedb8b Mon Sep 17 00:00:00 2001 From: yaso Date: Fri, 18 Jul 2025 10:13:39 +0200 Subject: [PATCH 17/43] add medu to edit review --- .../components/mih_business_info_card.dart | 105 +++++++++++++++++- 1 file changed, 102 insertions(+), 3 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart index acd0401b..f1072b23 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart @@ -1,5 +1,6 @@ import 'package:custom_rating_bar/custom_rating_bar.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_form.dart'; @@ -479,7 +480,7 @@ class _MihBusinessCardState extends State { Icons.star_rate_rounded, const Color(0xffe9e8a1), () { - addBusinessReviewRatingWindow(widget.width); + businessReviewRatingWindow(true, widget.width); }, ), // Padding( @@ -519,15 +520,40 @@ class _MihBusinessCardState extends State { } } - void addBusinessReviewRatingWindow(double width) { + void businessReviewRatingWindow(bool previouslyRated, double width) { showDialog( context: context, builder: (context) => MihPackageWindow( fullscreen: false, - windowTitle: "Add Review", + windowTitle: previouslyRated ? "Edit Review" : "Add Review", onWindowTapClose: () { Navigator.of(context).pop(); }, + menuOptions: previouslyRated + ? [ + SpeedDialChild( + child: Icon( + Icons.delete, + color: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + ), + label: "Delete Review", + labelBackgroundColor: + MzansiInnovationHub.of(context)!.theme.successColor(), + labelStyle: TextStyle( + color: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + fontWeight: FontWeight.bold, + ), + backgroundColor: + MzansiInnovationHub.of(context)!.theme.successColor(), + onTap: () { + // showTestWindow(); + showDeleteReviewAlert(); + }, + ), + ] + : null, windowBody: MihSingleChildScroll( child: Padding( padding: @@ -682,4 +708,77 @@ class _MihBusinessCardState extends State { ), ); } + + void showDeleteReviewAlert() { + showDialog( + context: context, + builder: (context) => MihPackageAlert( + alertColour: MzansiInnovationHub.of(context)!.theme.errorColor(), + alertIcon: Icon( + Icons.warning_rounded, + size: 100, + color: MzansiInnovationHub.of(context)!.theme.errorColor(), + ), + alertTitle: "Delete Review", + alertBody: Column( + children: [ + Text( + "Are you sure you want to delete this review? This action cannot be undone.", + style: TextStyle( + color: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + fontSize: 15, + ), + ), + const SizedBox(height: 25), + Wrap( + runAlignment: WrapAlignment.center, + spacing: 10, + runSpacing: 10, + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + MihButton( + width: 300, + onPressed: () { + Navigator.of(context).pop(); + }, + buttonColor: MzansiInnovationHub.of(context)! + .theme + .successColor(), + child: Text( + "Cancel", + style: TextStyle( + color: MzansiInnovationHub.of(context)! + .theme + .primaryColor(), + fontSize: 18, + ), + ), + ), + ], + ), + MihButton( + width: 300, + onPressed: () { + // Delete review logic here + Navigator.of(context).pop(); + }, + buttonColor: + MzansiInnovationHub.of(context)!.theme.errorColor(), + child: Text( + "Delete", + style: TextStyle( + color: MzansiInnovationHub.of(context)! + .theme + .primaryColor(), + fontSize: 18, + ), + ), + ), + ], + ), + )); + } } From 52bd3c34ae4972f9436294f3f73a605ceae989cb Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Fri, 18 Jul 2025 15:02:54 +0200 Subject: [PATCH 18/43] update get user review api --- backend/routers/mzansi_directory.py | 47 ++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/backend/routers/mzansi_directory.py b/backend/routers/mzansi_directory.py index c5969ff0..bc4366cf 100644 --- a/backend/routers/mzansi_directory.py +++ b/backend/routers/mzansi_directory.py @@ -8,9 +8,9 @@ from fastapi import Depends router = APIRouter() -class BusinessRatingUserGet(BaseModel): - app_id: str - business_id: str +# class BusinessRatingUserGet(BaseModel): +# app_id: str +# business_id: str class BusinessRatingInsertRequest(BaseModel): app_id: str @@ -28,8 +28,8 @@ class BusinessRatingUpdateRequest(BaseModel): rating_description: int rating_score: str -@router.get("/mzasni-directory/business-ratings/user/", tags=["Mzansi Directory"]) -async def read_all_ratings_by_business_id(itemRequest: BusinessRatingUserGet, session: SessionContainer = Depends(verify_session())): # , session: SessionContainer = Depends(verify_session()) +@router.get("/mzasni-directory/business-ratings/user/{app_id}/{business_id}", tags=["Mzansi Directory"]) +async def read_all_ratings_by_business_id(app_id: str,business_id: str, session: SessionContainer = Depends(verify_session())): # , session: SessionContainer = Depends(verify_session()) db = database.dbConnection.dbAllConnect() cursor = db.cursor() query = "" @@ -40,10 +40,15 @@ async def read_all_ratings_by_business_id(itemRequest: BusinessRatingUserGet, se query += "inner join app_data.users " query += "on business_ratings.app_id = users.app_id " query += "where business_ratings.business_id = %s and business_ratings.app_id = %s;" - cursor.execute(query, (itemRequest.business_id, - itemRequest.app_id,)) - items = [ - { + cursor.execute(query, (business_id, + app_id,)) + item = cursor.fetchone() # Get only one row + cursor.close() + db.close() + + if item: + # Return a single dictionary + return { "idbusiness_ratings": item[0], "app_id": item[1], "business_id": item[2], @@ -53,11 +58,25 @@ async def read_all_ratings_by_business_id(itemRequest: BusinessRatingUserGet, se "date_time": item[6], "reviewer": item[7], } - for item in cursor.fetchall() - ] - cursor.close() - db.close() - return items + else: + # Return an empty response or a specific message + return None + # items = [ + # { + # "idbusiness_ratings": item[0], + # "app_id": item[1], + # "business_id": item[2], + # "rating_title": item[3], + # "rating_description": item[4], + # "rating_score": item[5], + # "date_time": item[6], + # "reviewer": item[7], + # } + # for item in cursor.fetchall() + # ] + # cursor.close() + # db.close() + # return items[0] @router.get("/mzasni-directory/business-ratings/all/{business_id}", tags=["Mzansi Directory"]) async def read_all_ratings_by_business_id(business_id: str, session: SessionContainer = Depends(verify_session())): # , session: SessionContainer = Depends(verify_session()) From da74f2fda40417671bdc3cf74144b7a1cce4a030 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Fri, 18 Jul 2025 15:03:06 +0200 Subject: [PATCH 19/43] update business review object --- .../mih_objects/business_review.dart | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Frontend/lib/mih_components/mih_objects/business_review.dart diff --git a/Frontend/lib/mih_components/mih_objects/business_review.dart b/Frontend/lib/mih_components/mih_objects/business_review.dart new file mode 100644 index 00000000..5aed0bd9 --- /dev/null +++ b/Frontend/lib/mih_components/mih_objects/business_review.dart @@ -0,0 +1,46 @@ +class BusinessReview { + final int idbusiness_ratings; + final String app_id; + final String business_id; + final String rating_title; + final String rating_description; + final String rating_score; + final String date_time; + final String reviewer; + + BusinessReview({ + required this.idbusiness_ratings, + required this.app_id, + required this.business_id, + required this.rating_title, + required this.rating_description, + required this.rating_score, + required this.date_time, + required this.reviewer, + }); + factory BusinessReview.fromJson(Map json) { + return switch (json) { + { + "idbusiness_ratings": int idbusiness_ratings, + "app_id": String app_id, + "business_id": String business_id, + "rating_title": String rating_title, + "rating_description": String rating_description, + "rating_score": String rating_score, + "date_time": String date_time, + "reviewer": String reviewer, + } => + BusinessReview( + idbusiness_ratings: idbusiness_ratings, + app_id: app_id, + business_id: business_id, + rating_title: rating_title, + rating_description: rating_description, + rating_score: rating_score, + date_time: date_time, + reviewer: reviewer, + ), + _ => throw const FormatException('Failed to load loyalty card objects'), + }; + } +} From 838f5b1b4ea44548fc065a6d2bfd05b71b25a1ae Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Fri, 18 Jul 2025 15:04:10 +0200 Subject: [PATCH 20/43] Add Mzansi Directory service --- .../mih_mzansi_directory_services.dart | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 Frontend/lib/mih_services/mih_mzansi_directory_services.dart diff --git a/Frontend/lib/mih_services/mih_mzansi_directory_services.dart b/Frontend/lib/mih_services/mih_mzansi_directory_services.dart new file mode 100644 index 00000000..5a17b43c --- /dev/null +++ b/Frontend/lib/mih_services/mih_mzansi_directory_services.dart @@ -0,0 +1,114 @@ +import 'dart:convert'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/business_review.dart'; +import 'package:mzansi_innovation_hub/mih_config/mih_env.dart'; +import 'package:supertokens_flutter/http.dart' as http; + +class MihMzansiDirectoryServices { + final baseAPI = AppEnviroment.baseApiUrl; + + Future getUserReviewOfBusiness( + String app_id, + String business_id, + ) async { + final response = await http.get(Uri.parse( + "${AppEnviroment.baseApiUrl}/mzasni-directory/business-ratings/user/$app_id/$business_id")); + print(response.statusCode); + if (response.statusCode == 200) { + String body = response.body; + var jsonBody = jsonDecode(body); + BusinessReview? busRev = BusinessReview.fromJson(jsonBody); + return busRev; + } else { + return null; + } + } + + static Future> getAllReviewsofBusiness( + String business_id, + String businessid, + ) async { + final response = await http.get(Uri.parse( + "${AppEnviroment.baseApiUrl}/mzasni-directory/business-ratings/all/$business_id")); + if (response.statusCode == 200) { + Iterable l = jsonDecode(response.body); + List businessReviews = List.from( + l.map((model) => BusinessReview.fromJson(model))); + return businessReviews; + } else { + throw Exception('failed to fetch Business Reviews'); + } + } + + Future addLoyaltyCardAPICall( + String app_id, + String business_id, + String rating_title, + String rating_description, + int rating_score, + ) async { + var response = await http.post( + Uri.parse( + "${AppEnviroment.baseApiUrl}/mzasni-directory/business-rating/insert/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + body: jsonEncode({ + "app_id": app_id, + "business_id": business_id, + "rating_title": rating_title, + "rating_description": rating_description, + "rating_score": rating_score, + }), + ); + if (response.statusCode == 201) { + return response.statusCode; + } else { + return response.statusCode; + } + } + + Future deleteLoyaltyCardAPICall( + int idbusiness_ratings, + ) async { + var response = await http.delete( + Uri.parse( + "${AppEnviroment.baseApiUrl}/mzasni-directory/business-ratng/delete/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + body: jsonEncode( + {"idbusiness_ratings": idbusiness_ratings}), + ); + if (response.statusCode == 200) { + return response.statusCode; + } else { + return response.statusCode; + } + } + + static Future updateLoyaltyCardAPICall( + int idbusiness_ratings, + String rating_title, + String rating_description, + String rating_score, + ) async { + var response = await http.put( + Uri.parse( + "${AppEnviroment.baseApiUrl}/mzasni-directory/business-rating/update/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + body: jsonEncode({ + "idbusiness_ratings": idbusiness_ratings, + "rating_title": rating_title, + "rating_description": rating_description, + "rating_score": rating_score, + }), + ); + if (response.statusCode == 200) { + return response.statusCode; + } else { + return response.statusCode; + } + } +} From 5d84c26c3da37239e901b97d17e96ad65c4f7eb9 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Fri, 18 Jul 2025 15:04:26 +0200 Subject: [PATCH 21/43] pull rate on revierw --- .../components/mih_business_info_card.dart | 466 ++++++++++-------- 1 file changed, 270 insertions(+), 196 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart index f1072b23..e2edf8a3 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart @@ -2,14 +2,18 @@ import 'package:custom_rating_bar/custom_rating_bar.dart'; import 'package:flutter/material.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/business_review.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_form.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_alert.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_window.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_text_form_field.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_mzansi_directory_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart'; +import 'package:supertokens_flutter/supertokens.dart'; import 'package:url_launcher/url_launcher.dart'; class MihBusinessCard extends StatefulWidget { @@ -40,6 +44,7 @@ class MihBusinessCard extends StatefulWidget { class _MihBusinessCardState extends State { final _formKey = GlobalKey(); final TextEditingController _reviewTitleController = TextEditingController(); + final TextEditingController _reviewScoreController = TextEditingController(); final TextEditingController _reviewDescriptionController = TextEditingController(); late final VoidCallback _reviewDescriptionListener; @@ -341,6 +346,14 @@ class _MihBusinessCardState extends State { ); } + Future getUserReview() async { + String user_id = await SuperTokens.getUserId(); + return await MihMzansiDirectoryServices().getUserReviewOfBusiness( + user_id, + widget.businessid, + ); + } + @override void dispose() { super.dispose(); @@ -520,192 +533,255 @@ class _MihBusinessCardState extends State { } } - void businessReviewRatingWindow(bool previouslyRated, double width) { + Future businessReviewRatingWindow( + bool previouslyRated, double width) async { showDialog( context: context, - builder: (context) => MihPackageWindow( - fullscreen: false, - windowTitle: previouslyRated ? "Edit Review" : "Add Review", - onWindowTapClose: () { - Navigator.of(context).pop(); - }, - menuOptions: previouslyRated - ? [ - SpeedDialChild( - child: Icon( - Icons.delete, - color: - MzansiInnovationHub.of(context)!.theme.primaryColor(), - ), - label: "Delete Review", - labelBackgroundColor: - MzansiInnovationHub.of(context)!.theme.successColor(), - labelStyle: TextStyle( - color: - MzansiInnovationHub.of(context)!.theme.primaryColor(), - fontWeight: FontWeight.bold, - ), - backgroundColor: - MzansiInnovationHub.of(context)!.theme.successColor(), - onTap: () { - // showTestWindow(); - showDeleteReviewAlert(); - }, - ), - ] - : null, - windowBody: MihSingleChildScroll( - child: Padding( - padding: - MzansiInnovationHub.of(context)!.theme.screenType == "desktop" - ? EdgeInsets.symmetric(horizontal: width * 0.05) - : EdgeInsets.symmetric(horizontal: width * 0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - MihForm( - formKey: _formKey, - formFields: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Business Rating", - textAlign: TextAlign.left, - style: TextStyle( - color: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - const SizedBox(height: 4), - RatingBar( - size: 50, - alignment: Alignment.centerLeft, - filledIcon: Icons.star, - emptyIcon: Icons.star_border, - halfFilledIcon: Icons.star_half, - filledColor: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - emptyColor: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - halfFilledColor: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - isHalfAllowed: true, - initialRating: 1, - maxRating: 5, - onRatingChanged: (double) {}, - ), - const SizedBox(height: 10), - MihTextFormField( - // width: 200, - fillColor: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - inputColor: - MzansiInnovationHub.of(context)!.theme.primaryColor(), - controller: _reviewTitleController, - multiLineInput: false, - requiredText: true, - hintText: "Review Title", - validator: (value) { - return MihValidationServices() - .isEmpty(_reviewTitleController.text); - }, - ), - const SizedBox(height: 10), - MihTextFormField( - height: 250, - fillColor: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - inputColor: - MzansiInnovationHub.of(context)!.theme.primaryColor(), - controller: _reviewDescriptionController, - multiLineInput: true, - requiredText: false, - hintText: "Review Description", - validator: (value) { - if (_reviewDescriptionController.text.isEmpty) { - return null; - } else { - return MihValidationServices().validateLength( - _reviewDescriptionController.text, 256); - } - }, - ), - SizedBox( - height: 15, - child: ValueListenableBuilder( - valueListenable: _counter, - builder: - (BuildContext context, int value, Widget? child) { - return Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text( - "$value", - style: TextStyle( - color: getMissionVisionLimitColor(256), - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(width: 5), - Text( - "/256", - style: TextStyle( - color: getMissionVisionLimitColor(256), - fontWeight: FontWeight.bold, - ), - ), - ], - ); - }, - ), - ), - const SizedBox(height: 25), - Center( - child: MihButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - // submitForm(); - } else { - MihAlertServices().formNotFilledCompletely(context); - } - }, - buttonColor: MzansiInnovationHub.of(context)! - .theme - .successColor(), - width: 300, - child: Text( - "Add Review", - style: TextStyle( + builder: (context) => FutureBuilder( + future: getUserReview(), + builder: (context, asyncSnapshot) { + if (asyncSnapshot.connectionState == ConnectionState.waiting) { + return const Mihloadingcircle( + message: "Checking for previous reviews...", + ); + } else if (asyncSnapshot.connectionState == ConnectionState.done && + asyncSnapshot.hasData) { + _reviewTitleController.text = + asyncSnapshot.requireData!.rating_title; + _reviewDescriptionController.text = + asyncSnapshot.requireData!.rating_description; + _reviewScoreController.text = + asyncSnapshot.requireData!.rating_score; + return MihPackageWindow( + fullscreen: false, + windowTitle: + asyncSnapshot.hasData ? "Edit Review" : "Add Review", + onWindowTapClose: () { + Navigator.of(context).pop(); + }, + menuOptions: asyncSnapshot.hasData + ? [ + SpeedDialChild( + child: Icon( + Icons.delete, + color: MzansiInnovationHub.of(context)! + .theme + .primaryColor(), + ), + label: "Delete Review", + labelBackgroundColor: MzansiInnovationHub.of(context)! + .theme + .successColor(), + labelStyle: TextStyle( color: MzansiInnovationHub.of(context)! .theme .primaryColor(), - fontSize: 20, fontWeight: FontWeight.bold, ), + backgroundColor: MzansiInnovationHub.of(context)! + .theme + .successColor(), + onTap: () { + // showTestWindow(); + showDeleteReviewAlert(); + }, ), + ] + : null, + windowBody: MihSingleChildScroll( + child: Padding( + padding: + MzansiInnovationHub.of(context)!.theme.screenType == + "desktop" + ? EdgeInsets.symmetric(horizontal: width * 0.05) + : EdgeInsets.symmetric(horizontal: width * 0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + MihForm( + formKey: _formKey, + formFields: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Business Rating", + textAlign: TextAlign.left, + style: TextStyle( + color: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 4), + RatingBar( + size: 50, + alignment: Alignment.centerLeft, + filledIcon: Icons.star, + emptyIcon: Icons.star_border, + halfFilledIcon: Icons.star_half, + filledColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + emptyColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + halfFilledColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + isHalfAllowed: true, + initialRating: asyncSnapshot.hasData + ? double.parse(_reviewScoreController.text) + : 1, + maxRating: 5, + onRatingChanged: (double) { + setState(() { + _reviewScoreController.text = + double.toStringAsFixed(1); + }); + print(_reviewScoreController.text); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + // width: 200, + fillColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzansiInnovationHub.of(context)! + .theme + .primaryColor(), + controller: _reviewTitleController, + multiLineInput: false, + requiredText: true, + hintText: "Review Title", + validator: (value) { + return MihValidationServices() + .isEmpty(_reviewTitleController.text); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + height: 250, + fillColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzansiInnovationHub.of(context)! + .theme + .primaryColor(), + controller: _reviewDescriptionController, + multiLineInput: true, + requiredText: false, + hintText: "Review Description", + validator: (value) { + if (_reviewDescriptionController.text.isEmpty) { + return null; + } else { + return MihValidationServices().validateLength( + _reviewDescriptionController.text, 256); + } + }, + ), + SizedBox( + height: 15, + child: ValueListenableBuilder( + valueListenable: _counter, + builder: (BuildContext context, int value, + Widget? child) { + return Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "$value", + style: TextStyle( + color: + getMissionVisionLimitColor(256), + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 5), + Text( + "/256", + style: TextStyle( + color: + getMissionVisionLimitColor(256), + fontWeight: FontWeight.bold, + ), + ), + ], + ); + }, + ), + ), + const SizedBox(height: 25), + Center( + child: MihButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + // submitForm(); + } else { + MihAlertServices() + .formNotFilledCompletely(context); + } + }, + buttonColor: MzansiInnovationHub.of(context)! + .theme + .successColor(), + width: 300, + child: Text( + asyncSnapshot.hasData + ? "Edit Review" + : "Add Review", + style: TextStyle( + color: MzansiInnovationHub.of(context)! + .theme + .primaryColor(), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ); + } else { + return MihPackageAlert( + alertColour: + MzansiInnovationHub.of(context)!.theme.errorColor(), + alertIcon: Icon( + Icons.warning_rounded, + size: 100, + color: MzansiInnovationHub.of(context)!.theme.errorColor(), + ), + alertTitle: "Error Pulling Data", + alertBody: Column( + children: [ + Text( + "Please ensure you are connectede top the internet and you are running the latest version of MIH then try again.", + style: TextStyle( + color: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + fontSize: 15, ), ), ], ), - ], - ), - ), - ), - ), + ); + } + }), ); } @@ -733,12 +809,27 @@ class _MihBusinessCardState extends State { ), const SizedBox(height: 25), Wrap( - runAlignment: WrapAlignment.center, spacing: 10, runSpacing: 10, - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, children: [ + MihButton( + width: 300, + onPressed: () { + Navigator.of(context).pop(); + }, + buttonColor: + MzansiInnovationHub.of(context)!.theme.errorColor(), + child: Text( + "Delete", + style: TextStyle( + color: MzansiInnovationHub.of(context)! + .theme + .primaryColor(), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), MihButton( width: 300, onPressed: () { @@ -753,30 +844,13 @@ class _MihBusinessCardState extends State { color: MzansiInnovationHub.of(context)! .theme .primaryColor(), - fontSize: 18, + fontSize: 20, + fontWeight: FontWeight.bold, ), ), ), ], ), - MihButton( - width: 300, - onPressed: () { - // Delete review logic here - Navigator.of(context).pop(); - }, - buttonColor: - MzansiInnovationHub.of(context)!.theme.errorColor(), - child: Text( - "Delete", - style: TextStyle( - color: MzansiInnovationHub.of(context)! - .theme - .primaryColor(), - fontSize: 18, - ), - ), - ), ], ), )); From 79a2d234eba07041593fd7f4b91b7809030b31f5 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Mon, 21 Jul 2025 11:32:08 +0200 Subject: [PATCH 22/43] fix update basemodel --- backend/routers/mzansi_directory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/routers/mzansi_directory.py b/backend/routers/mzansi_directory.py index bc4366cf..d568f978 100644 --- a/backend/routers/mzansi_directory.py +++ b/backend/routers/mzansi_directory.py @@ -25,7 +25,7 @@ class BusinessRatingDeleteRequest(BaseModel): class BusinessRatingUpdateRequest(BaseModel): idbusiness_ratings: int rating_title: str - rating_description: int + rating_description: str rating_score: str @router.get("/mzasni-directory/business-ratings/user/{app_id}/{business_id}", tags=["Mzansi Directory"]) From cda981c92d64d78aea485152c181263d9e1b3454 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Mon, 21 Jul 2025 11:32:42 +0200 Subject: [PATCH 23/43] move review window to its own widget --- .../components/mih_business_info_card.dart | 361 ++---------------- 1 file changed, 22 insertions(+), 339 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart index e2edf8a3..088a531e 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart @@ -1,18 +1,11 @@ import 'package:custom_rating_bar/custom_rating_bar.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_objects/business_review.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_form.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_alert.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_window.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_text_form_field.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; -import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_mzansi_directory_services.dart'; -import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart'; import 'package:supertokens_flutter/supertokens.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -42,14 +35,6 @@ class MihBusinessCard extends StatefulWidget { } class _MihBusinessCardState extends State { - final _formKey = GlobalKey(); - final TextEditingController _reviewTitleController = TextEditingController(); - final TextEditingController _reviewScoreController = TextEditingController(); - final TextEditingController _reviewDescriptionController = - TextEditingController(); - late final VoidCallback _reviewDescriptionListener; - final ValueNotifier _counter = ValueNotifier(0); - Future _makePhoneCall(String phoneNumber) async { final Uri url = Uri(scheme: 'tel', path: phoneNumber); if (await canLaunchUrl(url)) { @@ -354,23 +339,6 @@ class _MihBusinessCardState extends State { ); } - @override - void dispose() { - super.dispose(); - _reviewDescriptionController.removeListener(_reviewDescriptionListener); - } - - @override - void initState() { - super.initState(); - _reviewDescriptionListener = () { - setState(() { - _counter.value = _reviewDescriptionController.text.characters.length; - }); - }; - _reviewDescriptionController.addListener(_reviewDescriptionListener); - } - @override Widget build(BuildContext context) { // double screenWidth = MediaQuery.of(context).size.width; @@ -525,281 +493,36 @@ class _MihBusinessCardState extends State { ); } - Color getMissionVisionLimitColor(int limit) { - if (_counter.value <= limit) { - return MzansiInnovationHub.of(context)!.theme.secondaryColor(); - } else { - return MzansiInnovationHub.of(context)!.theme.errorColor(); - } - } - Future businessReviewRatingWindow( bool previouslyRated, double width) async { showDialog( context: context, builder: (context) => FutureBuilder( - future: getUserReview(), - builder: (context, asyncSnapshot) { - if (asyncSnapshot.connectionState == ConnectionState.waiting) { - return const Mihloadingcircle( - message: "Checking for previous reviews...", - ); - } else if (asyncSnapshot.connectionState == ConnectionState.done && - asyncSnapshot.hasData) { - _reviewTitleController.text = - asyncSnapshot.requireData!.rating_title; - _reviewDescriptionController.text = - asyncSnapshot.requireData!.rating_description; - _reviewScoreController.text = - asyncSnapshot.requireData!.rating_score; - return MihPackageWindow( - fullscreen: false, - windowTitle: - asyncSnapshot.hasData ? "Edit Review" : "Add Review", - onWindowTapClose: () { - Navigator.of(context).pop(); - }, - menuOptions: asyncSnapshot.hasData - ? [ - SpeedDialChild( - child: Icon( - Icons.delete, - color: MzansiInnovationHub.of(context)! - .theme - .primaryColor(), - ), - label: "Delete Review", - labelBackgroundColor: MzansiInnovationHub.of(context)! - .theme - .successColor(), - labelStyle: TextStyle( - color: MzansiInnovationHub.of(context)! - .theme - .primaryColor(), - fontWeight: FontWeight.bold, - ), - backgroundColor: MzansiInnovationHub.of(context)! - .theme - .successColor(), - onTap: () { - // showTestWindow(); - showDeleteReviewAlert(); - }, - ), - ] - : null, - windowBody: MihSingleChildScroll( - child: Padding( - padding: - MzansiInnovationHub.of(context)!.theme.screenType == - "desktop" - ? EdgeInsets.symmetric(horizontal: width * 0.05) - : EdgeInsets.symmetric(horizontal: width * 0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - MihForm( - formKey: _formKey, - formFields: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Business Rating", - textAlign: TextAlign.left, - style: TextStyle( - color: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - const SizedBox(height: 4), - RatingBar( - size: 50, - alignment: Alignment.centerLeft, - filledIcon: Icons.star, - emptyIcon: Icons.star_border, - halfFilledIcon: Icons.star_half, - filledColor: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - emptyColor: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - halfFilledColor: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - isHalfAllowed: true, - initialRating: asyncSnapshot.hasData - ? double.parse(_reviewScoreController.text) - : 1, - maxRating: 5, - onRatingChanged: (double) { - setState(() { - _reviewScoreController.text = - double.toStringAsFixed(1); - }); - print(_reviewScoreController.text); - }, - ), - const SizedBox(height: 10), - MihTextFormField( - // width: 200, - fillColor: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - inputColor: MzansiInnovationHub.of(context)! - .theme - .primaryColor(), - controller: _reviewTitleController, - multiLineInput: false, - requiredText: true, - hintText: "Review Title", - validator: (value) { - return MihValidationServices() - .isEmpty(_reviewTitleController.text); - }, - ), - const SizedBox(height: 10), - MihTextFormField( - height: 250, - fillColor: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - inputColor: MzansiInnovationHub.of(context)! - .theme - .primaryColor(), - controller: _reviewDescriptionController, - multiLineInput: true, - requiredText: false, - hintText: "Review Description", - validator: (value) { - if (_reviewDescriptionController.text.isEmpty) { - return null; - } else { - return MihValidationServices().validateLength( - _reviewDescriptionController.text, 256); - } - }, - ), - SizedBox( - height: 15, - child: ValueListenableBuilder( - valueListenable: _counter, - builder: (BuildContext context, int value, - Widget? child) { - return Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text( - "$value", - style: TextStyle( - color: - getMissionVisionLimitColor(256), - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(width: 5), - Text( - "/256", - style: TextStyle( - color: - getMissionVisionLimitColor(256), - fontWeight: FontWeight.bold, - ), - ), - ], - ); - }, - ), - ), - const SizedBox(height: 25), - Center( - child: MihButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - // submitForm(); - } else { - MihAlertServices() - .formNotFilledCompletely(context); - } - }, - buttonColor: MzansiInnovationHub.of(context)! - .theme - .successColor(), - width: 300, - child: Text( - asyncSnapshot.hasData - ? "Edit Review" - : "Add Review", - style: TextStyle( - color: MzansiInnovationHub.of(context)! - .theme - .primaryColor(), - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ], - ), - ], - ), - ), - ), - ); - } else { - return MihPackageAlert( - alertColour: - MzansiInnovationHub.of(context)!.theme.errorColor(), - alertIcon: Icon( - Icons.warning_rounded, - size: 100, - color: MzansiInnovationHub.of(context)!.theme.errorColor(), - ), - alertTitle: "Error Pulling Data", - alertBody: Column( - children: [ - Text( - "Please ensure you are connectede top the internet and you are running the latest version of MIH then try again.", - style: TextStyle( - color: MzansiInnovationHub.of(context)! - .theme - .secondaryColor(), - fontSize: 15, - ), - ), - ], - ), - ); - } - }), - ); - } - - void showDeleteReviewAlert() { - showDialog( - context: context, - builder: (context) => MihPackageAlert( + future: getUserReview(), + builder: (context, asyncSnapshot) { + if (asyncSnapshot.connectionState == ConnectionState.waiting) { + return const Mihloadingcircle( + message: "Checking for previous reviews...", + ); + } else if (asyncSnapshot.connectionState == ConnectionState.done) { + return MihReviewBusinessWindow( + businessId: widget.businessid, + businessReview: asyncSnapshot.data, + screenWidth: width, + ); + } else { + return MihPackageAlert( alertColour: MzansiInnovationHub.of(context)!.theme.errorColor(), alertIcon: Icon( Icons.warning_rounded, size: 100, color: MzansiInnovationHub.of(context)!.theme.errorColor(), ), - alertTitle: "Delete Review", + alertTitle: "Error Pulling Data", alertBody: Column( children: [ Text( - "Are you sure you want to delete this review? This action cannot be undone.", + "Please ensure you are connectede top the internet and you are running the latest version of MIH then try again.", style: TextStyle( color: MzansiInnovationHub.of(context)! .theme @@ -807,52 +530,12 @@ class _MihBusinessCardState extends State { fontSize: 15, ), ), - const SizedBox(height: 25), - Wrap( - spacing: 10, - runSpacing: 10, - children: [ - MihButton( - width: 300, - onPressed: () { - Navigator.of(context).pop(); - }, - buttonColor: - MzansiInnovationHub.of(context)!.theme.errorColor(), - child: Text( - "Delete", - style: TextStyle( - color: MzansiInnovationHub.of(context)! - .theme - .primaryColor(), - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - ), - MihButton( - width: 300, - onPressed: () { - Navigator.of(context).pop(); - }, - buttonColor: MzansiInnovationHub.of(context)! - .theme - .successColor(), - child: Text( - "Cancel", - style: TextStyle( - color: MzansiInnovationHub.of(context)! - .theme - .primaryColor(), - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - ), - ], - ), ], ), - )); + ); + } + }, + ), + ); } } From 2309af73a188223dac7f5b193ec2c2af060a1791 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Mon, 21 Jul 2025 11:32:50 +0200 Subject: [PATCH 24/43] new review window widget --- .../mih_review_business_window.dart | 424 ++++++++++++++++++ 1 file changed, 424 insertions(+) create mode 100644 Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart new file mode 100644 index 00000000..efaeb9a7 --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart @@ -0,0 +1,424 @@ +import 'package:custom_rating_bar/custom_rating_bar.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_speed_dial/flutter_speed_dial.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/business_review.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_form.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_alert.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_window.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_text_form_field.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_alert_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_mzansi_directory_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart'; +import 'package:supertokens_flutter/supertokens.dart'; + +class MihReviewBusinessWindow extends StatefulWidget { + final String businessId; + final BusinessReview? businessReview; + final double screenWidth; + const MihReviewBusinessWindow({ + super.key, + required this.businessId, + required this.businessReview, + required this.screenWidth, + }); + + @override + State createState() => + _MihReviewBusinessWindowState(); +} + +class _MihReviewBusinessWindowState extends State { + final _formKey = GlobalKey(); + final TextEditingController _reviewTitleController = TextEditingController(); + final TextEditingController _reviewScoreController = TextEditingController(); + final TextEditingController _reviewDescriptionController = + TextEditingController(); + late final VoidCallback _reviewDescriptionListener; + final ValueNotifier _counter = ValueNotifier(0); + String userId = ""; + + void showDeleteReviewAlert() { + showDialog( + context: context, + builder: (context) => MihPackageAlert( + alertColour: MzansiInnovationHub.of(context)!.theme.errorColor(), + alertIcon: Icon( + Icons.warning_rounded, + size: 100, + color: MzansiInnovationHub.of(context)!.theme.errorColor(), + ), + alertTitle: "Delete Review", + alertBody: Column( + children: [ + Text( + "Are you sure you want to delete this review? This action cannot be undone.", + style: TextStyle( + color: MzansiInnovationHub.of(context)!.theme.secondaryColor(), + fontSize: 15, + ), + ), + const SizedBox(height: 25), + Wrap( + spacing: 10, + runSpacing: 10, + children: [ + MihButton( + width: 300, + onPressed: () async { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle(); + }, + ); + await MihMzansiDirectoryServices() + .deleteBusinessReview( + widget.businessReview!.idbusiness_ratings, + ) + .then((statusCode) { + Navigator.of(context).pop(); //Remove loading dialog + Navigator.of(context).pop(); //Remove delete dialog + if (statusCode == 200) { + Navigator.of(context).pop(); + MihAlertServices().successAlert( + "Successfully Deleted Review!", + "Your review has successfully been delete and will no longer appear under the business.", + context, + ); + } else { + MihAlertServices().errorAlert( + "Error Deleting Review", + "There was an error deleting your review. Please try again later.", + context, + ); + } + }); + }, + buttonColor: + MzansiInnovationHub.of(context)!.theme.errorColor(), + child: Text( + "Delete", + style: TextStyle( + color: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + MihButton( + width: 300, + onPressed: () { + Navigator.of(context).pop(); + }, + buttonColor: + MzansiInnovationHub.of(context)!.theme.successColor(), + child: Text( + "Cancel", + style: TextStyle( + color: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ], + ), + ), + ); + } + + Color getMissionVisionLimitColor(int limit) { + if (_counter.value <= limit) { + return MzansiInnovationHub.of(context)!.theme.secondaryColor(); + } else { + return MzansiInnovationHub.of(context)!.theme.errorColor(); + } + } + + void submitForm() async { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle(); + }, + ); + if (widget.businessReview != null) { + await MihMzansiDirectoryServices() + .updateBusinessReview( + widget.businessReview!.idbusiness_ratings, + _reviewTitleController.text, + _reviewDescriptionController.text, + _reviewScoreController.text, + ) + .then((statusCode) { + Navigator.of(context).pop(); //Remove loading dialog + if (statusCode == 200) { + Navigator.of(context).pop(); + MihAlertServices().successAlert( + "Successfully Updated Review!", + "Your review has successfully been updated and will now appear under the business.", + context, + ); + } else { + MihAlertServices().errorAlert( + "Error Updating Review", + "There was an error updating your review. Please try again later.", + context, + ); + } + }); + } else { + await MihMzansiDirectoryServices() + .addBusinessReview( + userId, + widget.businessId, + _reviewTitleController.text, + _reviewDescriptionController.text, + _reviewScoreController.text, + ) + .then((statusCode) { + Navigator.of(context).pop(); //Remove loading dialog + if (statusCode == 201) { + Navigator.of(context).pop(); + MihAlertServices().successAlert( + "Successfully Added Review!", + "Your review has successfully been added and will now appear under the business.", + context, + ); + } else { + MihAlertServices().errorAlert( + "Error Adding Review", + "There was an error adding your review. Please try again later.", + context, + ); + } + }); + } + } + + @override + void dispose() { + super.dispose(); + _reviewDescriptionController.removeListener(_reviewDescriptionListener); + } + + @override + void initState() { + super.initState(); + _reviewDescriptionListener = () { + setState(() { + _counter.value = _reviewDescriptionController.text.characters.length; + }); + }; + _reviewDescriptionController.addListener(_reviewDescriptionListener); + if (widget.businessReview != null) { + setState(() { + _reviewTitleController.text = widget.businessReview!.rating_title; + _reviewDescriptionController.text = + widget.businessReview!.rating_description; + _reviewScoreController.text = widget.businessReview!.rating_score; + }); + } + SuperTokens.getUserId().then((value) { + setState(() { + userId = value; + }); + }); + } + + @override + Widget build(BuildContext context) { + // return const Placeholder(); + return MihPackageWindow( + fullscreen: false, + windowTitle: widget.businessReview != null ? "Edit Review" : "Add Review", + onWindowTapClose: () { + Navigator.of(context).pop(); + }, + menuOptions: widget.businessReview != null + ? [ + SpeedDialChild( + child: Icon( + Icons.delete, + color: MzansiInnovationHub.of(context)!.theme.primaryColor(), + ), + label: "Delete Review", + labelBackgroundColor: + MzansiInnovationHub.of(context)!.theme.successColor(), + labelStyle: TextStyle( + color: MzansiInnovationHub.of(context)!.theme.primaryColor(), + fontWeight: FontWeight.bold, + ), + backgroundColor: + MzansiInnovationHub.of(context)!.theme.successColor(), + onTap: () { + showDeleteReviewAlert(); + }, + ), + ] + : null, + windowBody: MihSingleChildScroll( + child: Padding( + padding: + MzansiInnovationHub.of(context)!.theme.screenType == "desktop" + ? EdgeInsets.symmetric(horizontal: widget.screenWidth * 0.05) + : EdgeInsets.symmetric(horizontal: widget.screenWidth * 0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + MihForm( + formKey: _formKey, + formFields: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Business Rating", + textAlign: TextAlign.left, + style: TextStyle( + color: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 4), + RatingBar( + size: 50, + alignment: Alignment.centerLeft, + filledIcon: Icons.star, + emptyIcon: Icons.star_border, + halfFilledIcon: Icons.star_half, + filledColor: + MzansiInnovationHub.of(context)!.theme.secondaryColor(), + emptyColor: + MzansiInnovationHub.of(context)!.theme.secondaryColor(), + halfFilledColor: + MzansiInnovationHub.of(context)!.theme.secondaryColor(), + isHalfAllowed: true, + initialRating: widget.businessReview != null + ? double.parse(_reviewScoreController.text) + : 1, + maxRating: 5, + onRatingChanged: (double) { + setState(() { + _reviewScoreController.text = double.toStringAsFixed(1); + }); + print(_reviewScoreController.text); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + // width: 200, + fillColor: + MzansiInnovationHub.of(context)!.theme.secondaryColor(), + inputColor: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + controller: _reviewTitleController, + multiLineInput: false, + requiredText: true, + hintText: "Review Title", + validator: (value) { + return MihValidationServices() + .isEmpty(_reviewTitleController.text); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + height: 250, + fillColor: + MzansiInnovationHub.of(context)!.theme.secondaryColor(), + inputColor: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + controller: _reviewDescriptionController, + multiLineInput: true, + requiredText: false, + hintText: "Review Description", + validator: (value) { + if (_reviewDescriptionController.text.isEmpty) { + return null; + } else { + return MihValidationServices().validateLength( + _reviewDescriptionController.text, 256); + } + }, + ), + SizedBox( + height: 15, + child: ValueListenableBuilder( + valueListenable: _counter, + builder: + (BuildContext context, int value, Widget? child) { + return Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "$value", + style: TextStyle( + color: getMissionVisionLimitColor(256), + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 5), + Text( + "/256", + style: TextStyle( + color: getMissionVisionLimitColor(256), + fontWeight: FontWeight.bold, + ), + ), + ], + ); + }, + ), + ), + const SizedBox(height: 25), + Center( + child: MihButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + submitForm(); + } else { + MihAlertServices().formNotFilledCompletely(context); + } + }, + buttonColor: + MzansiInnovationHub.of(context)!.theme.successColor(), + width: 300, + child: Text( + widget.businessReview != null + ? "Edit Review" + : "Add Review", + style: TextStyle( + color: MzansiInnovationHub.of(context)! + .theme + .primaryColor(), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ); + } +} From eb0c501e42c8376ea18dc8da905e23063912e305 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Mon, 21 Jul 2025 11:33:09 +0200 Subject: [PATCH 25/43] add success and error to new alertservce --- .../lib/mih_services/mih_alert_services.dart | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/Frontend/lib/mih_services/mih_alert_services.dart b/Frontend/lib/mih_services/mih_alert_services.dart index 431834cc..35157e4b 100644 --- a/Frontend/lib/mih_services/mih_alert_services.dart +++ b/Frontend/lib/mih_services/mih_alert_services.dart @@ -55,4 +55,66 @@ class MihAlertServices { }, ); } + + void successAlert(String title, String message, BuildContext context) { + showDialog( + context: context, + builder: (context) { + return MihPackageAlert( + alertIcon: Icon( + Icons.check_circle_outline_rounded, + size: 150, + color: MzansiInnovationHub.of(context)!.theme.successColor(), + ), + alertTitle: title, + alertBody: Column( + children: [ + Text( + message, + style: TextStyle( + color: + MzansiInnovationHub.of(context)!.theme.secondaryColor(), + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 25), + ], + ), + alertColour: MzansiInnovationHub.of(context)!.theme.successColor(), + ); + }, + ); + } + + void errorAlert(String title, String message, BuildContext context) { + showDialog( + context: context, + builder: (context) { + return MihPackageAlert( + alertIcon: Icon( + Icons.warning_amber_rounded, + size: 150, + color: MzansiInnovationHub.of(context)!.theme.errorColor(), + ), + alertTitle: title, + alertBody: Column( + children: [ + Text( + message, + style: TextStyle( + color: + MzansiInnovationHub.of(context)!.theme.secondaryColor(), + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 25), + ], + ), + alertColour: MzansiInnovationHub.of(context)!.theme.errorColor(), + ); + }, + ); + } } From 82e28129d1bc566c7b704ccd92f7f571e4df3705 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Mon, 21 Jul 2025 11:33:20 +0200 Subject: [PATCH 26/43] change names of services --- .../lib/mih_services/mih_mzansi_directory_services.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Frontend/lib/mih_services/mih_mzansi_directory_services.dart b/Frontend/lib/mih_services/mih_mzansi_directory_services.dart index 5a17b43c..94552790 100644 --- a/Frontend/lib/mih_services/mih_mzansi_directory_services.dart +++ b/Frontend/lib/mih_services/mih_mzansi_directory_services.dart @@ -39,12 +39,12 @@ class MihMzansiDirectoryServices { } } - Future addLoyaltyCardAPICall( + Future addBusinessReview( String app_id, String business_id, String rating_title, String rating_description, - int rating_score, + String rating_score, ) async { var response = await http.post( Uri.parse( @@ -67,7 +67,7 @@ class MihMzansiDirectoryServices { } } - Future deleteLoyaltyCardAPICall( + Future deleteBusinessReview( int idbusiness_ratings, ) async { var response = await http.delete( @@ -86,7 +86,7 @@ class MihMzansiDirectoryServices { } } - static Future updateLoyaltyCardAPICall( + Future updateBusinessReview( int idbusiness_ratings, String rating_title, String rating_description, From 34b81d9d12728c48799fab0c53f72cee3d03a659 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Mon, 21 Jul 2025 18:52:43 +0200 Subject: [PATCH 27/43] Change to business as param --- .../components/mih_business_info_card.dart | 68 +++++++++++-------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart index 088a531e..19c0d9be 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart @@ -1,6 +1,7 @@ import 'package:custom_rating_bar/custom_rating_bar.dart'; 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_objects/business_review.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_alert.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; @@ -10,23 +11,25 @@ import 'package:supertokens_flutter/supertokens.dart'; import 'package:url_launcher/url_launcher.dart'; class MihBusinessCard extends StatefulWidget { - final String businessid; - final String businessName; - final String cellNumber; - final String email; - final String gpsLocation; - final String? website; - final double rating; + final Business business; + // final String businessid; + // final String businessName; + // final String cellNumber; + // final String email; + // final String gpsLocation; + // final String? website; + // final double rating; final double width; const MihBusinessCard({ super.key, - required this.businessid, - required this.businessName, - required this.cellNumber, - required this.email, - required this.gpsLocation, - required this.rating, - this.website, + required this.business, + // required this.businessid, + // required this.businessName, + // required this.cellNumber, + // required this.email, + // required this.gpsLocation, + // required this.rating, + // this.website, required this.width, }); @@ -53,7 +56,7 @@ class _MihBusinessCardState extends State { alertBody: Column( children: [ Text( - "We couldn't open your phone app to call ${widget.cellNumber}. To fix this, make sure you have a phone application installed and it's set as your default dialer.", + "We couldn't open your phone app to call ${widget.business.contact_no}. To fix this, make sure you have a phone application installed and it's set as your default dialer.", style: TextStyle( color: MzansiInnovationHub.of(context)! .theme @@ -103,7 +106,7 @@ class _MihBusinessCardState extends State { alertBody: Column( children: [ Text( - "We couldn't launch your email app to send a message to ${widget.email}. To fix this, please confirm that you have an email application installed and that it's set as your default.", + "We couldn't launch your email app to send a message to ${widget.business.bus_email}. To fix this, please confirm that you have an email application installed and that it's set as your default.", style: TextStyle( color: MzansiInnovationHub.of(context)! .theme @@ -144,7 +147,7 @@ class _MihBusinessCardState extends State { alertBody: Column( children: [ Text( - "There was an issue opening maps for ${widget.businessName}. This usually happens if you don't have a maps app installed or it's not set as your default. Please install one to proceed.", + "There was an issue opening maps for ${widget.business.Name}. This usually happens if you don't have a maps app installed or it's not set as your default. Please install one to proceed.", style: TextStyle( color: MzansiInnovationHub.of(context)! .theme @@ -173,7 +176,7 @@ class _MihBusinessCardState extends State { alertBody: Column( children: [ Text( - "There was an issue opening maps for ${widget.businessName}. This usually happens if you don't have a maps app installed or it's not set as your default. Please install one to proceed.", + "There was an issue opening maps for ${widget.business.Name}. This usually happens if you don't have a maps app installed or it's not set as your default. Please install one to proceed.", style: TextStyle( color: MzansiInnovationHub.of(context)! .theme @@ -335,7 +338,7 @@ class _MihBusinessCardState extends State { String user_id = await SuperTokens.getUserId(); return await MihMzansiDirectoryServices().getUserReviewOfBusiness( user_id, - widget.businessid, + widget.business.business_id, ); } @@ -370,7 +373,9 @@ class _MihBusinessCardState extends State { halfFilledColor: MzansiInnovationHub.of(context)!.theme.primaryColor(), isHalfAllowed: true, - initialRating: widget.rating, + initialRating: widget.business.rating.isNotEmpty + ? double.parse(widget.business.rating) + : 0, maxRating: 5, ), // Text( @@ -393,7 +398,7 @@ class _MihBusinessCardState extends State { const Color(0xffaff0b3), () { // print("Calling ${widget.cellNumber}"); - _makePhoneCall(widget.cellNumber); + _makePhoneCall(widget.business.contact_no); }, ), Divider( @@ -407,9 +412,9 @@ class _MihBusinessCardState extends State { () { // print("Emailing ${widget.email}"); _launchEmail( - widget.email, - "Inquiery about ${widget.businessName}", - "Dear ${widget.businessName},\n\nI would like to inquire about your services.\n\nBest regards,\n", + widget.business.bus_email, + "Inquiery about ${widget.business.Name}", + "Dear ${widget.business.Name},\n\nI would like to inquire about your services.\n\nBest regards,\n", ); }, ), @@ -422,9 +427,10 @@ class _MihBusinessCardState extends State { Icons.location_on, const Color(0xffd69d7d), () { - final latitude = double.parse(widget.gpsLocation.split(',')[0]); + final latitude = + double.parse(widget.business.gps_location.split(',')[0]); final longitude = - double.parse(widget.gpsLocation.split(',')[1]); + double.parse(widget.business.gps_location.split(',')[1]); _launchGoogleMapsWithUrl( latitude: latitude, longitude: longitude, @@ -432,20 +438,22 @@ class _MihBusinessCardState extends State { }, ), Visibility( - visible: widget.website != null && widget.website! != "", + visible: widget.business.website.isNotEmpty && + widget.business.website != "", child: Divider( color: MzansiInnovationHub.of(context)!.theme.primaryColor(), ), ), Visibility( - visible: widget.website != null && widget.website! != "", + visible: widget.business.website.isNotEmpty && + widget.business.website != "", child: _buildContactInfo( "Website", "Find out more about us.", Icons.vpn_lock, const Color(0xffd67d8a), () { - _launchWebsite(widget.website!); + _launchWebsite(widget.business.website); }, ), ), @@ -506,7 +514,7 @@ class _MihBusinessCardState extends State { ); } else if (asyncSnapshot.connectionState == ConnectionState.done) { return MihReviewBusinessWindow( - businessId: widget.businessid, + business: widget.business, businessReview: asyncSnapshot.data, screenWidth: width, ); From 147f9d985a015155369e6f7eb57cc5b15b487d8d Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Mon, 21 Jul 2025 18:53:01 +0200 Subject: [PATCH 28/43] change businessid to business --- .../components/mih_review_business_window.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart index efaeb9a7..977f0916 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart @@ -2,6 +2,7 @@ import 'package:custom_rating_bar/custom_rating_bar.dart'; import 'package:flutter/material.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.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_objects/business_review.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_form.dart'; @@ -16,12 +17,12 @@ import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.dart' import 'package:supertokens_flutter/supertokens.dart'; class MihReviewBusinessWindow extends StatefulWidget { - final String businessId; + final Business business; final BusinessReview? businessReview; final double screenWidth; const MihReviewBusinessWindow({ super.key, - required this.businessId, + required this.business, required this.businessReview, required this.screenWidth, }); @@ -179,7 +180,7 @@ class _MihReviewBusinessWindowState extends State { await MihMzansiDirectoryServices() .addBusinessReview( userId, - widget.businessId, + widget.business.business_id, _reviewTitleController.text, _reviewDescriptionController.text, _reviewScoreController.text, From 542bd5e6971ca38049e9fa854baa71bb61f9a9c4 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Mon, 21 Jul 2025 18:53:13 +0200 Subject: [PATCH 29/43] use business as param --- .../package_tools/mih_business_details.dart | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart index fa9fb039..acd79c0a 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart @@ -680,15 +680,16 @@ class _MihBusinessDetailsState extends State { SizedBox( width: 700, child: MihBusinessCard( - businessid: widget.arguments.business!.business_id, - businessName: widget.arguments.business!.Name, - cellNumber: widget.arguments.business!.contact_no, - email: widget.arguments.business!.bus_email, - gpsLocation: widget.arguments.business!.gps_location, - rating: widget.arguments.business!.rating.isNotEmpty - ? double.parse(widget.arguments.business!.rating) - : 0, - website: widget.arguments.business!.website, + // businessid: widget.arguments.business!.business_id, + // businessName: widget.arguments.business!.Name, + // cellNumber: widget.arguments.business!.contact_no, + // email: widget.arguments.business!.bus_email, + // gpsLocation: widget.arguments.business!.gps_location, + // rating: widget.arguments.business!.rating.isNotEmpty + // ? double.parse(widget.arguments.business!.rating) + // : 0, + // website: widget.arguments.business!.website, + business: widget.arguments.business!, width: width, ), ), From 625fae4d5a5ed3700aafe5722b7b57e78522abe5 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Mon, 21 Jul 2025 18:53:24 +0200 Subject: [PATCH 30/43] use business as param --- .../mih_business_details_view.dart | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart index 89ce09fd..fb182804 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart @@ -162,15 +162,16 @@ class _MihBusinessDetailsViewState extends State { SizedBox( width: 700, child: MihBusinessCard( - businessid: widget.business.business_id, - businessName: widget.business.Name, - cellNumber: widget.business.contact_no, - email: widget.business.bus_email, - gpsLocation: widget.business.gps_location, - rating: widget.business.rating.isNotEmpty - ? double.parse(widget.business.rating) - : 0, - website: widget.business.website, + business: widget.business, + // businessid: widget.business.business_id, + // businessName: widget.business.Name, + // cellNumber: widget.business.contact_no, + // email: widget.business.bus_email, + // gpsLocation: widget.business.gps_location, + // rating: widget.business.rating.isNotEmpty + // ? double.parse(widget.business.rating) + // : 0, + // website: widget.business.website, width: width, ), ), From 31e2c18d92cb24b0bdc481e5edbd694dd890931f Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Tue, 22 Jul 2025 09:35:52 +0200 Subject: [PATCH 31/43] fix get all business review --- Frontend/lib/mih_services/mih_mzansi_directory_services.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Frontend/lib/mih_services/mih_mzansi_directory_services.dart b/Frontend/lib/mih_services/mih_mzansi_directory_services.dart index 94552790..5e794442 100644 --- a/Frontend/lib/mih_services/mih_mzansi_directory_services.dart +++ b/Frontend/lib/mih_services/mih_mzansi_directory_services.dart @@ -23,9 +23,8 @@ class MihMzansiDirectoryServices { } } - static Future> getAllReviewsofBusiness( + Future> getAllReviewsofBusiness( String business_id, - String businessid, ) async { final response = await http.get(Uri.parse( "${AppEnviroment.baseApiUrl}/mzasni-directory/business-ratings/all/$business_id")); From c9f7ec0e2e3cbc385f98855fbddfd3810583c0e0 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Tue, 22 Jul 2025 09:36:19 +0200 Subject: [PATCH 32/43] add new review tool to business profile view --- .../mzansi_business_profile_view.dart | 8 ++ .../package_tools/mih_business_reviews.dart | 98 +++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart index f70aaefd..6314ada3 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart @@ -4,6 +4,7 @@ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tools.dart'; import 'package:flutter/material.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart'; class MzansiBusinessProfileView extends StatefulWidget { final Business business; @@ -54,6 +55,11 @@ class _MzansiBusinessProfileViewState extends State { _selcetedIndex = 0; }); }; + temp[const Icon(Icons.star_rate_rounded)] = () { + setState(() { + _selcetedIndex = 1; + }); + }; return MihPackageTools( tools: temp, selcetedIndex: _selcetedIndex, @@ -63,6 +69,7 @@ class _MzansiBusinessProfileViewState extends State { List getToolBody() { List toolBodies = [ MihBusinessDetailsView(business: widget.business), + MihBusinessReviews(businessId: widget.business.business_id), ]; return toolBodies; } @@ -70,6 +77,7 @@ class _MzansiBusinessProfileViewState extends State { List getToolTitle() { List toolTitles = [ "Profile", + "Reviews", ]; return toolTitles; } diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart new file mode 100644 index 00000000..dd5b669c --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart @@ -0,0 +1,98 @@ +import 'package:custom_rating_bar/custom_rating_bar.dart'; +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/business_review.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_mzansi_directory_services.dart'; + +class MihBusinessReviews extends StatefulWidget { + final String businessId; + const MihBusinessReviews({ + super.key, + required this.businessId, + }); + + @override + State createState() => _MihBusinessReviewsState(); +} + +class _MihBusinessReviewsState extends State { + // late Future> _reviews; + + // @override + // void initState() { + // super.initState(); + // _reviews = MihMzansiDirectoryServices().getAllReviewsofBusiness( + // widget.businessId, + // ); + // } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: MihMzansiDirectoryServices().getAllReviewsofBusiness( + widget.businessId, + ), + builder: (context, asyncSnapshot) { + if (asyncSnapshot.connectionState == ConnectionState.waiting) { + return const Mihloadingcircle(); + } else if (asyncSnapshot.connectionState == ConnectionState.done && + asyncSnapshot.hasData) { + List reviews = asyncSnapshot.data!; + return ListView.separated( + itemCount: reviews.length, + separatorBuilder: (context, index) => Divider(), + itemBuilder: (context, index) { + return ListTile( + title: RatingBar.readOnly( + size: 25, + alignment: Alignment.centerLeft, + filledIcon: Icons.star, + emptyIcon: Icons.star_border, + halfFilledIcon: Icons.star_half, + filledColor: + MzansiInnovationHub.of(context)!.theme.secondaryColor(), + emptyColor: + MzansiInnovationHub.of(context)!.theme.secondaryColor(), + halfFilledColor: + MzansiInnovationHub.of(context)!.theme.secondaryColor(), + isHalfAllowed: true, + initialRating: double.parse(reviews[index].rating_score), + maxRating: 5, + ), + subtitle: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Text( + reviews[index].rating_title, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + Row( + children: [ + Text( + reviews[index].rating_description, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.normal, + ), + ), + ], + ), + ], + ), + ); + }, + ); + } else { + return Center(child: Text('Error')); + } + }); + } +} From 3b73e324b4de4e74eea779bd6817237b5f3476e5 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Tue, 22 Jul 2025 10:39:50 +0200 Subject: [PATCH 33/43] order business reviews date desc --- backend/routers/mzansi_directory.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/routers/mzansi_directory.py b/backend/routers/mzansi_directory.py index d568f978..37a2d2a9 100644 --- a/backend/routers/mzansi_directory.py +++ b/backend/routers/mzansi_directory.py @@ -89,7 +89,8 @@ async def read_all_ratings_by_business_id(business_id: str, session: SessionCont query += "FROM mzansi_directory.business_ratings " query += "inner join app_data.users " query += "on business_ratings.app_id = users.app_id " - query += "where business_ratings.business_id = %s;" + query += "where business_ratings.business_id = %s " + query += "order by business_ratings.date_time desc;" cursor.execute(query, (business_id,)) items = [ { From ddef2fcf8e4c92ecfc036eebe3e66ad577b53d5d Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Tue, 22 Jul 2025 10:40:23 +0200 Subject: [PATCH 34/43] add a display is no reviews --- .../package_tools/mih_business_reviews.dart | 160 ++++++++++++------ 1 file changed, 109 insertions(+), 51 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart index dd5b669c..e598fce6 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart @@ -1,15 +1,17 @@ import 'package:custom_rating_bar/custom_rating_bar.dart'; 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_objects/business_review.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_mzansi_directory_services.dart'; class MihBusinessReviews extends StatefulWidget { - final String businessId; + final Business business; const MihBusinessReviews({ super.key, - required this.businessId, + required this.business, }); @override @@ -27,11 +29,27 @@ class _MihBusinessReviewsState extends State { // ); // } + void onReviewTap(BusinessReview? businessReview, double width) { + // showDialog(context: context, builder: (context)=> ) + showDialog( + context: context, + builder: (context) { + return MihReviewBusinessWindow( + business: widget.business, + businessReview: businessReview, + screenWidth: width, + readOnly: true, + ); + }, + ); + } + @override Widget build(BuildContext context) { + double screenWidth = MediaQuery.of(context).size.width; return FutureBuilder( future: MihMzansiDirectoryServices().getAllReviewsofBusiness( - widget.businessId, + widget.business.business_id, ), builder: (context, asyncSnapshot) { if (asyncSnapshot.connectionState == ConnectionState.waiting) { @@ -39,57 +57,97 @@ class _MihBusinessReviewsState extends State { } else if (asyncSnapshot.connectionState == ConnectionState.done && asyncSnapshot.hasData) { List reviews = asyncSnapshot.data!; - return ListView.separated( - itemCount: reviews.length, - separatorBuilder: (context, index) => Divider(), - itemBuilder: (context, index) { - return ListTile( - title: RatingBar.readOnly( - size: 25, - alignment: Alignment.centerLeft, - filledIcon: Icons.star, - emptyIcon: Icons.star_border, - halfFilledIcon: Icons.star_half, - filledColor: + if (reviews.isEmpty) { + return Column( + children: [ + const SizedBox(height: 50), + Icon( + Icons.star_rate_rounded, + size: 150, + color: MzansiInnovationHub.of(context)!.theme.secondaryColor(), - emptyColor: - MzansiInnovationHub.of(context)!.theme.secondaryColor(), - halfFilledColor: - MzansiInnovationHub.of(context)!.theme.secondaryColor(), - isHalfAllowed: true, - initialRating: double.parse(reviews[index].rating_score), - maxRating: 5, ), - subtitle: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Row( - children: [ - Text( - reviews[index].rating_title, - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - Row( - children: [ - Text( - reviews[index].rating_description, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.normal, - ), - ), - ], - ), - ], + Text( + "No reviews yet, be the first the review\n${widget.business.Name}", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), ), - ); - }, - ); + ], + ); + } else { + int descriptionDisplayCOunt = 75; + return ListView.separated( + itemCount: reviews.length, + separatorBuilder: (context, index) => Divider(), + itemBuilder: (context, index) { + return ListTile( + onTap: () { + onReviewTap(reviews[index], screenWidth); + }, + title: RatingBar.readOnly( + size: 25, + alignment: Alignment.centerLeft, + filledIcon: Icons.star, + emptyIcon: Icons.star_border, + halfFilledIcon: Icons.star_half, + filledColor: const Color(0xffe9e8a1), + // MzansiInnovationHub.of(context)!.theme.primaryColor(), + emptyColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + halfFilledColor: const Color(0xffe9e8a1), + // MzansiInnovationHub.of(context)!.theme.primaryColor(), + // filledColor: + // MzansiInnovationHub.of(context)!.theme.secondaryColor(), + // emptyColor: + // MzansiInnovationHub.of(context)!.theme.secondaryColor(), + // halfFilledColor: + // MzansiInnovationHub.of(context)!.theme.secondaryColor(), + isHalfAllowed: true, + initialRating: double.parse(reviews[index].rating_score), + maxRating: 5, + ), + subtitle: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Text( + reviews[index].rating_title, + softWrap: true, + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + Text( + "${reviews[index].rating_description.substring(0, reviews[index].rating_description.length >= descriptionDisplayCOunt ? descriptionDisplayCOunt : reviews[index].rating_description.length - 1)}${reviews[index].rating_description.length >= descriptionDisplayCOunt ? "..." : ""}", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.normal, + ), + ), + Row( + children: [ + Text( + reviews[index].date_time.split("T")[0], + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.normal, + ), + ), + ], + ), + ], + ), + ); + }, + ); + } } else { return Center(child: Text('Error')); } From 00dea2f78a5e209d50faaab2b80e93932d7e6a2e Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Tue, 22 Jul 2025 10:41:35 +0200 Subject: [PATCH 35/43] chanmge rating stars color top yellow --- .../components/mih_business_info_card.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart index 19c0d9be..94fe27b5 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart @@ -367,11 +367,11 @@ class _MihBusinessCardState extends State { filledIcon: Icons.star, emptyIcon: Icons.star_border, halfFilledIcon: Icons.star_half, - filledColor: - MzansiInnovationHub.of(context)!.theme.primaryColor(), + filledColor: const Color(0xffe9e8a1), + // MzansiInnovationHub.of(context)!.theme.primaryColor(), emptyColor: MzansiInnovationHub.of(context)!.theme.primaryColor(), - halfFilledColor: - MzansiInnovationHub.of(context)!.theme.primaryColor(), + halfFilledColor: const Color(0xffe9e8a1), + // MzansiInnovationHub.of(context)!.theme.primaryColor(), isHalfAllowed: true, initialRating: widget.business.rating.isNotEmpty ? double.parse(widget.business.rating) @@ -517,6 +517,7 @@ class _MihBusinessCardState extends State { business: widget.business, businessReview: asyncSnapshot.data, screenWidth: width, + readOnly: false, ); } else { return MihPackageAlert( From 845c8a6688ceec3737e08acc34e914af16bfeabf Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Tue, 22 Jul 2025 10:41:59 +0200 Subject: [PATCH 36/43] give window a read only mode --- .../mih_review_business_window.dart | 123 ++++++++++-------- 1 file changed, 72 insertions(+), 51 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart index 977f0916..9c12e472 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart @@ -20,11 +20,13 @@ class MihReviewBusinessWindow extends StatefulWidget { final Business business; final BusinessReview? businessReview; final double screenWidth; + final bool readOnly; const MihReviewBusinessWindow({ super.key, required this.business, required this.businessReview, required this.screenWidth, + required this.readOnly, }); @override @@ -205,6 +207,16 @@ class _MihReviewBusinessWindowState extends State { } } + String getWindowTitle() { + if (widget.readOnly) { + return "Review Details"; + } else if (widget.businessReview != null) { + return "Edit Review"; + } else { + return "Add Review"; + } + } + @override void dispose() { super.dispose(); @@ -240,11 +252,11 @@ class _MihReviewBusinessWindowState extends State { // return const Placeholder(); return MihPackageWindow( fullscreen: false, - windowTitle: widget.businessReview != null ? "Edit Review" : "Add Review", + windowTitle: getWindowTitle(), onWindowTapClose: () { Navigator.of(context).pop(); }, - menuOptions: widget.businessReview != null + menuOptions: widget.businessReview != null && !widget.readOnly ? [ SpeedDialChild( child: Icon( @@ -331,6 +343,7 @@ class _MihReviewBusinessWindowState extends State { controller: _reviewTitleController, multiLineInput: false, requiredText: true, + readOnly: widget.readOnly, hintText: "Review Title", validator: (value) { return MihValidationServices() @@ -346,7 +359,8 @@ class _MihReviewBusinessWindowState extends State { MzansiInnovationHub.of(context)!.theme.primaryColor(), controller: _reviewDescriptionController, multiLineInput: true, - requiredText: false, + requiredText: widget.readOnly, + readOnly: widget.readOnly, hintText: "Review Description", validator: (value) { if (_reviewDescriptionController.text.isEmpty) { @@ -357,59 +371,66 @@ class _MihReviewBusinessWindowState extends State { } }, ), - SizedBox( - height: 15, - child: ValueListenableBuilder( - valueListenable: _counter, - builder: - (BuildContext context, int value, Widget? child) { - return Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text( - "$value", - style: TextStyle( - color: getMissionVisionLimitColor(256), - fontWeight: FontWeight.bold, + Visibility( + visible: !widget.readOnly, + child: SizedBox( + height: 15, + child: ValueListenableBuilder( + valueListenable: _counter, + builder: + (BuildContext context, int value, Widget? child) { + return Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "$value", + style: TextStyle( + color: getMissionVisionLimitColor(256), + fontWeight: FontWeight.bold, + ), ), - ), - const SizedBox(width: 5), - Text( - "/256", - style: TextStyle( - color: getMissionVisionLimitColor(256), - fontWeight: FontWeight.bold, + const SizedBox(width: 5), + Text( + "/256", + style: TextStyle( + color: getMissionVisionLimitColor(256), + fontWeight: FontWeight.bold, + ), ), - ), - ], - ); - }, + ], + ); + }, + ), ), ), const SizedBox(height: 25), - Center( - child: MihButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - submitForm(); - } else { - MihAlertServices().formNotFilledCompletely(context); - } - }, - buttonColor: - MzansiInnovationHub.of(context)!.theme.successColor(), - width: 300, - child: Text( - widget.businessReview != null - ? "Edit Review" - : "Add Review", - style: TextStyle( - color: MzansiInnovationHub.of(context)! - .theme - .primaryColor(), - fontSize: 20, - fontWeight: FontWeight.bold, + Visibility( + visible: !widget.readOnly, + child: Center( + child: MihButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + submitForm(); + } else { + MihAlertServices().formNotFilledCompletely(context); + } + }, + buttonColor: MzansiInnovationHub.of(context)! + .theme + .successColor(), + width: 300, + child: Text( + widget.businessReview != null + ? "Edit Review" + : "Add Review", + style: TextStyle( + color: MzansiInnovationHub.of(context)! + .theme + .primaryColor(), + fontSize: 20, + fontWeight: FontWeight.bold, + ), ), ), ), From 52e463ee20aa090b05535d13ed1b7523f8be3b45 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Tue, 22 Jul 2025 10:42:16 +0200 Subject: [PATCH 37/43] use business instead of businessid --- .../business_profile/mzansi_business_profile_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart index 6314ada3..164863cb 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart @@ -69,7 +69,7 @@ class _MzansiBusinessProfileViewState extends State { List getToolBody() { List toolBodies = [ MihBusinessDetailsView(business: widget.business), - MihBusinessReviews(businessId: widget.business.business_id), + MihBusinessReviews(business: widget.business), ]; return toolBodies; } From 8a88b158eb9eeb85663163f2b1c102ddf84708ee Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Tue, 22 Jul 2025 10:52:57 +0200 Subject: [PATCH 38/43] Add reviewer name to review window and list --- .../mih_review_business_window.dart | 23 +++++++++++++++++-- .../package_tools/mih_business_reviews.dart | 23 +++++++++++-------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart index 9c12e472..5c155523 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart @@ -38,6 +38,8 @@ class _MihReviewBusinessWindowState extends State { final _formKey = GlobalKey(); final TextEditingController _reviewTitleController = TextEditingController(); final TextEditingController _reviewScoreController = TextEditingController(); + final TextEditingController _reviewReviewerController = + TextEditingController(); final TextEditingController _reviewDescriptionController = TextEditingController(); late final VoidCallback _reviewDescriptionListener; @@ -211,7 +213,7 @@ class _MihReviewBusinessWindowState extends State { if (widget.readOnly) { return "Review Details"; } else if (widget.businessReview != null) { - return "Edit Review"; + return "Update Review"; } else { return "Add Review"; } @@ -238,6 +240,7 @@ class _MihReviewBusinessWindowState extends State { _reviewDescriptionController.text = widget.businessReview!.rating_description; _reviewScoreController.text = widget.businessReview!.rating_score; + _reviewReviewerController.text = widget.businessReview!.reviewer; }); } SuperTokens.getUserId().then((value) { @@ -334,6 +337,22 @@ class _MihReviewBusinessWindowState extends State { }, ), const SizedBox(height: 10), + MihTextFormField( + // width: 200, + fillColor: + MzansiInnovationHub.of(context)!.theme.secondaryColor(), + inputColor: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + controller: _reviewReviewerController, + multiLineInput: false, + requiredText: true, + readOnly: true, + hintText: "Reviewer", + validator: (value) { + return null; + }, + ), + const SizedBox(height: 10), MihTextFormField( // width: 200, fillColor: @@ -422,7 +441,7 @@ class _MihReviewBusinessWindowState extends State { width: 300, child: Text( widget.businessReview != null - ? "Edit Review" + ? "Update Review" : "Add Review", style: TextStyle( color: MzansiInnovationHub.of(context)! diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart index e598fce6..fd4b3ad2 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart @@ -115,6 +115,13 @@ class _MihBusinessReviewsState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [ + // Text( + // "${reviews[index].reviewer} ", + // style: TextStyle( + // fontSize: 15, + // fontWeight: FontWeight.bold, + // ), + // ), Text( reviews[index].rating_title, softWrap: true, @@ -131,16 +138,12 @@ class _MihBusinessReviewsState extends State { fontWeight: FontWeight.normal, ), ), - Row( - children: [ - Text( - reviews[index].date_time.split("T")[0], - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.normal, - ), - ), - ], + Text( + "${reviews[index].date_time.split("T")[0]} ", + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.normal, + ), ), ], ), From 9ccbf220b4c0673127cd92bdf929682b2767b811 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Tue, 22 Jul 2025 12:05:00 +0200 Subject: [PATCH 39/43] add current rating to service --- .../mih_review_business_window.dart | 40 ++++++++++++------- .../mih_mzansi_directory_services.dart | 2 + 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart index 5c155523..c6ad65a5 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart @@ -188,6 +188,7 @@ class _MihReviewBusinessWindowState extends State { _reviewTitleController.text, _reviewDescriptionController.text, _reviewScoreController.text, + widget.business.rating.isEmpty ? "0.0" : widget.business.rating, ) .then((statusCode) { Navigator.of(context).pop(); //Remove loading dialog @@ -242,6 +243,8 @@ class _MihReviewBusinessWindowState extends State { _reviewScoreController.text = widget.businessReview!.rating_score; _reviewReviewerController.text = widget.businessReview!.reviewer; }); + } else { + _reviewScoreController.text = "1.0"; // Default score } SuperTokens.getUserId().then((value) { setState(() { @@ -336,21 +339,28 @@ class _MihReviewBusinessWindowState extends State { print(_reviewScoreController.text); }, ), - const SizedBox(height: 10), - MihTextFormField( - // width: 200, - fillColor: - MzansiInnovationHub.of(context)!.theme.secondaryColor(), - inputColor: - MzansiInnovationHub.of(context)!.theme.primaryColor(), - controller: _reviewReviewerController, - multiLineInput: false, - requiredText: true, - readOnly: true, - hintText: "Reviewer", - validator: (value) { - return null; - }, + Visibility( + visible: widget.readOnly, + child: const SizedBox(height: 10), + ), + Visibility( + visible: widget.readOnly, + child: MihTextFormField( + // width: 200, + fillColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: + MzansiInnovationHub.of(context)!.theme.primaryColor(), + controller: _reviewReviewerController, + multiLineInput: false, + requiredText: true, + readOnly: true, + hintText: "Reviewer", + validator: (value) { + return null; + }, + ), ), const SizedBox(height: 10), MihTextFormField( diff --git a/Frontend/lib/mih_services/mih_mzansi_directory_services.dart b/Frontend/lib/mih_services/mih_mzansi_directory_services.dart index 5e794442..f98e18ca 100644 --- a/Frontend/lib/mih_services/mih_mzansi_directory_services.dart +++ b/Frontend/lib/mih_services/mih_mzansi_directory_services.dart @@ -44,6 +44,7 @@ class MihMzansiDirectoryServices { String rating_title, String rating_description, String rating_score, + String current_rating, ) async { var response = await http.post( Uri.parse( @@ -57,6 +58,7 @@ class MihMzansiDirectoryServices { "rating_title": rating_title, "rating_description": rating_description, "rating_score": rating_score, + "current_rating": current_rating, }), ); if (response.statusCode == 201) { From 73cd6661eb26445c8f6a9362847eac04fb7f6aa4 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 23 Jul 2025 09:38:46 +0200 Subject: [PATCH 40/43] add business rating calc to api and service --- .../mih_mzansi_directory_services.dart | 31 +++-- backend/routers/mzansi_directory.py | 127 +++++++++++++----- 2 files changed, 115 insertions(+), 43 deletions(-) diff --git a/Frontend/lib/mih_services/mih_mzansi_directory_services.dart b/Frontend/lib/mih_services/mih_mzansi_directory_services.dart index f98e18ca..0a886723 100644 --- a/Frontend/lib/mih_services/mih_mzansi_directory_services.dart +++ b/Frontend/lib/mih_services/mih_mzansi_directory_services.dart @@ -11,7 +11,7 @@ class MihMzansiDirectoryServices { String business_id, ) async { final response = await http.get(Uri.parse( - "${AppEnviroment.baseApiUrl}/mzasni-directory/business-ratings/user/$app_id/$business_id")); + "${AppEnviroment.baseApiUrl}/mzansi-directory/business-ratings/user/$app_id/$business_id")); print(response.statusCode); if (response.statusCode == 200) { String body = response.body; @@ -27,7 +27,7 @@ class MihMzansiDirectoryServices { String business_id, ) async { final response = await http.get(Uri.parse( - "${AppEnviroment.baseApiUrl}/mzasni-directory/business-ratings/all/$business_id")); + "${AppEnviroment.baseApiUrl}/mzansi-directory/business-ratings/all/$business_id")); if (response.statusCode == 200) { Iterable l = jsonDecode(response.body); List businessReviews = List.from( @@ -48,7 +48,7 @@ class MihMzansiDirectoryServices { ) async { var response = await http.post( Uri.parse( - "${AppEnviroment.baseApiUrl}/mzasni-directory/business-rating/insert/"), + "${AppEnviroment.baseApiUrl}/mzansi-directory/business-rating/insert/"), headers: { "Content-Type": "application/json; charset=UTF-8" }, @@ -70,15 +70,22 @@ class MihMzansiDirectoryServices { Future deleteBusinessReview( int idbusiness_ratings, + String business_id, + String rating_score, + String current_rating, ) async { var response = await http.delete( Uri.parse( - "${AppEnviroment.baseApiUrl}/mzasni-directory/business-ratng/delete/"), + "${AppEnviroment.baseApiUrl}/mzansi-directory/business-rating/delete/"), headers: { "Content-Type": "application/json; charset=UTF-8" }, - body: jsonEncode( - {"idbusiness_ratings": idbusiness_ratings}), + body: jsonEncode({ + "idbusiness_ratings": idbusiness_ratings, + "business_id": business_id, + "rating_score": rating_score, + "current_rating": current_rating, + }), ); if (response.statusCode == 200) { return response.statusCode; @@ -89,21 +96,27 @@ class MihMzansiDirectoryServices { Future updateBusinessReview( int idbusiness_ratings, + String business_id, String rating_title, String rating_description, - String rating_score, + String rating_new_score, + String rating_old_score, + String current_rating, ) async { var response = await http.put( Uri.parse( - "${AppEnviroment.baseApiUrl}/mzasni-directory/business-rating/update/"), + "${AppEnviroment.baseApiUrl}/mzansi-directory/business-rating/update/"), headers: { "Content-Type": "application/json; charset=UTF-8" }, body: jsonEncode({ "idbusiness_ratings": idbusiness_ratings, + "business_id": business_id, "rating_title": rating_title, "rating_description": rating_description, - "rating_score": rating_score, + "rating_new_score": rating_new_score, + "rating_old_score": rating_old_score, + "current_rating": current_rating, }), ); if (response.statusCode == 200) { diff --git a/backend/routers/mzansi_directory.py b/backend/routers/mzansi_directory.py index 37a2d2a9..1ad55797 100644 --- a/backend/routers/mzansi_directory.py +++ b/backend/routers/mzansi_directory.py @@ -17,18 +17,25 @@ class BusinessRatingInsertRequest(BaseModel): business_id: str rating_title: str rating_description: str - rating_score: int + rating_score: str + current_rating: str class BusinessRatingDeleteRequest(BaseModel): idbusiness_ratings: int + business_id: str + rating_score: str + current_rating: str class BusinessRatingUpdateRequest(BaseModel): idbusiness_ratings: int + business_id: str rating_title: str rating_description: str - rating_score: str + rating_new_score: str + rating_old_score: str + current_rating: str -@router.get("/mzasni-directory/business-ratings/user/{app_id}/{business_id}", tags=["Mzansi Directory"]) +@router.get("/mzansi-directory/business-ratings/user/{app_id}/{business_id}", tags=["Mzansi Directory"]) async def read_all_ratings_by_business_id(app_id: str,business_id: str, session: SessionContainer = Depends(verify_session())): # , session: SessionContainer = Depends(verify_session()) db = database.dbConnection.dbAllConnect() cursor = db.cursor() @@ -78,7 +85,7 @@ async def read_all_ratings_by_business_id(app_id: str,business_id: str, session: # db.close() # return items[0] -@router.get("/mzasni-directory/business-ratings/all/{business_id}", tags=["Mzansi Directory"]) +@router.get("/mzansi-directory/business-ratings/all/{business_id}", tags=["Mzansi Directory"]) async def read_all_ratings_by_business_id(business_id: str, session: SessionContainer = Depends(verify_session())): # , session: SessionContainer = Depends(verify_session()) db = database.dbConnection.dbAllConnect() cursor = db.cursor() @@ -109,69 +116,121 @@ async def read_all_ratings_by_business_id(business_id: str, session: SessionCont db.close() return items -@router.post("/mzasni-directory/business-rating/insert/", tags=["Mzansi Directory"], status_code=201) +@router.post("/mzansi-directory/business-rating/insert/", tags=["Mzansi Directory"], status_code=201) async def insert_loyalty_card(itemRequest : BusinessRatingInsertRequest): #, session: SessionContainer = Depends(verify_session()) - db = database.dbConnection.dbMzansiDirectoryConnect() + db = database.dbConnection.dbAllConnect() nowDateTime = datetime.now() formatedDateTime = nowDateTime.strftime("%Y-%m-%d %H:%M:%S") cursor = db.cursor() - query = "insert into business_ratings " - query += "(app_id, business_id, rating_title, rating_description, rating_score, date_time) " - query += "values (%s, %s, %s, %s, %s, %s)" - notetData = (itemRequest.app_id, - itemRequest.business_id, - itemRequest.rating_title, - itemRequest.rating_description, - itemRequest.rating_score, - formatedDateTime, - ) try: - cursor.execute(query, notetData) + # Get No Of reviews for business + businessReviewCountQuery = "select count(*) from mzansi_directory.business_ratings where business_ratings.business_id = %s" + countData = (itemRequest.business_id,) + cursor.execute(businessReviewCountQuery, countData) + countResult = cursor.fetchone() + row_count = countResult[0] if countResult else 0 + print(f"Number of rows in business_ratings: {row_count}") + # add business rating + addQuery = "insert into mzansi_directory.business_ratings " + addQuery += "(business_ratings.app_id, business_ratings.business_id, business_ratings.rating_title, business_ratings.rating_description, business_ratings.rating_score, business_ratings.date_time) " + addQuery += "values (%s, %s, %s, %s, %s, %s)" + addQueryData = (itemRequest.app_id, + itemRequest.business_id, + itemRequest.rating_title, + itemRequest.rating_description, + itemRequest.rating_score, + formatedDateTime, + ) + cursor.execute(addQuery, addQueryData) + # Calc New Rating and update business rating + newRating = ((float(itemRequest.current_rating) * row_count) + float(itemRequest.rating_score)) / (row_count + 1) + print(f"New Rating: {newRating}") + updateBusinessQuery = "update app_data.business " + updateBusinessQuery += "set rating = %s " + updateBusinessQuery += "where business_id = %s" + updateBusinessData = (newRating, itemRequest.business_id) + cursor.execute(updateBusinessQuery, updateBusinessData) + db.commit() except Exception as error: print(error) raise HTTPException(status_code=404, detail="Failed to Create Record") # return {"message": error} - db.commit() cursor.close() db.close() return {"message": "Successfully Created Record"} -@router.delete("/mzasni-directory/business-ratng/delete/", tags=["Mzansi Directory"]) +@router.delete("/mzansi-directory/business-rating/delete/", tags=["Mzansi Directory"]) async def Delete_loyalty_card(itemRequest : BusinessRatingDeleteRequest, session: SessionContainer = Depends(verify_session())): #, session: SessionContainer = Depends(verify_session()) - db = database.dbConnection.dbMzansiDirectoryConnect() + db = database.dbConnection.dbAllConnect() cursor = db.cursor() - query = "delete from business_ratings " - query += "where idbusiness_ratings=%s" try: - cursor.execute(query, (str(itemRequest.idbusiness_ratings),)) + # Get No Of reviews for business + businessReviewCountQuery = "select count(*) from mzansi_directory.business_ratings where business_ratings.business_id = %s" + countData = (itemRequest.business_id,) + cursor.execute(businessReviewCountQuery, countData) + countResult = cursor.fetchone() + row_count = countResult[0] if countResult else 0 + print(f"Number of rows in business_ratings: {row_count}") + # Delete business rating + query = "delete from mzansi_directory.business_ratings " + query += "where business_ratings.idbusiness_ratings=%s" + cursor.execute(query, (str(itemRequest.idbusiness_ratings),)) + # Calc New Rating and update business rating + if(row_count <= 1): + newRating = 0.0 + else: + newRating = ((float(itemRequest.current_rating) * row_count) - float(itemRequest.rating_score)) / (row_count - 1) + print(f"New Rating: {newRating}") + updateBusinessQuery = "update app_data.business " + updateBusinessQuery += "set rating = %s " + updateBusinessQuery += "where business_id = %s" + updateBusinessData = (newRating, itemRequest.business_id) + cursor.execute(updateBusinessQuery, updateBusinessData) + db.commit() except Exception as error: print(error) raise HTTPException(status_code=404, detail="Failed to Delete Record") - db.commit() cursor.close() db.close() return {"message": "Successfully deleted Record"} -@router.put("/mzasni-directory/business-rating/update/", tags=["Mzansi Directory"]) +@router.put("/mzansi-directory/business-rating/update/", tags=["Mzansi Directory"]) async def UpdatePatient(itemRequest : BusinessRatingUpdateRequest, session: SessionContainer = Depends(verify_session())): db = database.dbConnection.dbMzansiDirectoryConnect() cursor = db.cursor() nowDateTime = datetime.now() formatedDateTime = nowDateTime.strftime("%Y-%m-%d %H:%M:%S") - query = "update business_ratings " - query += "set rating_title=%s, rating_description=%s, rating_score=%s, date_time=%s " - query += "where idbusiness_ratings=%s" - notetData = (itemRequest.rating_title, + try: + # Get No Of reviews for business + businessReviewCountQuery = "select count(*) from mzansi_directory.business_ratings where business_ratings.business_id = %s" + countData = (itemRequest.business_id,) + cursor.execute(businessReviewCountQuery, countData) + countResult = cursor.fetchone() + row_count = countResult[0] if countResult else 0 + print(f"Number of rows in business_ratings: {row_count}") + # Update business rating + query = "update business_ratings " + query += "set rating_title=%s, rating_description=%s, rating_score=%s, date_time=%s " + query += "where idbusiness_ratings=%s" + notetData = (itemRequest.rating_title, itemRequest.rating_description, - itemRequest.rating_score, + itemRequest.rating_new_score, formatedDateTime, itemRequest.idbusiness_ratings, - ) - try: - cursor.execute(query, notetData) + ) + cursor.execute(query, notetData) + # Calc New Rating and update business rating + # add new rating and old rating params + newRating = ((float(itemRequest.current_rating) * row_count) - float(itemRequest.rating_old_score) + float(itemRequest.rating_new_score)) / (row_count) + print(f"New Rating: {newRating}") + updateBusinessQuery = "update app_data.business " + updateBusinessQuery += "set rating = %s " + updateBusinessQuery += "where business_id = %s" + updateBusinessData = (newRating, itemRequest.business_id) + cursor.execute(updateBusinessQuery, updateBusinessData) + db.commit() except Exception as error: raise HTTPException(status_code=404, detail="Failed to Update Record") - db.commit() cursor.close() db.close() return {"message": "Successfully Updated Record"} \ No newline at end of file From 7f072534a7ead7e3a935d4650dcbffe1e11de5c8 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 23 Jul 2025 09:39:01 +0200 Subject: [PATCH 41/43] update service calls --- .../components/mih_review_business_window.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart index c6ad65a5..2254f42a 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart @@ -83,6 +83,9 @@ class _MihReviewBusinessWindowState extends State { await MihMzansiDirectoryServices() .deleteBusinessReview( widget.businessReview!.idbusiness_ratings, + widget.businessReview!.business_id, + widget.businessReview!.rating_score, + widget.business.rating, ) .then((statusCode) { Navigator.of(context).pop(); //Remove loading dialog @@ -159,9 +162,12 @@ class _MihReviewBusinessWindowState extends State { await MihMzansiDirectoryServices() .updateBusinessReview( widget.businessReview!.idbusiness_ratings, + widget.businessReview!.business_id, _reviewTitleController.text, _reviewDescriptionController.text, _reviewScoreController.text, + widget.businessReview!.rating_score, + widget.business.rating, ) .then((statusCode) { Navigator.of(context).pop(); //Remove loading dialog From 7dceccc170b5903e8a59c27f90e00714c6080a09 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 23 Jul 2025 10:16:00 +0200 Subject: [PATCH 42/43] fix state management --- .../mih_components/mih_objects/arguments.dart | 20 +++++++++++++++++ .../lib/mih_config/mih_routeGenerator.dart | 19 ++++++++-------- .../build_business_search_resultsList.dart | 8 ++++++- .../mzansi_directory/mzansi_directory.dart | 12 ++++++++-- .../package_tiles/mzansi_directory_tile.dart | 6 ++++- .../package_tools/mih_search_mzansi.dart | 22 +++++++++++++++++-- .../components/mih_business_info_card.dart | 3 +++ .../mih_review_business_window.dart | 14 +++++++++++- .../mzansi_business_profile_view.dart | 13 ++++++----- .../package_tools/mih_business_details.dart | 1 + .../mih_business_details_view.dart | 3 +++ .../package_tools/mih_business_reviews.dart | 1 + 12 files changed, 100 insertions(+), 22 deletions(-) diff --git a/Frontend/lib/mih_components/mih_objects/arguments.dart b/Frontend/lib/mih_components/mih_objects/arguments.dart index 5d85ec92..3f1cba68 100644 --- a/Frontend/lib/mih_components/mih_objects/arguments.dart +++ b/Frontend/lib/mih_components/mih_objects/arguments.dart @@ -31,6 +31,16 @@ class BusinessArguments { ); } +class BusinessViewArguments { + final Business business; + final String? startUpSearch; + + BusinessViewArguments( + this.business, + this.startUpSearch, + ); +} + class HomeArguments { final AppUser signedInUser; final BusinessUser? businessUser; @@ -218,6 +228,16 @@ class MzansiAiArguments { ); } +class MzansiDirectoryArguments { + final String? startUpSearch; + final bool personalSearch; + + MzansiDirectoryArguments( + this.startUpSearch, + this.personalSearch, + ); +} + class TestArguments { final AppUser user; final Business? business; diff --git a/Frontend/lib/mih_config/mih_routeGenerator.dart b/Frontend/lib/mih_config/mih_routeGenerator.dart index 130a1c98..8e05353d 100644 --- a/Frontend/lib/mih_config/mih_routeGenerator.dart +++ b/Frontend/lib/mih_config/mih_routeGenerator.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_layout/mih_print_prevew.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_objects/app_user.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_objects/arguments.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_objects/business.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/Example/package_test.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_notification_message.dart'; import 'package:mzansi_innovation_hub/mih_packages/about_mih/about_mih.dart'; @@ -122,13 +121,13 @@ class RouteGenerator { // } // break; // Use break and fall through to _errorRoute if argument type is wrong case AppRoutes.mzansiDirectory: - // if (args is AuthArguments) { - return MaterialPageRoute( - settings: settings, - builder: (_) => MzansiDirectory(), - ); - // } - // break; + if (args is MzansiDirectoryArguments) { + return MaterialPageRoute( + settings: settings, + builder: (_) => MzansiDirectory(arguments: args), + ); + } + break; case AppRoutes.notifications: if (args is NotificationArguments) { return MaterialPageRoute( @@ -188,10 +187,10 @@ class RouteGenerator { break; case AppRoutes.businessProfileView: - if (args is Business) { + if (args is BusinessViewArguments) { return MaterialPageRoute( settings: settings, - builder: (_) => MzansiBusinessProfileView(business: args), + builder: (_) => MzansiBusinessProfileView(arguments: args), ); } break; 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 index 02b918c3..5d8d533a 100644 --- 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 @@ -1,15 +1,18 @@ 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/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; + final String? startUpSearch; const BuildBusinessSearchResultsList({ super.key, required this.businessList, required this.myLocation, + required this.startUpSearch, }); @override @@ -37,7 +40,10 @@ class _BuildBusinessSearchResultsListState onTap: () { Navigator.of(context).pushNamed( '/business-profile/view', - arguments: widget.businessList[index], + arguments: BusinessViewArguments( + widget.businessList[index], + widget.startUpSearch, + ), ); }, splashColor: MzansiInnovationHub.of(context)! diff --git a/Frontend/lib/mih_packages/mzansi_directory/mzansi_directory.dart b/Frontend/lib/mih_packages/mzansi_directory/mzansi_directory.dart index 0706314b..3ddc5e55 100644 --- a/Frontend/lib/mih_packages/mzansi_directory/mzansi_directory.dart +++ b/Frontend/lib/mih_packages/mzansi_directory/mzansi_directory.dart @@ -1,11 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/arguments.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_action.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tools.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_directory/package_tools/mih_search_mzansi.dart'; class MzansiDirectory extends StatefulWidget { - const MzansiDirectory({super.key}); + final MzansiDirectoryArguments arguments; + const MzansiDirectory({ + super.key, + required this.arguments, + }); @override State createState() => _MzansiDirectoryState(); @@ -32,7 +37,10 @@ class _MzansiDirectoryState extends State { List getToolBody() { List toolBodies = [ - MihSearchMzansi(), + MihSearchMzansi( + startUpSearch: widget.arguments.startUpSearch, + personalSearch: widget.arguments.personalSearch, + ), // MihContacts(), // MihFavouriteBusinesses(), ]; diff --git a/Frontend/lib/mih_packages/mzansi_directory/package_tiles/mzansi_directory_tile.dart b/Frontend/lib/mih_packages/mzansi_directory/package_tiles/mzansi_directory_tile.dart index 601ef5d9..d74b46ab 100644 --- a/Frontend/lib/mih_packages/mzansi_directory/package_tiles/mzansi_directory_tile.dart +++ b/Frontend/lib/mih_packages/mzansi_directory/package_tiles/mzansi_directory_tile.dart @@ -1,5 +1,6 @@ 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_package_components/mih_icons.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tile.dart'; @@ -21,7 +22,10 @@ class _MzansiDirectoryTileState extends State { onTap: () { Navigator.of(context).pushNamed( '/mzansi-directory', - // arguments: WalletArguments(widget.signedInUser, 0), + arguments: MzansiDirectoryArguments( + null, // startUpSearch + true, // personalSearch + ), ); }, appName: "Mzansi Directory", 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 846c9b43..dcb4e55f 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 @@ -15,7 +15,13 @@ 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}); + final String? startUpSearch; + final bool personalSearch; + const MihSearchMzansi({ + super.key, + required this.startUpSearch, + required this.personalSearch, + }); @override State createState() => _MihSearchMzansiState(); @@ -24,7 +30,7 @@ class MihSearchMzansi extends StatefulWidget { class _MihSearchMzansiState extends State { final TextEditingController mzansiSearchController = TextEditingController(); final FocusNode searchFocusNode = FocusNode(); - bool userSearch = true; + late bool userSearch; Future?> futureUserSearchResults = Future.value(); Future?> futureBusinessSearchResults = Future.value(); late Future futurePosition = @@ -41,6 +47,17 @@ class _MihSearchMzansiState extends State { @override void initState() { super.initState(); + setState(() { + userSearch = widget.personalSearch; + mzansiSearchController.text = widget.startUpSearch ?? ""; + if (userSearch) { + futureUserSearchResults = + MihUserServices().searchUsers(mzansiSearchController.text, context); + } else { + futureBusinessSearchResults = MihBusinessDetailsServices() + .searchBusinesses(mzansiSearchController.text, context); + } + }); } @override @@ -246,6 +263,7 @@ class _MihSearchMzansiState extends State { BuildBusinessSearchResultsList( businessList: snapshot.requireData!, myLocation: myLocation, + startUpSearch: mzansiSearchController.text, ), ], ); diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart index 94fe27b5..c0137b42 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart @@ -12,6 +12,7 @@ import 'package:url_launcher/url_launcher.dart'; class MihBusinessCard extends StatefulWidget { final Business business; + final String? startUpSearch; // final String businessid; // final String businessName; // final String cellNumber; @@ -23,6 +24,7 @@ class MihBusinessCard extends StatefulWidget { const MihBusinessCard({ super.key, required this.business, + required this.startUpSearch, // required this.businessid, // required this.businessName, // required this.cellNumber, @@ -518,6 +520,7 @@ class _MihBusinessCardState extends State { businessReview: asyncSnapshot.data, screenWidth: width, readOnly: false, + startUpSearch: widget.startUpSearch, ); } else { return MihPackageAlert( diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart index 2254f42a..285b1215 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart @@ -2,6 +2,7 @@ import 'package:custom_rating_bar/custom_rating_bar.dart'; import 'package:flutter/material.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.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/business.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_objects/business_review.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_button.dart'; @@ -21,12 +22,14 @@ class MihReviewBusinessWindow extends StatefulWidget { final BusinessReview? businessReview; final double screenWidth; final bool readOnly; + final String? startUpSearch; const MihReviewBusinessWindow({ super.key, required this.business, required this.businessReview, required this.screenWidth, required this.readOnly, + required this.startUpSearch, }); @override @@ -172,7 +175,16 @@ class _MihReviewBusinessWindowState extends State { .then((statusCode) { Navigator.of(context).pop(); //Remove loading dialog if (statusCode == 200) { - Navigator.of(context).pop(); + Navigator.of(context).pop(); //pop window + Navigator.of(context).pop(); //pop business profile + Navigator.of(context).pop(); //pop directory + Navigator.of(context).pushNamed( + '/mzansi-directory', + arguments: MzansiDirectoryArguments( + widget.startUpSearch, // startUpSearch + false, // personalSearch + ), + ); MihAlertServices().successAlert( "Successfully Updated Review!", "Your review has successfully been updated and will now appear under the business.", diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart index 164863cb..fdba7344 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart @@ -1,4 +1,4 @@ -import 'package:mzansi_innovation_hub/mih_components/mih_objects/business.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/arguments.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_action.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tools.dart'; @@ -7,10 +7,10 @@ import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profi import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart'; class MzansiBusinessProfileView extends StatefulWidget { - final Business business; + final BusinessViewArguments arguments; const MzansiBusinessProfileView({ super.key, - required this.business, + required this.arguments, }); @override @@ -68,8 +68,11 @@ class _MzansiBusinessProfileViewState extends State { List getToolBody() { List toolBodies = [ - MihBusinessDetailsView(business: widget.business), - MihBusinessReviews(business: widget.business), + MihBusinessDetailsView( + business: widget.arguments.business, + startUpSearch: widget.arguments.startUpSearch, + ), + MihBusinessReviews(business: widget.arguments.business), ]; return toolBodies; } diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart index acd79c0a..1a7c6c1c 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart @@ -690,6 +690,7 @@ class _MihBusinessDetailsState extends State { // : 0, // website: widget.arguments.business!.website, business: widget.arguments.business!, + startUpSearch: null, width: width, ), ), diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart index fb182804..9c34764a 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart @@ -11,9 +11,11 @@ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_ class MihBusinessDetailsView extends StatefulWidget { final Business business; + final String? startUpSearch; const MihBusinessDetailsView({ super.key, required this.business, + required this.startUpSearch, }); @override @@ -163,6 +165,7 @@ class _MihBusinessDetailsViewState extends State { width: 700, child: MihBusinessCard( business: widget.business, + startUpSearch: widget.startUpSearch, // businessid: widget.business.business_id, // businessName: widget.business.Name, // cellNumber: widget.business.contact_no, diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart index fd4b3ad2..dbf8c871 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_reviews.dart @@ -39,6 +39,7 @@ class _MihBusinessReviewsState extends State { businessReview: businessReview, screenWidth: width, readOnly: true, + startUpSearch: null, ); }, ); From 4430c3f0f09e1d7a80a3907cc8ff4646af09a503 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Wed, 23 Jul 2025 10:34:13 +0200 Subject: [PATCH 43/43] magae state badly --- .../build_business_search_resultsList.dart | 2 +- .../mih_review_business_window.dart | 96 ++++++++++++++----- 2 files changed, 71 insertions(+), 27 deletions(-) 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 index 5d8d533a..c9ac8668 100644 --- 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 @@ -42,7 +42,7 @@ class _BuildBusinessSearchResultsListState '/business-profile/view', arguments: BusinessViewArguments( widget.businessList[index], - widget.startUpSearch, + widget.businessList[index].Name, ), ); }, diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart index 285b1215..c1f02028 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_review_business_window.dart @@ -94,7 +94,16 @@ class _MihReviewBusinessWindowState extends State { Navigator.of(context).pop(); //Remove loading dialog Navigator.of(context).pop(); //Remove delete dialog if (statusCode == 200) { - Navigator.of(context).pop(); + Navigator.of(context).pop(); //Remove window + Navigator.of(context).pop(); //Remove profile + Navigator.of(context).pop(); //Remove directory + Navigator.of(context).pushNamed( + '/mzansi-directory', + arguments: MzansiDirectoryArguments( + widget.startUpSearch, // startUpSearch + false, // personalSearch + ), + ); MihAlertServices().successAlert( "Successfully Deleted Review!", "Your review has successfully been delete and will no longer appear under the business.", @@ -211,7 +220,16 @@ class _MihReviewBusinessWindowState extends State { .then((statusCode) { Navigator.of(context).pop(); //Remove loading dialog if (statusCode == 201) { - Navigator.of(context).pop(); + Navigator.of(context).pop(); // pop window + Navigator.of(context).pop(); // pop business profile + Navigator.of(context).pop(); // pop directory + Navigator.of(context).pushNamed( + '/mzansi-directory', + arguments: MzansiDirectoryArguments( + widget.startUpSearch, // startUpSearch + false, // personalSearch + ), + ); MihAlertServices().successAlert( "Successfully Added Review!", "Your review has successfully been added and will now appear under the business.", @@ -333,30 +351,56 @@ class _MihReviewBusinessWindowState extends State { ], ), const SizedBox(height: 4), - RatingBar( - size: 50, - alignment: Alignment.centerLeft, - filledIcon: Icons.star, - emptyIcon: Icons.star_border, - halfFilledIcon: Icons.star_half, - filledColor: - MzansiInnovationHub.of(context)!.theme.secondaryColor(), - emptyColor: - MzansiInnovationHub.of(context)!.theme.secondaryColor(), - halfFilledColor: - MzansiInnovationHub.of(context)!.theme.secondaryColor(), - isHalfAllowed: true, - initialRating: widget.businessReview != null - ? double.parse(_reviewScoreController.text) - : 1, - maxRating: 5, - onRatingChanged: (double) { - setState(() { - _reviewScoreController.text = double.toStringAsFixed(1); - }); - print(_reviewScoreController.text); - }, - ), + widget.readOnly + ? RatingBar.readOnly( + size: 50, + alignment: Alignment.centerLeft, + filledIcon: Icons.star, + emptyIcon: Icons.star_border, + halfFilledIcon: Icons.star_half, + filledColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + emptyColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + halfFilledColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + isHalfAllowed: true, + initialRating: widget.businessReview != null + ? double.parse(_reviewScoreController.text) + : 1, + maxRating: 5, + ) + : RatingBar( + size: 50, + alignment: Alignment.centerLeft, + filledIcon: Icons.star, + emptyIcon: Icons.star_border, + halfFilledIcon: Icons.star_half, + filledColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + emptyColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + halfFilledColor: MzansiInnovationHub.of(context)! + .theme + .secondaryColor(), + isHalfAllowed: true, + initialRating: widget.businessReview != null + ? double.parse(_reviewScoreController.text) + : 1, + maxRating: 5, + onRatingChanged: (double) { + setState(() { + _reviewScoreController.text = + double.toStringAsFixed(1); + }); + print(_reviewScoreController.text); + }, + ), Visibility( visible: widget.readOnly, child: const SizedBox(height: 10),