diff --git a/Frontend/lib/mih_components/mih_objects/app_user.dart b/Frontend/lib/mih_components/mih_objects/app_user.dart index ca98edbf..94a00a7d 100644 --- a/Frontend/lib/mih_components/mih_objects/app_user.dart +++ b/Frontend/lib/mih_components/mih_objects/app_user.dart @@ -8,6 +8,7 @@ class AppUser { final String app_id; final String username; final String pro_pic_path; + final String purpose; const AppUser( this.idUser, @@ -18,6 +19,7 @@ class AppUser { this.app_id, this.username, this.pro_pic_path, + this.purpose, ); factory AppUser.fromJson(dynamic json) { @@ -30,6 +32,7 @@ class AppUser { json['app_id'], json['username'], json['pro_pic_path'], + json['purpose'], ); } } diff --git a/Frontend/lib/mih_components/mih_objects/arguments.dart b/Frontend/lib/mih_components/mih_objects/arguments.dart index 3e4d82e3..5d85ec92 100644 --- a/Frontend/lib/mih_components/mih_objects/arguments.dart +++ b/Frontend/lib/mih_components/mih_objects/arguments.dart @@ -217,3 +217,13 @@ class MzansiAiArguments { this.startUpQuestion, ); } + +class TestArguments { + final AppUser user; + final Business? business; + + TestArguments( + this.user, + this.business, + ); +} diff --git a/Frontend/lib/mih_components/mih_objects/business.dart b/Frontend/lib/mih_components/mih_objects/business.dart index cafded10..2939ba24 100644 --- a/Frontend/lib/mih_components/mih_objects/business.dart +++ b/Frontend/lib/mih_components/mih_objects/business.dart @@ -12,6 +12,9 @@ class Business { final String gps_location; final String practice_no; final String vat_no; + final String website; + final String rating; + final String mission_vision; const Business( this.business_id, @@ -26,6 +29,9 @@ class Business { this.gps_location, this.practice_no, this.vat_no, + this.website, + this.rating, + this.mission_vision, ); factory Business.fromJson(dynamic json) { @@ -42,6 +48,9 @@ class Business { json['gps_location'], json['practice_no'], json['vat_no'], + json['website'], + json['rating'], + json['mission_vision'], ); } } diff --git a/Frontend/lib/mih_components/mih_package_components/Example/package_test.dart b/Frontend/lib/mih_components/mih_package_components/Example/package_test.dart index 20f4a0a1..e54b27e8 100644 --- a/Frontend/lib/mih_components/mih_package_components/Example/package_test.dart +++ b/Frontend/lib/mih_components/mih_package_components/Example/package_test.dart @@ -9,7 +9,15 @@ import 'package:mzansi_innovation_hub/mih_components/mih_objects/arguments.dart' import 'package:flutter/material.dart'; class PackageTest extends StatefulWidget { - const PackageTest({super.key}); + // final AppUser user; + // final Business business; + final TestArguments arguments; + const PackageTest({ + super.key, + required this.arguments, + // required this.user, + // required this.business, + }); @override State createState() => _PackageTestState(); @@ -111,7 +119,10 @@ class _PackageTestState extends State { List getToolBody() { List toolBodies = [ - const PackageToolOne(), + PackageToolOne( + user: widget.arguments.user, + business: widget.arguments.business!, + ), const PackageToolTwo(), ]; return toolBodies; 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 351c9e83..9b5c6c8f 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 @@ -1,10 +1,17 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart'; +import 'package:geolocator/geolocator.dart'; import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/app_user.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/business.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_banner_ad.dart'; -import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/components/mih_business_card.dart'; +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_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'; 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_button.dart'; @@ -25,7 +32,13 @@ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_toggle.dart'; class PackageToolOne extends StatefulWidget { - const PackageToolOne({super.key}); + final AppUser user; + final Business business; + const PackageToolOne({ + super.key, + required this.user, + required this.business, + }); @override State createState() => _PackageToolOneState(); @@ -51,6 +64,8 @@ class _PackageToolOneState extends State { bool switchpositioin = true; final FocusNode searchFocusNode = FocusNode(); final _formKey = GlobalKey(); + late Future myCoordinates; + String myLocation = ""; void showTestFullWindow() { showDialog( @@ -133,6 +148,8 @@ class _PackageToolOneState extends State { // const NetworkImage( // "https://lh3.googleusercontent.com/nW4ZZ89Q1ATz7Ht3nsAVWXL_cwNi4gNusqQZiL60UuuI3FG-VM7bTYDoJ-sUr2kDTdorfQYjxo5PjDM-0MO5rA=s512"); }); + + // myCoordinates = MIHLocationAPI().getGPSPosition(context); } Widget getBody(double width) { @@ -166,6 +183,114 @@ class _PackageToolOneState extends State { ], ), const SizedBox(height: 20), + Center( + child: MihButton( + onPressed: () { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle( + message: "Getting your profile data", + ); + }, + ); + }, + buttonColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + elevation: 10, + width: 300, + child: Text( + "Show Loading", + style: TextStyle( + color: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Personal Preview", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ], + ), + const SizedBox(height: 10), + MihPersonalProfilePreview( + user: widget.user, + ), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "Business Preview", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ], + ), + const SizedBox(height: 10), + FutureBuilder( + future: MIHLocationAPI().getGPSPosition(context), + builder: (context, asyncSnapshot) { + // print(asyncSnapshot.connectionState); + if (asyncSnapshot.connectionState == + ConnectionState.waiting) { + // return MihBusinessProfilePreview( + // business: widget.business, + // myLocation: null, + // ).redacted( + // context: context, + // redact: true, + // ); + return Container( + width: 150, + height: 50, + // color: Colors.black, + child: Center(child: CircularProgressIndicator()), + ); + } else if (asyncSnapshot.hasError || + !asyncSnapshot.hasData || + asyncSnapshot.data == null) { + return Container( + width: 150, + height: 50, + color: Colors.red, + child: Center(child: Text("Location unavailable")), + ); + } else { + final myLocation = asyncSnapshot.data + .toString() + .replaceAll("Latitude: ", "") + .replaceAll("Longitude: ", ""); + print("My Location is this: $myLocation"); + return MihBusinessProfilePreview( + business: widget.business, + myLocation: myLocation, + ); + } + }), + const SizedBox(height: 10), MihBusinessCard( businessName: "Mzansi Innovation Hub", cellNumber: "0788300006", @@ -361,6 +486,10 @@ class _PackageToolOneState extends State { multiLineInput: true, requiredText: false, hintText: "Enter Multi Line Text", + validator: (value) { + return MihValidationServices() + .validateLength(_textFieldFourController.text, 50); + }, ), const SizedBox(height: 20), Align( diff --git a/Frontend/lib/mih_components/mih_package_components/assets/fonts/Mih_Icons.ttf b/Frontend/lib/mih_components/mih_package_components/assets/fonts/Mih_Icons.ttf index 29479c3b..71c04e15 100644 Binary files a/Frontend/lib/mih_components/mih_package_components/assets/fonts/Mih_Icons.ttf and b/Frontend/lib/mih_components/mih_package_components/assets/fonts/Mih_Icons.ttf differ diff --git a/Frontend/lib/mih_components/mih_package_components/assets/fonts/style.css b/Frontend/lib/mih_components/mih_package_components/assets/fonts/style.css index 8a7cac9c..97fe7b5a 100644 --- a/Frontend/lib/mih_components/mih_package_components/assets/fonts/style.css +++ b/Frontend/lib/mih_components/mih_package_components/assets/fonts/style.css @@ -1,10 +1,10 @@ @font-face { font-family: 'Mih_Icons'; - src: url('fonts/Mih_Icons.eot?kmk862'); - src: url('fonts/Mih_Icons.eot?kmk862#iefix') format('embedded-opentype'), - url('fonts/Mih_Icons.ttf?kmk862') format('truetype'), - url('fonts/Mih_Icons.woff?kmk862') format('woff'), - url('fonts/Mih_Icons.svg?kmk862#Mih_Icons') format('svg'); + src: url('fonts/Mih_Icons.eot?blbuxz'); + src: url('fonts/Mih_Icons.eot?blbuxz#iefix') format('embedded-opentype'), + url('fonts/Mih_Icons.ttf?blbuxz') format('truetype'), + url('fonts/Mih_Icons.woff?blbuxz') format('woff'), + url('fonts/Mih_Icons.svg?blbuxz#Mih_Icons') format('svg'); font-weight: normal; font-style: normal; font-display: block; @@ -26,66 +26,70 @@ -moz-osx-font-smoothing: grayscale; } -.icon-personal_profile:before { - content: "\e90f"; -} - -.icon-notifications:before { - content: "\e90e"; -} - -.icon-i_dont_know:before { - content: "\e90d"; -} - -.icon-business_setup:before { - content: "\e90b"; -} - -.icon-profile_setup:before { - content: "\e90c"; -} - -.icon-mih_ring:before { - content: "\e90a"; -} - -.icon-about_mih:before { +.icon-mzansi_directory:before { content: "\e900"; } -.icon-access_control:before { +.icon-personal_profile:before { content: "\e901"; } -.icon-business_profile:before { +.icon-about_mih:before { content: "\e902"; } -.icon-calculator:before { +.icon-access_control:before { content: "\e903"; } -.icon-calendar:before { +.icon-business_profile:before { content: "\e904"; } -.icon-mih_logo:before { +.icon-business_setup:before { content: "\e905"; } -.icon-mzansi_ai:before { +.icon-i_dont_know:before { content: "\e906"; } -.icon-mzansi_wallet:before { +.icon-mih_logo:before { content: "\e907"; } -.icon-patient_manager:before { +.icon-mih_ring:before { content: "\e908"; } -.icon-patient_profile:before { +.icon-mzansi_ai:before { content: "\e909"; +} + +.icon-mzansi_wallet:before { + content: "\e90a"; +} + +.icon-notifications:before { + content: "\e90b"; +} + +.icon-patient_manager:before { + content: "\e90c"; +} + +.icon-patient_profile:before { + content: "\e90d"; +} + +.icon-profile_setup:before { + content: "\e90e"; +} + +.icon-calculator:before { + content: "\e940"; +} + +.icon-calendar:before { + content: "\e953"; } \ No newline at end of file diff --git a/Frontend/lib/mih_components/mih_package_components/mih_banner_ad.dart b/Frontend/lib/mih_components/mih_package_components/mih_banner_ad.dart index b84bc754..d8c60c87 100644 --- a/Frontend/lib/mih_components/mih_package_components/mih_banner_ad.dart +++ b/Frontend/lib/mih_components/mih_package_components/mih_banner_ad.dart @@ -30,7 +30,8 @@ class _MihBannerAdState extends State { onAdFailedToLoad: (ad, err) { debugPrint('BannerAd failed to load: $err'); setState(() { - errorMessage = 'Failed to load ad: ${err.message}'; + errorMessage = + 'Failed to load ad- Message: ${err.message} Code :${err.code}'; }); ad.dispose(); // Dispose the ad to free resources }, diff --git a/Frontend/lib/mih_components/mih_package_components/mih_business_profile_preview.dart b/Frontend/lib/mih_components/mih_package_components/mih_business_profile_preview.dart new file mode 100644 index 00000000..8cba8112 --- /dev/null +++ b/Frontend/lib/mih_components/mih_package_components/mih_business_profile_preview.dart @@ -0,0 +1,112 @@ +import 'package:file_picker/file_picker.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_package_components/mih_circle_avatar.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_icons.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_location_services.dart'; + +class MihBusinessProfilePreview extends StatefulWidget { + final Business business; + final String? myLocation; + const MihBusinessProfilePreview({ + super.key, + required this.business, + required this.myLocation, + }); + + @override + State createState() => + _MihBusinessProfilePreviewState(); +} + +class _MihBusinessProfilePreviewState extends State { + late Future futureImageUrl; + PlatformFile? file; + + String calculateDistance() { + double distanceInKm = MIHLocationAPI().getDistanceInMeaters( + widget.myLocation!, widget.business.gps_location) / + 1000; + return "${distanceInKm.toStringAsFixed(2)} km"; + } + + @override + void initState() { + super.initState(); + futureImageUrl = + MihFileApi.getMinioFileUrl(widget.business.logo_path, context); + } + + @override + Widget build(BuildContext context) { + double profilePictureWidth = 60; + return Row( + children: [ + FutureBuilder( + future: futureImageUrl, + builder: (context, asyncSnapshot) { + if (asyncSnapshot.connectionState == ConnectionState.done && + asyncSnapshot.hasData) { + if (asyncSnapshot.requireData != "") { + return MihCircleAvatar( + imageFile: NetworkImage(asyncSnapshot.requireData), + width: profilePictureWidth, + editable: false, + fileNameController: TextEditingController(), + userSelectedfile: file, + frameColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + backgroundColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + onChange: () {}, + ); + } else { + return Icon( + MihIcons.iDontKnow, + size: profilePictureWidth, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ); + } + } else { + return Icon( + MihIcons.mihRing, + size: profilePictureWidth, + ); + } + }), + const SizedBox(width: 15), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + widget.business.Name, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + Text( + widget.business.type, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 15, + ), + ), + Text( + widget.myLocation != null || widget.myLocation!.isEmpty + ? calculateDistance() + : "0.00 km", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 10, + ), + ), + ], + ) + ], + ); + } +} diff --git a/Frontend/lib/mih_components/mih_package_components/mih_circle_avatar.dart b/Frontend/lib/mih_components/mih_package_components/mih_circle_avatar.dart index 25c0cee5..d076c49b 100644 --- a/Frontend/lib/mih_components/mih_package_components/mih_circle_avatar.dart +++ b/Frontend/lib/mih_components/mih_package_components/mih_circle_avatar.dart @@ -34,17 +34,18 @@ class _MihCircleAvatarState extends State { late ImageProvider? imagePreview; ImageProvider? getAvatar() { - Color dark = const Color(0XFF3A4454); + // Color dark = const Color(0XFF3A4454); if (widget.imageFile == null) { - if (widget.backgroundColor == dark) { - print("here in light icon"); - return const AssetImage( - 'lib/mih_components/mih_package_components/assets/images/i-dont-know-light.png'); - } else { - print("here in dark icon"); - return const AssetImage( - 'lib/mih_components/mih_package_components/assets/images/i-dont-know-dark.png'); - } + return null; + // if (widget.backgroundColor == dark) { + // print("here in light icon"); + // return const AssetImage( + // 'lib/mih_components/mih_package_components/assets/images/i-dont-know-light.png'); + // } else { + // print("here in dark icon"); + // return const AssetImage( + // 'lib/mih_components/mih_package_components/assets/images/i-dont-know-dark.png'); + // } } else { return widget.imageFile; } @@ -69,16 +70,30 @@ class _MihCircleAvatarState extends State { child: Stack( alignment: Alignment.center, children: [ - CircleAvatar( - radius: widget.width / 2.2, - backgroundColor: widget.backgroundColor, - backgroundImage: imagePreview, + Visibility( + visible: imagePreview != null, + child: CircleAvatar( + radius: widget.width / 2.2, + backgroundColor: widget.backgroundColor, + backgroundImage: imagePreview, + ), ), - FittedBox( - fit: BoxFit.fill, + Visibility( + visible: imagePreview != null, + child: FittedBox( + fit: BoxFit.fill, + child: Icon( + size: widget.width, + MihIcons.mihRing, + color: widget.frameColor, + ), + ), + ), + Visibility( + visible: imagePreview == null, child: Icon( + MihIcons.iDontKnow, size: widget.width, - MihIcons.mihRing, color: widget.frameColor, ), ), diff --git a/Frontend/lib/mih_components/mih_package_components/mih_icons.dart b/Frontend/lib/mih_components/mih_package_components/mih_icons.dart index 86f5c6ad..bfd4a9ac 100644 --- a/Frontend/lib/mih_components/mih_package_components/mih_icons.dart +++ b/Frontend/lib/mih_components/mih_package_components/mih_icons.dart @@ -11,48 +11,54 @@ class MihIcons { // IconData constants based on your style.css file // Note: We convert the hex code from CSS (\eXXX) to an integer (0xeXXX) - static const IconData aboutMih = + static const IconData mzansiDirectory = IconData(0xe900, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData accessControl = + static const IconData personalProfile = IconData(0xe901, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData businessProfile = + static const IconData aboutMih = IconData(0xe902, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData calculator = + static const IconData accessControl = IconData(0xe903, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData calendar = + static const IconData businessProfile = IconData(0xe904, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData mihLogo = + static const IconData businessSetup = IconData(0xe905, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData mzansiAi = + static const IconData iDontKnow = IconData(0xe906, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData mzansiWallet = + static const IconData mihLogo = IconData(0xe907, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData patientManager = + static const IconData mihRing = IconData(0xe908, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData patientProfile = + static const IconData mzansiAi = IconData(0xe909, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData mihRing = + static const IconData mzansiWallet = IconData(0xe90a, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData profileSetup = - IconData(0xe90c, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - - static const IconData businessSetup = + static const IconData notifications = IconData(0xe90b, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData notifications = + static const IconData patientManager = + IconData(0xe90c, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); + + static const IconData patientProfile = + IconData(0xe90d, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); + + static const IconData profileSetup = IconData(0xe90e, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); - static const IconData personalProfile = - IconData(0xe90f, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); + static const IconData calculator = + IconData(0xe940, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); + + static const IconData calendar = + IconData(0xe953, fontFamily: _mihFontFam, fontPackage: _mihFontPkg); } diff --git a/Frontend/lib/mih_components/mih_package_components/mih_personal_profile_preview.dart b/Frontend/lib/mih_components/mih_package_components/mih_personal_profile_preview.dart new file mode 100644 index 00000000..8e7ee029 --- /dev/null +++ b/Frontend/lib/mih_components/mih_package_components/mih_personal_profile_preview.dart @@ -0,0 +1,107 @@ +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/app_user.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_circle_avatar.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_icons.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart'; + +class MihPersonalProfilePreview extends StatefulWidget { + final AppUser user; + const MihPersonalProfilePreview({ + super.key, + required this.user, + }); + + @override + State createState() => + _MihPersonalProfilePreviewState(); +} + +class _MihPersonalProfilePreviewState extends State { + late Future futureImageUrl; + // String imageUrl = ""; + PlatformFile? file; + + @override + void initState() { + super.initState(); + futureImageUrl = + MihFileApi.getMinioFileUrl(widget.user.pro_pic_path, context); + } + + @override + Widget build(BuildContext context) { + double profilePictureWidth = 60; + return Row( + children: [ + FutureBuilder( + future: futureImageUrl, + builder: (context, asyncSnapshot) { + if (asyncSnapshot.connectionState == ConnectionState.done && + asyncSnapshot.hasData) { + if (asyncSnapshot.requireData != "") { + return MihCircleAvatar( + imageFile: NetworkImage(asyncSnapshot.requireData), + width: profilePictureWidth, + editable: false, + fileNameController: TextEditingController(), + userSelectedfile: file, + frameColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + backgroundColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + onChange: () {}, + ); + } else { + return Icon( + MihIcons.iDontKnow, + size: profilePictureWidth, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ); + } + } else { + return Icon( + MihIcons.mihRing, + size: profilePictureWidth, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ); + } + }, + ), + const SizedBox(width: 15), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + widget.user.username.isNotEmpty + ? widget.user.username + : "Username", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + Text( + widget.user.fname.isNotEmpty + ? "${widget.user.fname} ${widget.user.lname}" + : "Name Surname", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 15, + ), + ), + Text( + widget.user.type.toUpperCase(), + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 10, + ), + ), + ], + ) + ], + ); + } +} diff --git a/Frontend/lib/mih_components/mih_package_components/mih_text_form_field.dart b/Frontend/lib/mih_components/mih_package_components/mih_text_form_field.dart index d276d33e..986b1d21 100644 --- a/Frontend/lib/mih_components/mih_package_components/mih_text_form_field.dart +++ b/Frontend/lib/mih_components/mih_package_components/mih_text_form_field.dart @@ -87,10 +87,12 @@ class _MihTextFormFieldState extends State { @override Widget build(BuildContext context) { + final isMultiline = widget.multiLineInput == true; return Center( child: SizedBox( width: widget.width, - height: widget.height, + // height: widget.height, + height: isMultiline ? null : widget.height, child: Theme( data: Theme.of(context).copyWith( textSelectionTheme: TextSelectionThemeData( diff --git a/Frontend/lib/mih_components/mih_package_components/mih_toggle.dart b/Frontend/lib/mih_components/mih_package_components/mih_toggle.dart index 2b113a90..0dd660f6 100644 --- a/Frontend/lib/mih_components/mih_package_components/mih_toggle.dart +++ b/Frontend/lib/mih_components/mih_package_components/mih_toggle.dart @@ -25,12 +25,20 @@ class MihToggle extends StatefulWidget { class _MihToggleState extends State { late bool togglePosition; + @override + void didUpdateWidget(covariant MihToggle oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.initialPostion != oldWidget.initialPostion) { + setState(() { + togglePosition = widget.initialPostion; + }); + } + } + @override void initState() { super.initState(); - setState(() { - togglePosition = widget.initialPostion; - }); + togglePosition = widget.initialPostion; } @override @@ -51,7 +59,7 @@ class _MihToggleState extends State { ), const SizedBox(width: 10), Switch( - value: widget.initialPostion, + value: togglePosition, trackOutlineColor: WidgetStateProperty.resolveWith( (states) { if (widget.readOnly == true) { @@ -81,7 +89,15 @@ class _MihToggleState extends State { // activeTrackColor: widget.fillColor, // inactiveThumbColor: widget.fillColor, // inactiveTrackColor: widget.secondaryFillColor, - onChanged: widget.readOnly != true ? widget.onChange : null, + // onChanged: widget.readOnly != true ? widget.onChange : null, + onChanged: widget.readOnly != true + ? (newValue) { + setState(() { + togglePosition = newValue; // Update internal state + }); + widget.onChange(newValue); // Call the parent's onChange + } + : null, ), ], ); diff --git a/Frontend/lib/mih_components/mih_pop_up_messages/mih_loading_circle.dart b/Frontend/lib/mih_components/mih_pop_up_messages/mih_loading_circle.dart index 8d61a660..98206878 100644 --- a/Frontend/lib/mih_components/mih_pop_up_messages/mih_loading_circle.dart +++ b/Frontend/lib/mih_components/mih_pop_up_messages/mih_loading_circle.dart @@ -3,7 +3,8 @@ import '../../main.dart'; import 'package:gif_view/gif_view.dart'; class Mihloadingcircle extends StatefulWidget { - const Mihloadingcircle({super.key}); + final String? message; + const Mihloadingcircle({super.key, this.message}); @override State createState() => _MihloadingcircleState(); @@ -50,23 +51,45 @@ class _MihloadingcircleState extends State { @override Widget build(BuildContext context) { return Dialog( - child: Container( - padding: EdgeInsets.all(popUpPaddingSize), - width: 250, - height: 250, - decoration: BoxDecoration( - color: MzanziInnovationHub.of(context)!.theme.primaryColor(), - borderRadius: BorderRadius.circular(25.0), - border: Border.all( + child: IntrinsicWidth( + child: IntrinsicHeight( + child: Container( + padding: EdgeInsets.all(popUpPaddingSize), + // width: 250, + // height: 275, + decoration: BoxDecoration( color: MzanziInnovationHub.of(context)!.theme.primaryColor(), - width: 5.0), - ), - child: GifView.asset( - MzanziInnovationHub.of(context)!.theme.loadingImageLocation(), - height: 200, - width: 200, - frameRate: 30, - )), + borderRadius: BorderRadius.circular(25.0), + border: Border.all( + color: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + width: 5.0), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + GifView.asset( + MzanziInnovationHub.of(context)! + .theme + .loadingImageLocation(), + height: 200, + width: 200, + frameRate: 30, + ), + widget.message != null + ? Text( + widget.message!, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ) + : SizedBox(), + ], + )), + ), + ), ); } } diff --git a/Frontend/lib/mih_config/mih_routeGenerator.dart b/Frontend/lib/mih_config/mih_routeGenerator.dart index cc20eb9f..130a1c98 100644 --- a/Frontend/lib/mih_config/mih_routeGenerator.dart +++ b/Frontend/lib/mih_config/mih_routeGenerator.dart @@ -2,6 +2,7 @@ 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'; @@ -15,9 +16,12 @@ import 'package:mzansi_innovation_hub/mih_packages/calculator/mih_calculator.dar import 'package:mzansi_innovation_hub/mih_packages/calendar/mzansi_calendar.dart'; import 'package:mzansi_innovation_hub/mih_packages/mih_authentication/mih_authentication.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_ai/mzansi_ai.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_directory/mzansi_directory.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/mzansi_business_profile.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/profile_business_add.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/mzansi_profile.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/mzansi_profile_view.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_wallet/components/mih_barcode_scanner.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_wallet/mih_wallet.dart'; import 'package:mzansi_innovation_hub/mih_packages/patient_profile/pat_manager/pat_manager.dart'; @@ -44,16 +48,18 @@ class AppRoutes { static const String forgotPassword = '/forgot-password'; static const String aboutMih = '/about'; static const String mzansiProfile = '/mzansi-profile'; + static const String mzansiProfileView = '/mzansi-profile/view'; static const String businessProfileSetup = '/business-profile/set-up'; static const String businessProfileManage = '/business-profile/manage'; + static const String businessProfileView = '/business-profile/view'; static const String patientProfile = '/patient-profile'; static const String patientProfileSetup = '/patient-profile/set-up'; static const String patientProfileEdit = '/patient-profile/edit'; static const String mzansiWallet = '/mzansi-wallet'; + static const String mzansiDirectory = '/mzansi-directory'; static const String mihAccess = '/mih-access'; static const String calendar = '/calendar'; - static const String appointments = - '/appointments'; // Consider unifying with /calendar + static const String appointments = '/appointments'; static const String patientManager = '/patient-manager'; static const String patientManagerPatient = '/patient-manager/patient'; static const String fileViewer = '/file-veiwer'; @@ -115,6 +121,14 @@ 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; case AppRoutes.notifications: if (args is NotificationArguments) { return MaterialPageRoute( @@ -146,6 +160,15 @@ class RouteGenerator { } break; + case AppRoutes.mzansiProfileView: + if (args is AppUser) { + return MaterialPageRoute( + settings: settings, + builder: (_) => MzansiProfileView(user: args), + ); + } + break; + case AppRoutes.businessProfileSetup: if (args is AppUser) { return MaterialPageRoute( @@ -164,6 +187,15 @@ class RouteGenerator { } break; + case AppRoutes.businessProfileView: + if (args is Business) { + return MaterialPageRoute( + settings: settings, + builder: (_) => MzansiBusinessProfileView(business: args), + ); + } + break; + case AppRoutes.patientProfile: if (args is PatientViewArguments) { return MaterialPageRoute( @@ -288,11 +320,13 @@ class RouteGenerator { break; case AppRoutes.packageDevTest: - // No arguments expected for this test route - return MaterialPageRoute( - settings: settings, - builder: (_) => const PackageTest(), - ); + if (args is TestArguments) { + return MaterialPageRoute( + settings: settings, + builder: (_) => PackageTest(arguments: args), + ); + } + break; default: // If no match is found, fall through to the error route diff --git a/Frontend/lib/mih_packages/about_mih/about_mih.dart b/Frontend/lib/mih_packages/about_mih/about_mih.dart index e71da5d1..0115df4c 100644 --- a/Frontend/lib/mih_packages/about_mih/about_mih.dart +++ b/Frontend/lib/mih_packages/about_mih/about_mih.dart @@ -100,7 +100,7 @@ class _AboutMihState extends State { "About", "Privacy Policy", "Terms of Service", - "Attributes", + "Attributions", ]; return toolTitles; } diff --git a/Frontend/lib/mih_packages/about_mih/package_tools/mih_ attributes.dart b/Frontend/lib/mih_packages/about_mih/package_tools/mih_ attributes.dart index aac1860d..b96de189 100644 --- a/Frontend/lib/mih_packages/about_mih/package_tools/mih_ attributes.dart +++ b/Frontend/lib/mih_packages/about_mih/package_tools/mih_ attributes.dart @@ -99,11 +99,11 @@ class _MihAttributesState extends State { Widget getBody() { String message = - "Some APIs, Icons and Assets used in this MIH were sourced from third party providers.\n"; + "Some APIs, Icons and Assets used in MIH were sourced from third party providers.\n"; message += "We are grateful to the talented creators for providing these resources.\n"; message += - "As per the terms for free user for these third party providers, the following assets require attribution"; + "As per the terms for free use for these third party providers, the following assets require attribution"; return MihSingleChildScroll( child: Column( @@ -184,10 +184,11 @@ class _MihAttributesState extends State { ), ], ), - displayIcon(MihIcons.mihLogo, "Tarah Meth", - "https://www.linkedin.com/in/tarah-meth-3b6309254/"), displayIcon(MihIcons.mihRing, "Tarah Meth", "https://www.linkedin.com/in/tarah-meth-3b6309254/"), + displayIcon(MihIcons.mihLogo, "Tarah Meth", + "https://www.linkedin.com/in/tarah-meth-3b6309254/"), + displayIcon(MihIcons.mzansiAi, "Ollama", "https://ollama.com/"), displayIcon(MihIcons.mzansiWallet, "Freepik", "https://www.flaticon.com/free-icon/wallet-passes-app_3884407?term=wallet&page=1&position=21&origin=search&related_id=3884407"), displayIcon(MihIcons.patientProfile, "RaftelDesign", @@ -200,6 +201,8 @@ class _MihAttributesState extends State { "https://www.flaticon.com/free-icon/calculator_2374409?term=calculator&page=1&position=20&origin=search&related_id=2374409"), displayIcon(MihIcons.aboutMih, "Chanut", "https://www.flaticon.com/free-icon/info_151776?term=about&page=1&position=8&origin=search&related_id=151776"), + displayIcon(MihIcons.personalProfile, "Freepik", + "https://www.flaticon.com/free-icon/user_1077063?term=profile&page=1&position=6&origin=search&related_id=1077063"), displayIcon(MihIcons.businessProfile, "Gravisio", "https://www.flaticon.com/free-icon/contractor_11813336?term=company+profile&page=1&position=2&origin=search&related_id=11813336"), displayIcon(MihIcons.patientManager, "Vector Tank", @@ -210,6 +213,8 @@ class _MihAttributesState extends State { "https://www.flaticon.com/free-icon/business_13569850?term=company+add&page=1&position=25&origin=search&related_id=13569850"), displayIcon(MihIcons.calculator, "fawazahmed0", "https://github.com/fawazahmed0/exchange-api"), + displayIcon(MihIcons.iDontKnow, "Freepik", + "https://www.flaticon.com/free-icon/i-dont-know_5359909?term=i+dont+know&page=1&position=7&origin=search&related_id=5359909"), ], ), ), diff --git a/Frontend/lib/mih_packages/mih_home/mih_home_legacy.dart b/Frontend/lib/mih_packages/mih_home/mih_home_legacy.dart index 17346b69..8ee7050d 100644 --- a/Frontend/lib/mih_packages/mih_home/mih_home_legacy.dart +++ b/Frontend/lib/mih_packages/mih_home/mih_home_legacy.dart @@ -902,8 +902,8 @@ class _MIHHomeLegacyState extends State { MIHLocationAPI().getGPSPosition(context).then((position) { if (position != null) { print(position); - print( - "Distance: ${MIHLocationAPI().getDistanceInMeaters(position, position)}m"); + // print( + // "Distance: ${MIHLocationAPI().getDistanceInMeaters(position, position)}m"); } }); }, diff --git a/Frontend/lib/mih_packages/mih_home/package_tools/mih_business_home.dart b/Frontend/lib/mih_packages/mih_home/package_tools/mih_business_home.dart index 2da37769..5b1efdf5 100644 --- a/Frontend/lib/mih_packages/mih_home/package_tools/mih_business_home.dart +++ b/Frontend/lib/mih_packages/mih_home/package_tools/mih_business_home.dart @@ -12,6 +12,7 @@ import 'package:mzansi_innovation_hub/mih_packages/about_mih/package_tile/about_ import 'package:mzansi_innovation_hub/mih_packages/calculator/package_tiles/mih_calculator_tile.dart'; import 'package:mzansi_innovation_hub/mih_packages/calendar/package_tiles/mzansi_calendar_tile.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_ai/package_tiles/mzansi_ai_tile.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_directory/package_tiles/mzansi_directory_tile.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/package_tiles/mzansi_business_profile_tile.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/package_tiles/mzansi_setup_business_profile_tile.dart'; import 'package:mzansi_innovation_hub/mih_packages/patient_profile/pat_manager/package_tiles/pat_manager_tile.dart'; @@ -112,10 +113,9 @@ class _MihBusinessHomeState extends State packageSize: packageSize, ) }); - //=============== Mzansi AI =============== + //=============== Mzansi Directory =============== temp.add({ - "Mzansi AI": MzansiAiTile( - signedInUser: widget.signedInUser, + "Mzansi Directory": MzansiDirectoryTile( packageSize: packageSize, ) }); @@ -126,6 +126,13 @@ class _MihBusinessHomeState extends State packageSize: packageSize, ) }); + //=============== Mzansi AI =============== + temp.add({ + "Mzansi AI": MzansiAiTile( + signedInUser: widget.signedInUser, + packageSize: packageSize, + ) + }); //=============== About MIH =============== temp.add({"About MIH": AboutMihTile(packageSize: packageSize)}); return temp; @@ -278,6 +285,7 @@ class _MihBusinessHomeState extends State mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ + const SizedBox(height: 50), Icon( MihIcons.mzansiAi, size: 165, diff --git a/Frontend/lib/mih_packages/mih_home/package_tools/mih_personal_home.dart b/Frontend/lib/mih_packages/mih_home/package_tools/mih_personal_home.dart index dfd07ba9..a9818458 100644 --- a/Frontend/lib/mih_packages/mih_home/package_tools/mih_personal_home.dart +++ b/Frontend/lib/mih_packages/mih_home/package_tools/mih_personal_home.dart @@ -1,4 +1,3 @@ -import 'package:flutter/services.dart'; import 'package:mzansi_innovation_hub/main.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_icons.dart'; @@ -14,6 +13,7 @@ import 'package:mzansi_innovation_hub/mih_packages/access_review/package_tile/mi import 'package:mzansi_innovation_hub/mih_packages/calculator/package_tiles/mih_calculator_tile.dart'; import 'package:mzansi_innovation_hub/mih_packages/calendar/package_tiles/mzansi_calendar_tile.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_ai/package_tiles/mzansi_ai_tile.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_directory/package_tiles/mzansi_directory_tile.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/package_tiles/mzansi_profile_tile.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/personal_profile/package_tiles/mzansi_setup_profile_tile.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_wallet/package_tiles/mih_wallet_tile.dart'; @@ -114,10 +114,9 @@ class _MihPersonalHomeState extends State packageSize: packageSize, ) }); - //=============== Mzansi AI =============== + //=============== Mzansi Directory =============== temp.add({ - "Mzansi AI": MzansiAiTile( - signedInUser: widget.signedInUser, + "Mzansi Directory": MzansiDirectoryTile( packageSize: packageSize, ) }); @@ -133,6 +132,13 @@ class _MihPersonalHomeState extends State packageSize: packageSize, ) }); + //=============== Mzansi AI =============== + temp.add({ + "Mzansi AI": MzansiAiTile( + signedInUser: widget.signedInUser, + packageSize: packageSize, + ) + }); //=============== Calculator =============== temp.add({ "Calculator": MihCalculatorTile( @@ -156,7 +162,10 @@ class _MihPersonalHomeState extends State onTap: () { Navigator.of(context).pushNamed( '/package-dev', - //arguments: widget.signedInUser, + arguments: TestArguments( + widget.signedInUser, + widget.business, + ), ); }, appName: "Test", @@ -322,6 +331,7 @@ class _MihPersonalHomeState extends State mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ + const SizedBox(height: 50), Icon( MihIcons.mzansiAi, size: 165, diff --git a/Frontend/lib/mih_packages/mzansi_directory/builders/build_business_search_resultsList.dart b/Frontend/lib/mih_packages/mzansi_directory/builders/build_business_search_resultsList.dart new file mode 100644 index 00000000..39b69ca0 --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_directory/builders/build_business_search_resultsList.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/business.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_business_profile_preview.dart'; + +class BuildBusinessSearchResultsList extends StatefulWidget { + final List businessList; + final String myLocation; + const BuildBusinessSearchResultsList({ + super.key, + required this.businessList, + required this.myLocation, + }); + + @override + State createState() => + _BuildBusinessSearchResultsListState(); +} + +class _BuildBusinessSearchResultsListState + extends State { + @override + Widget build(BuildContext context) { + return ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: widget.businessList.length, + separatorBuilder: (BuildContext context, index) { + return Divider( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ); + }, + itemBuilder: (context, index) { + return Material( + color: MzanziInnovationHub.of(context)!.theme.primaryColor(), + child: InkWell( + onTap: () { + Navigator.of(context).pushNamed( + '/business-profile/view', + arguments: widget.businessList[index], + ); + }, + splashColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor() + .withOpacity(0.2), + borderRadius: BorderRadius.circular(15), + child: Padding( + padding: EdgeInsetsGeometry.symmetric( + // vertical: 5, + horizontal: 25, + ), + child: MihBusinessProfilePreview( + business: widget.businessList[index], + myLocation: widget.myLocation, + ), + ), + ), + ); + }, + ); + } +} diff --git a/Frontend/lib/mih_packages/mzansi_directory/builders/build_user_search_results_list.dart b/Frontend/lib/mih_packages/mzansi_directory/builders/build_user_search_results_list.dart new file mode 100644 index 00000000..84f7d84c --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_directory/builders/build_user_search_results_list.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/app_user.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_personal_profile_preview.dart'; + +class BuildUserSearchResultsList extends StatefulWidget { + final List userList; + const BuildUserSearchResultsList({ + super.key, + required this.userList, + }); + + @override + State createState() => + _BuildUserSearchResultsListState(); +} + +class _BuildUserSearchResultsListState + extends State { + @override + Widget build(BuildContext context) { + return ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: widget.userList.length, + separatorBuilder: (BuildContext context, index) { + return Divider( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ); + }, + itemBuilder: (context, index) { + return Material( + color: MzanziInnovationHub.of(context)!.theme.primaryColor(), + child: InkWell( + onTap: () { + Navigator.of(context).pushNamed( + '/mzansi-profile/view', + arguments: widget.userList[index], + ); + }, + splashColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor() + .withOpacity(0.2), + borderRadius: BorderRadius.circular(15), + child: Padding( + padding: EdgeInsetsGeometry.symmetric( + // vertical: 5, + horizontal: 25, + ), + child: MihPersonalProfilePreview( + user: widget.userList[index], + ), + ), + ), + ); + }, + ); + } +} diff --git a/Frontend/lib/mih_packages/mzansi_directory/mzansi_directory.dart b/Frontend/lib/mih_packages/mzansi_directory/mzansi_directory.dart new file mode 100644 index 00000000..17332452 --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_directory/mzansi_directory.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.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_contacts.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_directory/package_tools/mih_favourite_businesses.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_directory/package_tools/mih_search_mzansi.dart'; + +class MzansiDirectory extends StatefulWidget { + const MzansiDirectory({super.key}); + + @override + State createState() => _MzansiDirectoryState(); +} + +class _MzansiDirectoryState extends State { + int _selcetedIndex = 0; + + @override + Widget build(BuildContext context) { + return MihPackage( + appActionButton: getAction(), + appTools: getTools(), + appBody: getToolBody(), + appToolTitles: getToolTitle(), + selectedbodyIndex: _selcetedIndex, + onIndexChange: (newValue) { + setState(() { + _selcetedIndex = newValue; + }); + }, + ); + } + + List getToolBody() { + List toolBodies = [ + MihSearchMzansi(), + MihContacts(), + MihFavouriteBusinesses(), + ]; + return toolBodies; + } + + MihPackageAction getAction() { + return MihPackageAction( + icon: const Icon(Icons.arrow_back), + iconSize: 35, + onTap: () { + Navigator.of(context).pop(); + FocusScope.of(context).unfocus(); + }, + ); + } + + MihPackageTools getTools() { + Map temp = {}; + temp[const Icon(Icons.search)] = () { + setState(() { + _selcetedIndex = 0; + }); + }; + // temp[const Icon(Icons.person)] = () { + // setState(() { + // _selcetedIndex = 1; + // }); + // }; + // temp[const Icon(Icons.business_center)] = () { + // setState(() { + // _selcetedIndex = 2; + // }); + // }; + return MihPackageTools( + tools: temp, + selcetedIndex: _selcetedIndex, + ); + } + + List getToolTitle() { + List toolTitles = [ + "Mzansi Search", + "Contacts", + "Favourite Businesses", + ]; + return toolTitles; + } +} 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 new file mode 100644 index 00000000..f37eae32 --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_directory/package_tiles/mzansi_directory_tile.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/main.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'; + +class MzansiDirectoryTile extends StatefulWidget { + final double packageSize; + const MzansiDirectoryTile({ + super.key, + required this.packageSize, + }); + + @override + State createState() => _MzansiDirectoryTileState(); +} + +class _MzansiDirectoryTileState extends State { + @override + Widget build(BuildContext context) { + return MihPackageTile( + onTap: () { + Navigator.of(context).pushNamed( + '/mzansi-directory', + // arguments: WalletArguments(widget.signedInUser, 0), + ); + }, + appName: "Mzansi Directory", + appIcon: Icon( + MihIcons.mzansiDirectory, + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + // size: widget.packageSize, + ), + iconSize: widget.packageSize, + primaryColor: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + secondaryColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), + ); + } +} diff --git a/Frontend/lib/mih_packages/mzansi_directory/package_tools/mih_contacts.dart b/Frontend/lib/mih_packages/mzansi_directory/package_tools/mih_contacts.dart new file mode 100644 index 00000000..22d57c0e --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_directory/package_tools/mih_contacts.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tool_body.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_search_bar.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; + +class MihContacts extends StatefulWidget { + const MihContacts({super.key}); + + @override + State createState() => _MihContactsState(); +} + +class _MihContactsState extends State { + final TextEditingController contactSearchController = TextEditingController(); + final FocusNode searchFocusNode = FocusNode(); + + @override + Widget build(BuildContext context) { + final Size size = MediaQuery.sizeOf(context); + final double width = size.width; + return MihPackageToolBody( + borderOn: false, + bodyItem: getBody(width), + ); + } + + Widget getBody(double width) { + return MihSingleChildScroll( + child: Column( + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: width / 20), + child: MihSearchBar( + controller: contactSearchController, + hintText: "Search Contacts", + prefixIcon: Icons.search, + fillColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + hintColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), + onPrefixIconTap: () {}, + searchFocusNode: searchFocusNode, + ), + ), + const SizedBox(height: 10), + ], + ), + ); + } +} diff --git a/Frontend/lib/mih_packages/mzansi_directory/package_tools/mih_favourite_businesses.dart b/Frontend/lib/mih_packages/mzansi_directory/package_tools/mih_favourite_businesses.dart new file mode 100644 index 00000000..ca16cfc3 --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_directory/package_tools/mih_favourite_businesses.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tool_body.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_search_bar.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; + +class MihFavouriteBusinesses extends StatefulWidget { + const MihFavouriteBusinesses({super.key}); + + @override + State createState() => _MihFavouriteBusinessesState(); +} + +class _MihFavouriteBusinessesState extends State { + final TextEditingController businessSearchController = + TextEditingController(); + final FocusNode searchFocusNode = FocusNode(); + + @override + Widget build(BuildContext context) { + final Size size = MediaQuery.sizeOf(context); + final double width = size.width; + return MihPackageToolBody( + borderOn: false, + bodyItem: getBody(width), + ); + } + + Widget getBody(double width) { + return MihSingleChildScroll( + child: Column( + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: width / 20), + child: MihSearchBar( + controller: businessSearchController, + hintText: "Search Businesses", + prefixIcon: Icons.search, + fillColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + hintColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), + onPrefixIconTap: () {}, + searchFocusNode: searchFocusNode, + ), + ), + const SizedBox(height: 10), + ], + ), + ); + } +} 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 new file mode 100644 index 00000000..b0f93e33 --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_directory/package_tools/mih_search_mzansi.dart @@ -0,0 +1,322 @@ +import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/app_user.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/business.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_icons.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_tool_body.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_search_bar.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_single_child_scroll.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_directory/builders/build_business_search_resultsList.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_directory/builders/build_user_search_results_list.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_business_details_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_location_services.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_user_services.dart'; + +class MihSearchMzansi extends StatefulWidget { + const MihSearchMzansi({super.key}); + + @override + State createState() => _MihSearchMzansiState(); +} + +class _MihSearchMzansiState extends State { + final TextEditingController mzansiSearchController = TextEditingController(); + final FocusNode searchFocusNode = FocusNode(); + bool userSearch = true; + Future?> futureUserSearchResults = Future.value(); + Future?> futureBusinessSearchResults = Future.value(); + late Future futurePosition = + MIHLocationAPI().getGPSPosition(context); + List userSearchResults = []; + List businessSearchResults = []; + + @override + void dispose() { + super.dispose(); + mzansiSearchController.dispose(); + } + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + final Size size = MediaQuery.sizeOf(context); + final double width = size.width; + return MihPackageToolBody( + borderOn: false, + bodyItem: getBody(width), + ); + } + + Widget getBody(double width) { + return MihSingleChildScroll( + child: Column( + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: width / 20), + child: MihSearchBar( + controller: mzansiSearchController, + hintText: "Search Mzansi", + prefixIcon: Icons.search, + prefixAltIcon: userSearch + ? MihIcons.personalProfile + : MihIcons.businessProfile, + suffixTools: [ + IconButton( + onPressed: () { + setState(() { + // searchTypeVisibility = !searchTypeVisibility; + userSearch = !userSearch; + if (userSearch) { + futureUserSearchResults = MihUserServices() + .searchUsers( + mzansiSearchController.text, context); + } else { + futureBusinessSearchResults = + MihBusinessDetailsServices().searchBusinesses( + mzansiSearchController.text, context); + } + }); + }, + icon: Icon( + Icons.display_settings, + size: 35, + color: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + )) + ], + fillColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + hintColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), + onPrefixIconTap: () { + if (userSearch) { + setState(() { + futureUserSearchResults = MihUserServices() + .searchUsers(mzansiSearchController.text, context); + }); + } else { + setState(() { + futureBusinessSearchResults = MihBusinessDetailsServices() + .searchBusinesses(mzansiSearchController.text, context); + }); + } + }, + onClearIconTap: () { + setState(() { + futureUserSearchResults = Future.value(); + futureBusinessSearchResults = Future.value(); + mzansiSearchController.clear(); + }); + }, + searchFocusNode: searchFocusNode, + ), + ), + const SizedBox(height: 10), + FutureBuilder( + future: futurePosition, + builder: (context, asyncSnapshot) { + if (asyncSnapshot.connectionState == ConnectionState.waiting) { + return const Mihloadingcircle( + message: "Getting Your GPS Location Ready", + ); + } else { + final myLocation = asyncSnapshot.data + .toString() + .replaceAll("Latitude: ", "") + .replaceAll("Longitude: ", ""); + print("My Location is : $myLocation"); + return displaySearchResults(userSearch, myLocation); + } + }), + ], + ), + ); + } + + Widget displaySearchResults(bool userSearch, String myLocation) { + if (userSearch) { + return FutureBuilder( + future: futureUserSearchResults, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Mihloadingcircle(); + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData && + snapshot.requireData!.isNotEmpty) { + // return Text("Pulled Data successfully"); + snapshot.requireData! + .sort((a, b) => a.username.compareTo(b.username)); + return Column( + children: [ + Text( + "People of Mzansi", + style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 10), + BuildUserSearchResultsList(userList: snapshot.requireData!), + ], + ); + } else if (!snapshot.hasData) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 50), + Icon( + MihIcons.personalProfile, + size: 165, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + const SizedBox(height: 10), + Text( + "People Of Mzansi!", + textAlign: TextAlign.center, + overflow: TextOverflow.visible, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ], + ); + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData && + snapshot.requireData!.isEmpty) { + // return Text("Pulled Data successfully"); + return Column( + children: [ + const SizedBox(height: 50), + Icon( + MihIcons.iDontKnow, + size: 165, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + const SizedBox(height: 10), + Text( + "Let's Try Refining Your Search", + textAlign: TextAlign.center, + overflow: TextOverflow.visible, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ], + ); + } else { + return Center( + child: Text( + "Error pulling Patients Data\n/users/search/${mzansiSearchController.text}", + style: TextStyle( + fontSize: 25, + color: MzanziInnovationHub.of(context)!.theme.errorColor()), + textAlign: TextAlign.center, + ), + ); + } + }, + ); + } else { + return FutureBuilder( + future: futureBusinessSearchResults, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Mihloadingcircle(); + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData && + snapshot.requireData!.isNotEmpty) { + // return Text("Pulled Data successfully"); + snapshot.requireData!.sort((a, b) => a.Name.compareTo(b.Name)); + return Column( + children: [ + Text( + "Businesses of Mzansi", + style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 10), + BuildBusinessSearchResultsList( + businessList: snapshot.requireData!, + myLocation: myLocation, + ), + ], + ); + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData && + snapshot.requireData!.isEmpty) { + // return Text("Pulled Data successfully"); + return Column( + children: [ + const SizedBox(height: 50), + Icon( + MihIcons.iDontKnow, + size: 165, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + const SizedBox(height: 10), + Text( + "Let's Try Refining Your Search", + textAlign: TextAlign.center, + overflow: TextOverflow.visible, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ], + ); + } else if (!snapshot.hasData) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 50), + Icon( + MihIcons.businessProfile, + size: 165, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + const SizedBox(height: 10), + Text( + "Businesses Of Mzansi!", + textAlign: TextAlign.center, + overflow: TextOverflow.visible, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ], + ); + } else { + return Center( + child: Text( + "Error pulling Patients Data\n/users/search/${mzansiSearchController.text}", + style: TextStyle( + fontSize: 25, + color: MzanziInnovationHub.of(context)!.theme.errorColor()), + textAlign: TextAlign.center, + ), + ); + } + }, + ); + } + } +} diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_card.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart similarity index 55% rename from Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_card.dart rename to Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart index 32316654..02bf1290 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_card.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/components/mih_business_info_card.dart @@ -8,14 +8,14 @@ class MihBusinessCard extends StatefulWidget { final String cellNumber; final String email; final String gpsLocation; - final String website; + final String? website; const MihBusinessCard({ super.key, required this.businessName, required this.cellNumber, required this.email, required this.gpsLocation, - required this.website, + this.website, }); @override @@ -35,15 +35,17 @@ class _MihBusinessCardState extends State { alertIcon: Icon( Icons.warning_rounded, size: 100, + color: MzanziInnovationHub.of(context)!.theme.errorColor(), ), alertTitle: "Error Making Call", alertBody: Column( children: [ Text( - "Unable to lauch phone to call ${widget.cellNumber}", + "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.", style: TextStyle( - color: - MzanziInnovationHub.of(context)!.theme.errorColor(), + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), fontSize: 15, ), ), @@ -83,15 +85,17 @@ class _MihBusinessCardState extends State { alertIcon: Icon( Icons.warning_rounded, size: 100, + color: MzanziInnovationHub.of(context)!.theme.errorColor(), ), alertTitle: "Error Creating Email", alertBody: Column( children: [ Text( - "Unable to lauch email to ${widget.email}", + "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.", style: TextStyle( - color: - MzanziInnovationHub.of(context)!.theme.errorColor(), + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), fontSize: 15, ), ), @@ -115,8 +119,6 @@ class _MihBusinessCardState extends State { if (await canLaunchUrl(googleMapsUrl)) { await launchUrl(googleMapsUrl); } else { - print( - 'Could not launch Google Maps. Make sure the Google Maps app is installed or an internet connection is available.'); showDialog( context: context, builder: (context) { @@ -124,15 +126,17 @@ class _MihBusinessCardState extends State { alertIcon: Icon( Icons.warning_rounded, size: 100, + color: MzanziInnovationHub.of(context)!.theme.errorColor(), ), alertTitle: "Error Creating Maps", alertBody: Column( children: [ Text( - "Unable to lauch maps to ${widget.businessName}", + "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.", style: TextStyle( - color: - MzanziInnovationHub.of(context)!.theme.errorColor(), + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), fontSize: 15, ), ), @@ -151,15 +155,17 @@ class _MihBusinessCardState extends State { alertIcon: Icon( Icons.warning_rounded, size: 100, + color: MzanziInnovationHub.of(context)!.theme.errorColor(), ), alertTitle: "Error Creating Maps", alertBody: Column( children: [ Text( - "Unable to lauch maps to ${widget.businessName}", + "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.", style: TextStyle( - color: - MzanziInnovationHub.of(context)!.theme.errorColor(), + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), fontSize: 15, ), ), @@ -172,7 +178,11 @@ class _MihBusinessCardState extends State { } Future _launchWebsite(String urlString) async { - final Uri url = Uri.parse(urlString); + String newUrl = urlString; + if (!newUrl.startsWith("https://")) { + newUrl = "https://$urlString"; + } + final Uri url = Uri.parse(newUrl); try { if (await canLaunchUrl(url)) { await launchUrl(url); @@ -185,15 +195,17 @@ class _MihBusinessCardState extends State { alertIcon: Icon( Icons.warning_rounded, size: 100, + color: MzanziInnovationHub.of(context)!.theme.errorColor(), ), alertTitle: "Error Opening Website", alertBody: Column( children: [ Text( - "Unable to lauch ${widget.businessName}", + "We couldn't open the link to $newUrl. To view this website, please ensure you have a web browser installed and set as your default.", style: TextStyle( - color: - MzanziInnovationHub.of(context)!.theme.errorColor(), + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), fontSize: 15, ), ), @@ -212,15 +224,17 @@ class _MihBusinessCardState extends State { alertIcon: Icon( Icons.warning_rounded, size: 100, + color: MzanziInnovationHub.of(context)!.theme.errorColor(), ), alertTitle: "Error Opening Website", alertBody: Column( children: [ Text( - "Unable to lauch ${widget.businessName}", + "We couldn't open the link to $newUrl. To view this website, please ensure you have a web browser installed and set as your default.", style: TextStyle( - color: - MzanziInnovationHub.of(context)!.theme.errorColor(), + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), fontSize: 15, ), ), @@ -250,6 +264,7 @@ class _MihBusinessCardState extends State { borderRadius: BorderRadius.circular(15), child: Padding( padding: EdgeInsetsGeometry.symmetric( + // vertical: 5, horizontal: 25, ), child: Row( @@ -306,119 +321,126 @@ class _MihBusinessCardState extends State { @override Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), - borderRadius: BorderRadius.circular(10), - ), - child: Column( - children: [ - const SizedBox(height: 10), - _buildContactInfo( - "Call", - "Give us a quick call.", - Icons.phone, - const Color(0xffaff0b3), - () { - // print("Calling ${widget.cellNumber}"); - _makePhoneCall(widget.cellNumber); - }, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: Divider( + return Material( + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor() + .withValues(alpha: 0.6), + borderRadius: BorderRadius.circular(25), + elevation: 10, + shadowColor: Colors.black, + child: Container( + decoration: BoxDecoration( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + children: [ + const SizedBox(height: 10), + _buildContactInfo( + "Call", + "Give us a quick call.", + Icons.phone, + const Color(0xffaff0b3), + () { + // print("Calling ${widget.cellNumber}"); + _makePhoneCall(widget.cellNumber); + }, + ), + Divider( color: MzanziInnovationHub.of(context)!.theme.primaryColor(), ), - ), - _buildContactInfo( - "Email", - "Send us an email.", - Icons.email, - const Color(0xffdaa2e9), - () { - // 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", - ); - }, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), - child: Divider( + _buildContactInfo( + "Email", + "Send us an email.", + Icons.email, + const Color(0xffdaa2e9), + () { + // 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", + ); + }, + ), + Divider( color: MzanziInnovationHub.of(context)!.theme.primaryColor(), ), - ), - _buildContactInfo( - "Location", - "Come visit us.", - Icons.location_on, - const Color(0xffe9e8a1), - () { - final latitude = double.parse(widget.gpsLocation.split(',')[0]); - final longitude = double.parse(widget.gpsLocation.split(',')[1]); - _launchGoogleMapsWithUrl( - latitude: latitude, - longitude: longitude, - ); - }, - ), - // Padding( - // padding: const EdgeInsets.symmetric(horizontal: 10.0), - // child: Divider( - // color: MzanziInnovationHub.of(context)!.theme.primaryColor(), - // ), - // ), - // _buildContactInfo( - // "Website", - // "Find out more about us.", - // Icons.vpn_lock, - // const Color(0xffd67d8a), - // () { - // _launchWebsite(widget.website); - // }, - // ), - // Padding( - // padding: const EdgeInsets.symmetric(horizontal: 10.0), - // child: Divider( - // color: MzanziInnovationHub.of(context)!.theme.primaryColor(), - // ), - // ), - // _buildContactInfo( - // "Rate Us", - // "Let us know how we are doing.", - // Icons.star_rate_rounded, - // const Color(0xffd69d7d), - // () { - // print("Opeining rating dialog"); - // // _launchWebsite(widget.website); - // }, - // ), - // Padding( - // padding: const EdgeInsets.symmetric(horizontal: 10.0), - // child: Divider( - // color: MzanziInnovationHub.of(context)!.theme.primaryColor(), - // ), - // ), - // _buildContactInfo( - // "Bookmark", - // "Save us for later.", - // Icons.bookmark_add_rounded, - // const Color(0xff6e7dcc), - // () { - // // _launchWebsite(widget.website); - // print("Saving ${widget.businessName} to Directory"); - // }, - // ), - const SizedBox(height: 10), - // Padding( - // padding: const EdgeInsets.symmetric(horizontal: 10.0), - // child: Divider( - // color: MzanziInnovationHub.of(context)!.theme.primaryColor(), - // ), - // ), - ], + _buildContactInfo( + "Location", + "Come visit us.", + Icons.location_on, + const Color(0xffe9e8a1), + () { + final latitude = double.parse(widget.gpsLocation.split(',')[0]); + final longitude = + double.parse(widget.gpsLocation.split(',')[1]); + _launchGoogleMapsWithUrl( + latitude: latitude, + longitude: longitude, + ); + }, + ), + Visibility( + visible: widget.website != null && widget.website! != "", + child: Divider( + color: MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ), + Visibility( + visible: widget.website != null && widget.website! != "", + child: _buildContactInfo( + "Website", + "Find out more about us.", + Icons.vpn_lock, + const Color(0xffd67d8a), + () { + _launchWebsite(widget.website!); + }, + ), + ), + // Padding( + // padding: const EdgeInsets.symmetric(horizontal: 10.0), + // child: Divider( + // color: MzanziInnovationHub.of(context)!.theme.primaryColor(), + // ), + // ), + // _buildContactInfo( + // "Rate Us", + // "Let us know how we are doing.", + // Icons.star_rate_rounded, + // const Color(0xffd69d7d), + // () { + // print("Opeining rating dialog"); + // // _launchWebsite(widget.website); + // }, + // ), + // Padding( + // padding: const EdgeInsets.symmetric(horizontal: 10.0), + // child: Divider( + // color: MzanziInnovationHub.of(context)!.theme.primaryColor(), + // ), + // ), + // _buildContactInfo( + // "Bookmark", + // "Save us for later.", + // Icons.bookmark_add_rounded, + // const Color(0xff6e7dcc), + // () { + // // _launchWebsite(widget.website); + // print("Saving ${widget.businessName} to Directory"); + // }, + // ), + const SizedBox(height: 10), + // Padding( + // padding: const EdgeInsets.symmetric(horizontal: 10.0), + // child: Divider( + // color: MzanziInnovationHub.of(context)!.theme.primaryColor(), + // ), + // ), + ], + ), ), ); } 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 new file mode 100644 index 00000000..f70aaefd --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile_view.dart @@ -0,0 +1,76 @@ +import 'package:mzansi_innovation_hub/mih_components/mih_objects/business.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:flutter/material.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart'; + +class MzansiBusinessProfileView extends StatefulWidget { + final Business business; + const MzansiBusinessProfileView({ + super.key, + required this.business, + }); + + @override + State createState() => + _MzansiBusinessProfileViewState(); +} + +class _MzansiBusinessProfileViewState extends State { + int _selcetedIndex = 0; + + @override + Widget build(BuildContext context) { + return MihPackage( + appActionButton: getAction(), + appTools: getTools(), + appBody: getToolBody(), + appToolTitles: getToolTitle(), + selectedbodyIndex: _selcetedIndex, + onIndexChange: (newValue) { + setState(() { + _selcetedIndex = newValue; + }); + }, + ); + } + + MihPackageAction getAction() { + return MihPackageAction( + icon: const Icon(Icons.arrow_back), + iconSize: 35, + onTap: () { + Navigator.of(context).pop(); + FocusScope.of(context).unfocus(); + }, + ); + } + + MihPackageTools getTools() { + Map temp = {}; + temp[const Icon(Icons.business)] = () { + setState(() { + _selcetedIndex = 0; + }); + }; + return MihPackageTools( + tools: temp, + selcetedIndex: _selcetedIndex, + ); + } + + List getToolBody() { + List toolBodies = [ + MihBusinessDetailsView(business: widget.business), + ]; + return toolBodies; + } + + List getToolTitle() { + List toolTitles = [ + "Profile", + ]; + return toolTitles; + } +} 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 a0e0bcf2..3ef2a88c 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 @@ -4,7 +4,8 @@ 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_floating_menu.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_window.dart'; -import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/components/mih_business_card.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_services/mih_alert_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_business_details_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart'; @@ -12,7 +13,6 @@ import 'package:mzansi_innovation_hub/mih_services/mih_location_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_validation_services.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_button.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_dropdwn_field.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_tool_body.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_package_alert.dart'; @@ -47,13 +47,17 @@ class _MihBusinessDetailsState extends State { final contactController = TextEditingController(); final emailController = TextEditingController(); final locationController = TextEditingController(); + final websiteController = TextEditingController(); + final ratingController = TextEditingController(); + final missionVisionController = TextEditingController(); final _formKey = GlobalKey(); + final ValueNotifier _counter = ValueNotifier(0); late String env; Future submitForm() async { if (isFormFilled()) { int statusCode = 0; - statusCode = await MihBusinessDetailsServices().updateBusinessDetails( + statusCode = await MihBusinessDetailsServices().updateBusinessDetailsV2( widget.arguments.business!.business_id, nameController.text, typeController.text, @@ -64,6 +68,9 @@ class _MihBusinessDetailsState extends State { contactController.text, locationController.text, fileNameController.text, + websiteController.text, + ratingController.text.isEmpty ? "0" : ratingController.text, + missionVisionController.text, context, ); if (statusCode == 200) { @@ -246,22 +253,6 @@ class _MihBusinessDetailsState extends State { ), ), const SizedBox(height: 20), - MihTextFormField( - fillColor: MzanziInnovationHub.of(context)! - .theme - .secondaryColor(), - inputColor: MzanziInnovationHub.of(context)! - .theme - .primaryColor(), - controller: regController, - multiLineInput: false, - requiredText: true, - hintText: "Registration No.", - validator: (value) { - return MihValidationServices().isEmpty(value); - }, - ), - const SizedBox(height: 10), MihTextFormField( fillColor: MzanziInnovationHub.of(context)! .theme @@ -277,18 +268,33 @@ class _MihBusinessDetailsState extends State { return MihValidationServices().isEmpty(value); }, ), - const SizedBox(height: 15), - MihDropdownField( + const SizedBox(height: 10), + MihTextFormField( + fillColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), controller: typeController, + multiLineInput: false, + requiredText: true, hintText: "Business Type", - dropdownOptions: const ["Doctors Office", "Other"], - editable: true, - enableSearch: true, validator: (value) { return MihValidationServices().isEmpty(value); }, - requiredText: true, ), + // MihDropdownField( + // controller: typeController, + // hintText: "Business Type", + // dropdownOptions: const ["Doctors Office", "Other"], + // editable: true, + // enableSearch: true, + // validator: (value) { + // return MihValidationServices().isEmpty(value); + // }, + // requiredText: true, + // ), const SizedBox(height: 10), MihTextFormField( fillColor: MzanziInnovationHub.of(context)! @@ -297,30 +303,13 @@ class _MihBusinessDetailsState extends State { inputColor: MzanziInnovationHub.of(context)! .theme .primaryColor(), - controller: practiceNoController, + controller: emailController, multiLineInput: false, - requiredText: - typeController.text == "Doctors Office", - hintText: "Practice Number", - validator: (validateValue) { + requiredText: true, + hintText: "Business Email", + validator: (value) { return MihValidationServices() - .isEmpty(validateValue); - }, - ), - const SizedBox(height: 10), - MihTextFormField( - fillColor: MzanziInnovationHub.of(context)! - .theme - .secondaryColor(), - inputColor: MzanziInnovationHub.of(context)! - .theme - .primaryColor(), - controller: vatNoController, - multiLineInput: false, - requiredText: true, - hintText: "VAT Number", - validator: (value) { - return MihValidationServices().isEmpty(value); + .validateEmail(value); }, ), const SizedBox(height: 10), @@ -347,13 +336,110 @@ class _MihBusinessDetailsState extends State { inputColor: MzanziInnovationHub.of(context)! .theme .primaryColor(), - controller: emailController, + controller: websiteController, multiLineInput: false, - requiredText: true, - hintText: "Business Email", + requiredText: false, + hintText: "Business Website", validator: (value) { return MihValidationServices() - .validateEmail(value); + .validateWebsite(value, false); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + height: 250, + fillColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + controller: missionVisionController, + multiLineInput: true, + requiredText: true, + hintText: "Business Mission & Vision", + validator: (value) { + return MihValidationServices().validateLength( + missionVisionController.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: 10.0), + MihTextFormField( + fillColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + controller: regController, + multiLineInput: false, + requiredText: true, + hintText: "Registration No.", + validator: (value) { + return MihValidationServices().isEmpty(value); + }, + ), + const SizedBox(height: 10), + MihTextFormField( + fillColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + controller: practiceNoController, + multiLineInput: false, + requiredText: + typeController.text == "Doctors Office", + hintText: "Practice Number", + validator: (validateValue) { + return null; + }, + ), + const SizedBox(height: 10), + MihTextFormField( + fillColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + controller: vatNoController, + multiLineInput: false, + requiredText: true, + hintText: "VAT Number", + validator: (value) { + return MihValidationServices().isEmpty(value); }, ), const SizedBox(height: 10), @@ -377,6 +463,14 @@ class _MihBusinessDetailsState extends State { const SizedBox(width: 10.0), MihButton( onPressed: () { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle( + message: "Getting your location", + ); + }, + ); MIHLocationAPI() .getGPSPosition(context) .then((position) { @@ -386,6 +480,8 @@ class _MihBusinessDetailsState extends State { "${position.latitude}, ${position.longitude}"; }); } + //Dismiss loading indicator + Navigator.of(context).pop(); }); }, buttonColor: MzanziInnovationHub.of(context)! @@ -442,6 +538,14 @@ class _MihBusinessDetailsState extends State { )); } + Color getMissionVisionLimitColor(int limit) { + if (_counter.value <= limit) { + return MzanziInnovationHub.of(context)!.theme.secondaryColor(); + } else { + return MzanziInnovationHub.of(context)!.theme.errorColor(); + } + } + @override void dispose() { super.dispose(); @@ -454,6 +558,9 @@ class _MihBusinessDetailsState extends State { contactController.dispose(); emailController.dispose(); locationController.dispose(); + websiteController.dispose(); + ratingController.dispose(); + missionVisionController.dispose(); imageFile = null; } @@ -471,12 +578,20 @@ class _MihBusinessDetailsState extends State { contactController.text = widget.arguments.business!.contact_no; emailController.text = widget.arguments.business!.bus_email; locationController.text = widget.arguments.business!.gps_location; + websiteController.text = widget.arguments.business!.website; + ratingController.text = widget.arguments.business!.rating; + missionVisionController.text = widget.arguments.business!.mission_vision; }); if (AppEnviroment.getEnv() == "Prod") { env = "Prod"; } else { env = "Dev"; } + missionVisionController.addListener(() { + setState(() { + _counter.value = missionVisionController.text.characters.length; + }); + }); } @override @@ -530,10 +645,9 @@ class _MihBusinessDetailsState extends State { ), ), ), - // Center( + // FittedBox( // child: Text( - // "*DEMO TEXT* This would be the bio of the user telling us a bit about themself and let. This would be the bio of the user telling us a bit about themself and let. This would be the bio of the user telling us a bit about themself", - // textAlign: TextAlign.center, + // "Mission & Vision", // style: TextStyle( // fontSize: 15, // fontWeight: FontWeight.bold, @@ -543,6 +657,24 @@ class _MihBusinessDetailsState extends State { // ), // ), // ), + Center( + child: SizedBox( + width: 700, + child: Text( + widget.arguments.business!.mission_vision.isNotEmpty + ? widget.arguments.business!.mission_vision + : "No Mission & Vision added yet", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ), + ), const SizedBox(height: 20), SizedBox( width: 700, @@ -552,8 +684,7 @@ class _MihBusinessDetailsState extends State { email: widget.arguments.business!.bus_email, gpsLocation: widget.arguments.business!.gps_location, //To-Do: Add the business Website - website: - "https://app.mzansi-innovation-hub.co.za/privacy.html", + website: widget.arguments.business!.website, ), ), 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 new file mode 100644 index 00000000..a7558b4e --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details_view.dart @@ -0,0 +1,180 @@ +import 'package:file_picker/file_picker.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_package_components/mih_icons.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_file_services.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_package_tool_body.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_circle_avatar.dart'; + +class MihBusinessDetailsView extends StatefulWidget { + final Business business; + const MihBusinessDetailsView({ + super.key, + required this.business, + }); + + @override + State createState() => _MihBusinessDetailsViewState(); +} + +class _MihBusinessDetailsViewState extends State { + late Future futureImageUrl; + PlatformFile? file; + + @override + void dispose() { + super.dispose(); + } + + @override + void initState() { + super.initState(); + futureImageUrl = + MihFileApi.getMinioFileUrl(widget.business.logo_path, context); + } + + @override + Widget build(BuildContext context) { + double screenWidth = MediaQuery.of(context).size.width; + return MihPackageToolBody( + borderOn: false, + innerHorizontalPadding: 10, + bodyItem: getBody(screenWidth, context), + ); + } + + Widget getBody(double width, BuildContext context) { + double profilePictureWidth = 150; + return Stack( + children: [ + MihSingleChildScroll( + child: Padding( + padding: + MzanziInnovationHub.of(context)!.theme.screenType == "desktop" + ? EdgeInsets.symmetric(horizontal: width * 0.2) + : EdgeInsets.symmetric(horizontal: width * 0.075), + child: Column( + children: [ + FutureBuilder( + future: futureImageUrl, + builder: (context, asyncSnapshot) { + if (asyncSnapshot.connectionState == + ConnectionState.done && + asyncSnapshot.hasData) { + if (asyncSnapshot.requireData != "") { + return MihCircleAvatar( + imageFile: NetworkImage(asyncSnapshot.requireData), + width: profilePictureWidth, + editable: false, + fileNameController: TextEditingController(), + userSelectedfile: file, + frameColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + backgroundColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + onChange: () {}, + ); + } else { + return Icon( + MihIcons.iDontKnow, + size: profilePictureWidth, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ); + } + } else { + return Icon( + MihIcons.mihRing, + size: profilePictureWidth, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ); + } + }), + // Center( + // child: MihCircleAvatar( + // imageFile: widget.logoImage, + // width: 150, + // editable: false, + // fileNameController: fileNameController, + // userSelectedfile: imageFile, + // frameColor: + // MzanziInnovationHub.of(context)!.theme.secondaryColor(), + // backgroundColor: + // MzanziInnovationHub.of(context)!.theme.primaryColor(), + // onChange: (selectedfile) { + // setState(() { + // imageFile = selectedfile; + // }); + // }, + // ), + // ), + FittedBox( + child: Text( + widget.business.Name, + style: TextStyle( + fontSize: 35, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ), + // FittedBox( + // child: Text( + // "Mission & Vision", + // style: TextStyle( + // fontSize: 15, + // fontWeight: FontWeight.bold, + // color: MzanziInnovationHub.of(context)! + // .theme + // .secondaryColor(), + // ), + // ), + // ), + Center( + child: SizedBox( + width: 700, + child: Text( + widget.business.mission_vision.isNotEmpty + ? widget.business.mission_vision + : "No Mission & Vision added yet", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ), + ), + const SizedBox(height: 20), + SizedBox( + width: 700, + child: MihBusinessCard( + businessName: widget.business.Name, + cellNumber: widget.business.contact_no, + email: widget.business.bus_email, + gpsLocation: widget.business.gps_location, + //To-Do: Add the business Website + website: widget.business.website, + ), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_user_search.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_user_search.dart index f55bb7d3..14abf077 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_user_search.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_user_search.dart @@ -1,16 +1,13 @@ -import 'dart:convert'; import 'package:mzansi_innovation_hub/main.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_package_tool_body.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_search_bar.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; -import 'package:mzansi_innovation_hub/mih_config/mih_env.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_packages/mzansi_profile/business_profile/builders/build_user_list.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:supertokens_flutter/http.dart' as http; +import 'package:mzansi_innovation_hub/mih_services/mih_user_services.dart'; class MihBusinessUserSearch extends StatefulWidget { final BusinessArguments arguments; @@ -33,18 +30,19 @@ class _MihBusinessUserSearchState extends State { String errorBody = ""; Future> fetchUsers(String search) async { - final response = await http - .get(Uri.parse("${AppEnviroment.baseApiUrl}/users/search/$search")); - errorCode = response.statusCode.toString(); - errorBody = response.body; - if (response.statusCode == 200) { - Iterable l = jsonDecode(response.body); - List users = - List.from(l.map((model) => AppUser.fromJson(model))); - return users; - } else { - throw Exception('failed to load patients'); - } + return MihUserServices().searchUsers(search, context); + // final response = await http + // .get(Uri.parse("${AppEnviroment.baseApiUrl}/users/search/$search")); + // errorCode = response.statusCode.toString(); + // errorBody = response.body; + // if (response.statusCode == 200) { + // Iterable l = jsonDecode(response.body); + // List users = + // List.from(l.map((model) => AppUser.fromJson(model))); + // return users; + // } else { + // throw Exception('failed to load patients'); + // } } void submitUserForm() { @@ -142,7 +140,7 @@ class _MihBusinessUserSearchState extends State { } else { return Center( child: Text( - "$errorCode: Error pulling Patients Data\n/patients/search/$userSearch\n$errorBody", + "$errorCode: Error pulling Patients Data\n/users/search/$userSearch\n$errorBody", style: TextStyle( fontSize: 25, color: diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/profile_business_add.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/profile_business_add.dart index bea0eeff..7d20fd8c 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/profile_business_add.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/profile_business_add.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:http/http.dart'; import 'package:mzansi_innovation_hub/main.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_business_details_services.dart'; import 'package:mzansi_innovation_hub/mih_services/mih_file_services.dart'; @@ -56,12 +57,16 @@ class _ProfileBusinessAddState extends State { final locationController = TextEditingController(); final practiceNoController = TextEditingController(); final vatNoController = TextEditingController(); + final websiteController = TextEditingController(); + final ratingController = TextEditingController(); + final missionVisionController = TextEditingController(); ImageProvider? logoPreview; ImageProvider? signaturePreview; PlatformFile? selectedLogo; PlatformFile? selectedSignature; + final ValueNotifier _counter = ValueNotifier(0); final ValueNotifier busType = ValueNotifier(""); final _formKey = GlobalKey(); late String env; @@ -123,6 +128,9 @@ class _ProfileBusinessAddState extends State { contactController.text, locationController.text, logonameController.text, + websiteController.text, + "0", + missionVisionController.text, context, ); print(response.body); @@ -223,7 +231,7 @@ class _ProfileBusinessAddState extends State { headerAlignment: MainAxisAlignment.center, headerItems: [ Text( - "Add Business Profile", + "Set Up Business Profile", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 25, @@ -233,6 +241,14 @@ class _ProfileBusinessAddState extends State { ); } + Color getMissionVisionLimitColor(int limit) { + if (_counter.value <= limit) { + return MzanziInnovationHub.of(context)!.theme.secondaryColor(); + } else { + return MzanziInnovationHub.of(context)!.theme.errorColor(); + } + } + MIHBody getBody(double width) { return MIHBody( borderOn: false, @@ -273,22 +289,6 @@ class _ProfileBusinessAddState extends State { MihForm( formKey: _formKey, formFields: [ - MihTextFormField( - fillColor: MzanziInnovationHub.of(context)! - .theme - .secondaryColor(), - inputColor: MzanziInnovationHub.of(context)! - .theme - .primaryColor(), - controller: regController, - multiLineInput: false, - requiredText: true, - hintText: "Registration No.", - validator: (value) { - return MihValidationServices().isEmpty(value); - }, - ), - const SizedBox(height: 10.0), MihTextFormField( fillColor: MzanziInnovationHub.of(context)! .theme @@ -304,47 +304,6 @@ class _ProfileBusinessAddState extends State { return MihValidationServices().isEmpty(value); }, ), - const SizedBox(height: 15.0), - MihDropdownField( - controller: typeController, - hintText: "Business Type", - dropdownOptions: const ["Doctors Office", "Other"], - editable: true, - enableSearch: true, - validator: (value) { - return MihValidationServices().isEmpty(value); - }, - requiredText: true, - ), - const SizedBox(height: 10.0), - ValueListenableBuilder( - valueListenable: busType, - builder: (BuildContext context, String value, - Widget? child) { - return Visibility( - visible: value == "Doctors Office", - child: MihTextFormField( - fillColor: MzanziInnovationHub.of(context)! - .theme - .secondaryColor(), - inputColor: MzanziInnovationHub.of(context)! - .theme - .primaryColor(), - controller: practiceNoController, - multiLineInput: false, - requiredText: true, - hintText: "Practice Number", - validator: (validateValue) { - if (value == "Doctors Office") { - return MihValidationServices() - .isEmpty(validateValue); - } - return null; - }, - ), - ); - }, - ), const SizedBox(height: 10.0), MihTextFormField( fillColor: MzanziInnovationHub.of(context)! @@ -353,14 +312,41 @@ class _ProfileBusinessAddState extends State { inputColor: MzanziInnovationHub.of(context)! .theme .primaryColor(), - controller: vatNoController, + controller: typeController, multiLineInput: false, requiredText: true, - hintText: "VAT Number", + hintText: "Business Type", validator: (value) { return MihValidationServices().isEmpty(value); }, ), + // MihDropdownField( + // controller: typeController, + // hintText: "Business Type", + // dropdownOptions: const ["Doctors Office", "Other"], + // editable: true, + // enableSearch: true, + // validator: (value) { + // return MihValidationServices().isEmpty(value); + // }, + // requiredText: true, + // ), + const SizedBox(height: 10.0), + MihTextFormField( + fillColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + controller: emailController, + multiLineInput: false, + requiredText: true, + hintText: "Business Email", + validator: (value) { + return MihValidationServices().validateEmail(value); + }, + ), const SizedBox(height: 10.0), MihTextFormField( fillColor: MzanziInnovationHub.of(context)! @@ -385,12 +371,111 @@ class _ProfileBusinessAddState extends State { inputColor: MzanziInnovationHub.of(context)! .theme .primaryColor(), - controller: emailController, + controller: websiteController, + multiLineInput: false, + requiredText: false, + hintText: "Business Website", + validator: (value) { + return MihValidationServices() + .validateWebsite(value, false); + }, + ), + const SizedBox(height: 10.0), + MihTextFormField( + height: 250, + fillColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + controller: missionVisionController, + multiLineInput: true, + requiredText: true, + hintText: "Business Mission & Vision", + validator: (value) { + return MihValidationServices().validateLength( + missionVisionController.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: 10.0), + + MihTextFormField( + fillColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + controller: regController, multiLineInput: false, requiredText: true, - hintText: "Business Email", + hintText: "Registration No.", validator: (value) { - return MihValidationServices().validateEmail(value); + return MihValidationServices().isEmpty(value); + }, + ), + const SizedBox(height: 10.0), + MihTextFormField( + fillColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + controller: practiceNoController, + multiLineInput: false, + requiredText: false, + hintText: "Practice Number", + validator: (validateValue) { + return null; + }, + ), + + const SizedBox(height: 10.0), + MihTextFormField( + fillColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + inputColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + controller: vatNoController, + multiLineInput: false, + requiredText: true, + hintText: "VAT Number", + validator: (value) { + return MihValidationServices().isEmpty(value); }, ), const SizedBox(height: 10.0), @@ -414,6 +499,14 @@ class _ProfileBusinessAddState extends State { const SizedBox(width: 10.0), MihButton( onPressed: () { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle( + message: "Getting your location", + ); + }, + ); MIHLocationAPI() .getGPSPosition(context) .then((position) { @@ -423,6 +516,7 @@ class _ProfileBusinessAddState extends State { "${position.latitude}, ${position.longitude}"; }); } + Navigator.of(context).pop(); }); }, buttonColor: MzanziInnovationHub.of(context)! @@ -573,6 +667,7 @@ class _ProfileBusinessAddState extends State { @override void initState() { + super.initState(); typeController.addListener(typeSelected); setState(() { fnameController.text = widget.signedInUser.fname; @@ -584,7 +679,11 @@ class _ProfileBusinessAddState extends State { } else { env = "Dev"; } - super.initState(); + missionVisionController.addListener(() { + setState(() { + _counter.value = missionVisionController.text.characters.length; + }); + }); } @override diff --git a/Frontend/lib/mih_packages/mzansi_profile/personal_profile/mzansi_profile.dart b/Frontend/lib/mih_packages/mzansi_profile/personal_profile/mzansi_profile.dart index 667c061b..c9c201f1 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/personal_profile/mzansi_profile.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/personal_profile/mzansi_profile.dart @@ -49,7 +49,7 @@ class _MzansiProfileState extends State { MihPackageTools getTools() { Map temp = {}; - temp[const Icon(Icons.perm_identity)] = () { + temp[const Icon(Icons.person)] = () { setState(() { _selcetedIndex = 0; }); diff --git a/Frontend/lib/mih_packages/mzansi_profile/personal_profile/mzansi_profile_view.dart b/Frontend/lib/mih_packages/mzansi_profile/personal_profile/mzansi_profile_view.dart new file mode 100644 index 00000000..a1c47ae8 --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_profile/personal_profile/mzansi_profile_view.dart @@ -0,0 +1,76 @@ +import 'package:mzansi_innovation_hub/mih_components/mih_objects/app_user.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_profile/personal_profile/package_tools/mih_personal_profile_view.dart'; +import 'package:flutter/material.dart'; + +class MzansiProfileView extends StatefulWidget { + final AppUser user; + const MzansiProfileView({ + super.key, + required this.user, + }); + + @override + State createState() => _MzansiProfileViewState(); +} + +class _MzansiProfileViewState extends State { + int _selcetedIndex = 0; + + @override + Widget build(BuildContext context) { + return MihPackage( + appActionButton: getAction(), + appTools: getTools(), + appBody: getToolBody(), + appToolTitles: getToolTitle(), + selectedbodyIndex: _selcetedIndex, + onIndexChange: (newValue) { + setState(() { + _selcetedIndex = newValue; + }); + }, + ); + } + + MihPackageAction getAction() { + return MihPackageAction( + icon: const Icon(Icons.arrow_back), + iconSize: 35, + onTap: () { + Navigator.of(context).pop(); + FocusScope.of(context).unfocus(); + }, + ); + } + + MihPackageTools getTools() { + Map temp = {}; + temp[const Icon(Icons.person)] = () { + setState(() { + _selcetedIndex = 0; + }); + }; + return MihPackageTools( + tools: temp, + selcetedIndex: _selcetedIndex, + ); + } + + List getToolBody() { + List toolBodies = []; + toolBodies.add(MihPersonalProfileView( + user: widget.user, + )); + return toolBodies; + } + + List getToolTitle() { + List toolTitles = [ + "Profile", + ]; + return toolTitles; + } +} diff --git a/Frontend/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile.dart b/Frontend/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile.dart index bbff0a47..61404c32 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile.dart @@ -37,6 +37,8 @@ class _MihPersonalProfileState extends State { final usernameController = TextEditingController(); final fnameController = TextEditingController(); final lnameController = TextEditingController(); + final purposeController = TextEditingController(); + final ValueNotifier _counter = ValueNotifier(0); PlatformFile? proPic; late ImageProvider? propicPreview; late bool businessUser; @@ -112,12 +114,13 @@ class _MihPersonalProfileState extends State { } Future updateUserApiCall() async { - int responseCode = await MihUserServices().updateUser( + int responseCode = await MihUserServices().updateUserV2( widget.arguments.signedInUser, fnameController.text, lnameController.text, usernameController.text, proPicController.text, + purposeController.text, businessUser, context, ); @@ -184,6 +187,14 @@ class _MihPersonalProfileState extends State { ); } + Color getPurposeLimitColor(int limit) { + if (_counter.value <= limit) { + return MzanziInnovationHub.of(context)!.theme.secondaryColor(); + } else { + return MzanziInnovationHub.of(context)!.theme.errorColor(); + } + } + void editProfileWindow(double width) { showDialog( context: context, @@ -282,6 +293,52 @@ class _MihPersonalProfileState extends State { }, ), const SizedBox(height: 10.0), + MihTextFormField( + height: 250, + fillColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + inputColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + controller: purposeController, + multiLineInput: true, + requiredText: true, + hintText: "Your Purpose", + validator: (value) { + return MihValidationServices() + .validateLength(purposeController.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: getPurposeLimitColor(256), + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(width: 5), + Text( + "/256", + style: TextStyle( + color: getPurposeLimitColor(256), + fontWeight: FontWeight.bold, + ), + ), + ], + ); + }, + ), + ), + const SizedBox(height: 10.0), MihToggle( hintText: "Activate Business Account", initialPostion: businessUser, @@ -336,11 +393,13 @@ class _MihPersonalProfileState extends State { usernameController.dispose(); fnameController.dispose(); lnameController.dispose(); + purposeController.dispose(); super.dispose(); } @override void initState() { + super.initState(); var proPicName = ""; if (widget.arguments.signedInUser.pro_pic_path.isNotEmpty) { proPicName = widget.arguments.signedInUser.pro_pic_path.split("/").last; @@ -350,6 +409,11 @@ class _MihPersonalProfileState extends State { } else { env = "Dev"; } + purposeController.addListener(() { + setState(() { + _counter.value = purposeController.text.characters.length; + }); + }); setState(() { propicPreview = widget.arguments.propicFile; oldProPicName = proPicName; @@ -357,9 +421,9 @@ class _MihPersonalProfileState extends State { fnameController.text = widget.arguments.signedInUser.fname; lnameController.text = widget.arguments.signedInUser.lname; usernameController.text = widget.arguments.signedInUser.username; + purposeController.text = widget.arguments.signedInUser.purpose; businessUser = isBusinessUser(); }); - super.initState(); } @override @@ -404,7 +468,9 @@ class _MihPersonalProfileState extends State { ), FittedBox( child: Text( - "@${widget.arguments.signedInUser.username}", + widget.arguments.signedInUser.username.isNotEmpty + ? widget.arguments.signedInUser.username + : "username", style: TextStyle( fontSize: 35, fontWeight: FontWeight.bold, @@ -416,7 +482,9 @@ class _MihPersonalProfileState extends State { ), FittedBox( child: Text( - "${widget.arguments.signedInUser.fname} ${widget.arguments.signedInUser.lname}", + widget.arguments.signedInUser.fname.isNotEmpty + ? "${widget.arguments.signedInUser.fname} ${widget.arguments.signedInUser.lname}" + : "Name Surname", style: TextStyle( fontSize: 25, fontWeight: FontWeight.bold, @@ -438,20 +506,25 @@ class _MihPersonalProfileState extends State { ), ), ), - // const SizedBox(height: 10.0), - // Center( - // child: Text( - // "*DEMO TEXT* This would be the bio of the user telling us a bit about themself and let. This would be the bio of the user telling us a bit about themself and let. This would be the bio of the user telling us a bit about themself", - // textAlign: TextAlign.center, - // style: TextStyle( - // fontSize: 15, - // fontWeight: FontWeight.bold, - // color: MzanziInnovationHub.of(context)! - // .theme - // .secondaryColor(), - // ), - // ), - // ), + const SizedBox(height: 10.0), + Center( + child: SizedBox( + width: 700, + child: Text( + widget.arguments.signedInUser.purpose.isNotEmpty + ? widget.arguments.signedInUser.purpose + : "No purpose added yet", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ), + ), const SizedBox(height: 30.0), Center( child: MihButton( @@ -463,7 +536,9 @@ class _MihPersonalProfileState extends State { MzanziInnovationHub.of(context)!.theme.successColor(), width: 300, child: Text( - "Edit Profile", + widget.arguments.signedInUser.username.isEmpty + ? "Set Up Profile" + : "Edit Profile", style: TextStyle( color: MzanziInnovationHub.of(context)! .theme diff --git a/Frontend/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile_view.dart b/Frontend/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile_view.dart new file mode 100644 index 00000000..8f0822f5 --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_profile/personal_profile/package_tools/mih_personal_profile_view.dart @@ -0,0 +1,198 @@ +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_objects/app_user.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_icons.dart'; +import 'package:mzansi_innovation_hub/mih_services/mih_file_services.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_package_tool_body.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_circle_avatar.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; + +class MihPersonalProfileView extends StatefulWidget { + final AppUser user; + const MihPersonalProfileView({ + super.key, + required this.user, + }); + + @override + State createState() => _MihPersonalProfileViewState(); +} + +class _MihPersonalProfileViewState extends State { + late Future futureImageUrl; + PlatformFile? file; + + @override + void dispose() { + super.dispose(); + } + + @override + void initState() { + super.initState(); + futureImageUrl = + MihFileApi.getMinioFileUrl(widget.user.pro_pic_path, context); + } + + @override + Widget build(BuildContext context) { + double screenWidth = MediaQuery.of(context).size.width; + return MihPackageToolBody( + borderOn: false, + innerHorizontalPadding: 10, + bodyItem: getBody(screenWidth), + ); + } + + Widget getBody(double width) { + double profilePictureWidth = 150; + return MihSingleChildScroll( + child: Padding( + padding: MzanziInnovationHub.of(context)!.theme.screenType == "desktop" + ? EdgeInsets.symmetric(horizontal: width * 0.2) + : EdgeInsets.symmetric(horizontal: width * 0.075), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + FutureBuilder( + future: futureImageUrl, + builder: (context, asyncSnapshot) { + if (asyncSnapshot.connectionState == ConnectionState.done && + asyncSnapshot.hasData) { + if (asyncSnapshot.requireData != "") { + return MihCircleAvatar( + imageFile: NetworkImage(asyncSnapshot.requireData), + width: profilePictureWidth, + editable: false, + fileNameController: TextEditingController(), + userSelectedfile: file, + frameColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + backgroundColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + onChange: () {}, + ); + } else { + return Icon( + MihIcons.iDontKnow, + size: profilePictureWidth, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ); + } + } else { + return Icon( + MihIcons.mihRing, + size: profilePictureWidth, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ); + } + // return Center( + // child: MihCircleAvatar( + // imageFile: propicPreview, + // width: 150, + // editable: false, + // fileNameController: proPicController, + // userSelectedfile: proPic, + // frameColor: MzanziInnovationHub.of(context)! + // .theme + // .secondaryColor(), + // backgroundColor: + // MzanziInnovationHub.of(context)!.theme.primaryColor(), + // onChange: (selectedImage) { + // setState(() { + // proPic = selectedImage; + // }); + // }, + // ), + // ); + }), + FittedBox( + child: Text( + widget.user.username.isNotEmpty + ? widget.user.username + : "Username", + style: TextStyle( + fontSize: 35, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ), + FittedBox( + child: Text( + widget.user.fname.isNotEmpty + ? "${widget.user.fname} ${widget.user.lname}" + : "Name Surname", + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ), + FittedBox( + child: Text( + widget.user.type.toUpperCase(), + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ), + const SizedBox(height: 10.0), + Center( + child: SizedBox( + width: 700, + child: Text( + widget.user.purpose.isNotEmpty + ? widget.user.purpose + : "No purpose added yet", + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ), + ), + const SizedBox(height: 30.0), + // Center( + // child: MihButton( + // onPressed: () { + // // Connect with the user + // }, + // buttonColor: + // MzanziInnovationHub.of(context)!.theme.successColor(), + // width: 300, + // child: Text( + // widget.user.username.isEmpty + // ? "Set Up Profile" + // : "Edit Profile", + // style: TextStyle( + // color: + // MzanziInnovationHub.of(context)!.theme.primaryColor(), + // fontSize: 20, + // fontWeight: FontWeight.bold, + // ), + // ), + // ), + // ), + ], + ), + ), + ); + } +} diff --git a/Frontend/lib/mih_services/mih_business_details_services.dart b/Frontend/lib/mih_services/mih_business_details_services.dart index 2ca27ed4..7e5f0cf1 100644 --- a/Frontend/lib/mih_services/mih_business_details_services.dart +++ b/Frontend/lib/mih_services/mih_business_details_services.dart @@ -9,6 +9,26 @@ import '../mih_components/mih_pop_up_messages/mih_error_message.dart'; import 'package:supertokens_flutter/http.dart' as http; class MihBusinessDetailsServices { + Future> searchBusinesses( + String searchText, + BuildContext context, + ) async { + var response = await http.get( + Uri.parse("${AppEnviroment.baseApiUrl}/businesses/search/$searchText"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + ); + if (response.statusCode == 200) { + Iterable l = jsonDecode(response.body); + List businesses = + List.from(l.map((model) => Business.fromJson(model))); + return businesses; + } else { + throw Exception('failed to load users'); + } + } + Future getBusinessDetails( String app_id, ) async { @@ -38,6 +58,9 @@ class MihBusinessDetailsServices { String businessPhoneNumber, String businessLocation, String businessLogoFilename, + String businessWebsite, + String businessRating, + String businessMissionVision, BuildContext context, ) async { showDialog( @@ -46,6 +69,9 @@ class MihBusinessDetailsServices { return const Mihloadingcircle(); }, ); + String logoPath = businessLogoFilename.isNotEmpty + ? "$appId/business_files/$businessLogoFilename" + : ""; var response = await http.post( Uri.parse("${AppEnviroment.baseApiUrl}/business/insert/"), headers: { @@ -56,18 +82,73 @@ class MihBusinessDetailsServices { "type": businessType, "registration_no": businessRegistrationNo, "logo_name": businessLogoFilename, - "logo_path": "$appId/business_files/$businessLogoFilename", + "logo_path": logoPath, "contact_no": businessPhoneNumber, "bus_email": businessEmail, "gps_location": businessLocation, "practice_no": businessPracticeNo, "vat_no": businessVatNo, + "website": businessWebsite, + "rating": businessRating, + "mission_vision": businessMissionVision, }), ); Navigator.of(context).pop(); return response; } + Future updateBusinessDetailsV2( + String business_id, + String business_name, + String business_type, + String business_registration_no, + String business_practice_no, + String business_vat_no, + String business_email, + String business_phone_number, + String business_location, + String business_logo_name, + String businessWebsite, + String businessRating, + String businessMissionVision, + BuildContext context, + ) async { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle(); + }, + ); + var response = await http.put( + Uri.parse("${AppEnviroment.baseApiUrl}/business/update/v2/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + body: jsonEncode({ + "business_id": business_id, + "Name": business_name, + "type": business_type, + "registration_no": business_registration_no, + "logo_name": business_logo_name, + "logo_path": "$business_id/business_files/$business_logo_name", + "contact_no": business_phone_number, + "bus_email": business_email, + "gps_location": business_location, + "practice_no": business_practice_no, + "vat_no": business_vat_no, + "website": businessWebsite, + "rating": businessRating, + "mission_vision": businessMissionVision, + }), + ); + Navigator.of(context).pop(); + if (response.statusCode == 200) { + return 200; + } else { + return 500; + } + } + Future updateBusinessDetails( String business_id, String business_name, @@ -110,7 +191,6 @@ class MihBusinessDetailsServices { if (response.statusCode == 200) { return 200; } else { - internetConnectionPopUp(context); return 500; } } diff --git a/Frontend/lib/mih_services/mih_location_services.dart b/Frontend/lib/mih_services/mih_location_services.dart index 2b884e17..4cdc0848 100644 --- a/Frontend/lib/mih_services/mih_location_services.dart +++ b/Frontend/lib/mih_services/mih_location_services.dart @@ -1,4 +1,3 @@ -import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import '../mih_components/mih_pop_up_messages/mih_error_message.dart'; @@ -14,29 +13,20 @@ class MIHLocationAPI { ///if user has blocked permission (denied or denied forver), user will get error pop up. ///if user has granted permission (while in use), function will return Position object. Future getGPSPosition(BuildContext context) async { - showDialog( - context: context, - builder: (context) { - return const Mihloadingcircle(); - }, - ); - //Check the type of permission granted + print("Before checkPermission"); // Debug LocationPermission permission = await Geolocator.checkPermission(); + print("After checkPermission: $permission"); // Debug if (permission == LocationPermission.denied) { - //First time user (auto denied pernission) request permission from user permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { - //User denied permission showPermissionError(context); return null; } else if (permission == LocationPermission.deniedForever) { - //User denied permission Forever showPermissionError(context); return null; } else { Position location = await Geolocator.getCurrentPosition( locationSettings: locationSettings); - //print(location); return location; } } else if (permission == LocationPermission.deniedForever) { @@ -45,15 +35,17 @@ class MIHLocationAPI { } else { Position location = await Geolocator.getCurrentPosition( locationSettings: locationSettings); - //print(location); - Navigator.of(context).pop(); return location; } } - double getDistanceInMeaters(Position startPosition, Position endPosition) { - return Geolocator.distanceBetween(startPosition.latitude, - startPosition.longitude, endPosition.latitude, endPosition.longitude); + double getDistanceInMeaters(String startPosition, String endPosition) { + double startLatitude = double.parse(startPosition.split(", ")[0]); + double startLogitude = double.parse(startPosition.split(", ")[1]); + double endLatitude = double.parse(endPosition.split(", ")[0]); + double endLogitude = double.parse(endPosition.split(", ")[1]); + return Geolocator.distanceBetween( + startLatitude, startLogitude, endLatitude, endLogitude); } void showPermissionError(BuildContext context) { diff --git a/Frontend/lib/mih_services/mih_user_services.dart b/Frontend/lib/mih_services/mih_user_services.dart index 1432da4d..01d1ebb4 100644 --- a/Frontend/lib/mih_services/mih_user_services.dart +++ b/Frontend/lib/mih_services/mih_user_services.dart @@ -63,6 +63,26 @@ class MihUserServices { } } + Future> searchUsers( + String searchText, + BuildContext context, + ) async { + var response = await http.get( + Uri.parse("${AppEnviroment.baseApiUrl}/users/search/$searchText"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + ); + if (response.statusCode == 200) { + Iterable l = jsonDecode(response.body); + List users = + List.from(l.map((model) => AppUser.fromJson(model))); + return users; + } else { + throw Exception('failed to load users'); + } + } + Future getUserDetails( String app_id, BuildContext context, @@ -83,6 +103,46 @@ class MihUserServices { } } + Future updateUserV2( + AppUser signedInUser, + String firstName, + String lastName, + String username, + String profilePicture, + String purpose, + bool isBusinessUser, + BuildContext context, + ) async { + var fileName = profilePicture.replaceAll(RegExp(r' '), '-'); + var filePath = "${signedInUser.app_id}/profile_files/$fileName"; + String profileType; + if (isBusinessUser) { + profileType = "business"; + } else { + profileType = "personal"; + } + var response = await http.put( + Uri.parse("${AppEnviroment.baseApiUrl}/user/update/v2/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + body: jsonEncode({ + "idusers": signedInUser.idUser, + "username": username, + "fnam": firstName, + "lname": lastName, + "type": profileType, + "pro_pic_path": filePath, + "purpose": purpose, + }), + ); + if (response.statusCode == 200) { + return response.statusCode; + } else { + return response.statusCode; + } + } + Future updateUser( AppUser signedInUser, String firstName, diff --git a/Frontend/lib/mih_services/mih_validation_services.dart b/Frontend/lib/mih_services/mih_validation_services.dart index 36595cee..d7085268 100644 --- a/Frontend/lib/mih_services/mih_validation_services.dart +++ b/Frontend/lib/mih_services/mih_validation_services.dart @@ -16,6 +16,18 @@ class MihValidationServices { return null; } + String? validateWebsite(String? website, bool required) { + final websiteRegex = RegExp( + r'^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$'); + if (!required && website!.isEmpty) { + return null; + } + if (!websiteRegex.hasMatch(website!)) { + return "Invalid Website Format"; + } + return null; + } + String? validateEmail(String? email) { if (email == null || email.isEmpty) { return "Email is required"; diff --git a/backend/routers/business.py b/backend/routers/business.py index 6bdcf54c..0dc993d0 100644 --- a/backend/routers/business.py +++ b/backend/routers/business.py @@ -27,6 +27,9 @@ class businessInsertRequest(BaseModel): gps_location: str practice_no: str vat_no: str + website: str + rating: str + mission_vision: str class businessUpdateRequest(BaseModel): business_id: str @@ -41,6 +44,60 @@ class businessUpdateRequest(BaseModel): practice_no: str vat_no: str +class businessUpdateRequestV2(BaseModel): + business_id: str + Name: str + type: str + registration_no: str + logo_name: str + logo_path: str + contact_no: str + bus_email: str + gps_location: str + practice_no: str + vat_no: str + website: str + rating: str + mission_vision: str + + +# Get List of all files +@router.get("/businesses/search/{search}", tags=["MIH Business"]) +async def read_all_businesses(search: str, session: SessionContainer = Depends(verify_session())): #, session: SessionContainer = Depends(verify_session()) + db = database.dbConnection.dbAppDataConnect() + cursor = db.cursor() + query = "SELECT business.business_id, business.Name, business.type, business.registration_no, " + query += "business.logo_name, business.logo_path, business.contact_no, business.bus_email, " + query += "business.gps_location, " + 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" + search_term = f"%{search.lower()}%" # Add wildcards and lowercase + cursor.execute(query, (search_term, search_term)) + items = [ + { + "business_id": item[0], + "Name": item[1], + "type": item[2], + "registration_no": item[3], + "logo_name": item[4], + "logo_path": item[5], + "contact_no": item[6], + "bus_email": item[7], + "app_id": "", + "gps_location": item[8], + "practice_no": item[9], + "vat_no": item[10], + "website": item[11], + "rating": item[12], + "mission_vision": item[13], + } + for item in cursor.fetchall() + ] + cursor.close() + db.close() + return items # Get List of all files @router.get("/business/business_id/{business_id}", tags=["MIH Business"]) @@ -50,7 +107,8 @@ async def read_business_by_business_id(business_id: str, session: SessionContain query = "SELECT business.business_id, business.Name, business.type, business.registration_no, " query += "business.logo_name, business.logo_path, business.contact_no, business.bus_email, " query += "business_users.app_id, business.gps_location, " - query += "practice_no, vat_no " + query += "practice_no, vat_no, " + query += "website, rating, mission_vision " query += "FROM business " query += "inner join business_users " query += "on business.business_id=business_users.business_id " @@ -73,6 +131,9 @@ async def read_business_by_business_id(business_id: str, session: SessionContain "gps_location": item[9], "practice_no": item[10], "vat_no": item[11], + "website": item[12], + "rating": item[13], + "mission_vision": item[14], } for item in cursor.fetchall() ] @@ -93,7 +154,8 @@ async def read_business_by_app_id(app_id: str, session: SessionContainer = Depen query = "SELECT business.business_id, business.Name, business.type, business.registration_no, " query += "business.logo_name, business.logo_path, business.contact_no, business.bus_email, " query += "business_users.app_id, business.gps_location, " - query += "practice_no, vat_no " + query += "practice_no, vat_no, " + query += "website, rating, mission_vision " query += "FROM business " query += "inner join business_users " query += "on business.business_id=business_users.business_id " @@ -116,6 +178,9 @@ async def read_business_by_app_id(app_id: str, session: SessionContainer = Depen "gps_location": item[9], "practice_no": item[10], "vat_no": item[11], + "website": item[12], + "rating": item[13], + "mission_vision": item[14], } for item in cursor.fetchall() ] @@ -132,8 +197,8 @@ async def insert_business_details(itemRequest : businessInsertRequest, session: db = database.dbConnection.dbAppDataConnect() cursor = db.cursor() query = "insert into business " - query += "(business_id, Name, type, registration_no, logo_name, logo_path, contact_no, bus_email, gps_location, practice_no, vat_no) " - query += "values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" + query += "(business_id, Name, type, registration_no, logo_name, logo_path, contact_no, bus_email, gps_location, practice_no, vat_no, website, rating, mission_vision) " + query += "values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" uuidString = str(uuid.uuid1()) userData = (uuidString, itemRequest.Name, @@ -145,9 +210,12 @@ async def insert_business_details(itemRequest : businessInsertRequest, session: itemRequest.bus_email, itemRequest.gps_location, itemRequest.practice_no, - itemRequest.vat_no) + itemRequest.vat_no, + itemRequest.website, + itemRequest.rating, + itemRequest.mission_vision, + ) try: - print(query) print(userData) cursor.execute(query, userData) @@ -187,4 +255,37 @@ async def Update_Business_details(itemRequest : businessUpdateRequest, session: db.commit() cursor.close() db.close() + return {"message": "Successfully Updated Record"} + +@router.put("/business/update/v2/", tags=["MIH Business"]) +async def Update_Business_details(itemRequest : businessUpdateRequestV2, session: SessionContainer = Depends(verify_session())): #, session: SessionContainer = Depends(verify_session()) + db = database.dbConnection.dbAppDataConnect() + # print(itemRequest.gps_location) + cursor = db.cursor() + query = "update business " + query += "set Name=%s, type=%s, registration_no=%s, logo_name=%s, logo_path=%s, contact_no=%s, bus_email=%s, gps_location=%s, practice_no=%s, vat_no=%s, website=%s, rating=%s, mission_vision=%s " + query += "where business_id=%s" + userData = (itemRequest.Name, + itemRequest.type, + itemRequest.registration_no, + itemRequest.logo_name, + itemRequest.logo_path, + itemRequest.contact_no, + itemRequest.bus_email, + itemRequest.gps_location, + itemRequest.practice_no, + itemRequest.vat_no, + itemRequest.website, + itemRequest.rating, + itemRequest.mission_vision, + itemRequest.business_id, + ) + try: + cursor.execute(query, userData) + except Exception as error: + raise HTTPException(status_code=404, detail=error) + #return {"query": query, "message": error} + db.commit() + cursor.close() + db.close() return {"message": "Successfully Updated Record"} \ No newline at end of file diff --git a/backend/routers/users.py b/backend/routers/users.py index bb3c1311..7c51120e 100644 --- a/backend/routers/users.py +++ b/backend/routers/users.py @@ -22,6 +22,15 @@ class userInsertRequest(BaseModel): email: str app_id: str +class userUpdateRequestV2(BaseModel): + idusers: int + username: str + fnam: str + lname: str + type: str + pro_pic_path: str + purpose: str + class userUpdateRequest(BaseModel): idusers: int username: str @@ -76,6 +85,7 @@ async def read_all_users(search: str, session: SessionContainer = Depends(verify "app_id": item[5], "username": item[6], "pro_pic_path": item[7], + "purpose": item[8], } for item in cursor.fetchall() ] @@ -113,6 +123,7 @@ async def read_users_by_app_id(app_id: str, session: SessionContainer = Depends( "app_id": item[5], "username": item[6], "pro_pic_path": item[7], + "purpose": item[8], } for item in cursor.fetchall() ] @@ -126,10 +137,10 @@ async def insert_User_details(itemRequest : userInsertRequest, session: SessionC db = database.dbConnection.dbAppDataConnect() cursor = db.cursor() query = "insert into users " - query += "(email, fname, lname, type, app_id, username, pro_pic_path) " - query += "values (%s, %s, %s, %s, %s, %s, %s)" + query += "(email, fname, lname, type, app_id, username, pro_pic_path, purpose) " + query += "values (%s, %s, %s, %s, %s, %s, %s, %s)" userData = (itemRequest.email,"","","personal", - itemRequest.app_id, "", "") + itemRequest.app_id, "", "","") try: cursor.execute(query, userData) except Exception as error: @@ -140,6 +151,32 @@ async def insert_User_details(itemRequest : userInsertRequest, session: SessionC db.close() return {"message": "Successfully Created Record"} +# Update User on table +@router.put("/user/update/v2/", tags=["MIH Users"]) +async def Update_User_details(itemRequest : userUpdateRequestV2, session: SessionContainer = Depends(verify_session())): + db = database.dbConnection.dbAppDataConnect() + cursor = db.cursor() + query = "update users " + query += "set username=%s, fname=%s, lname=%s, type=%s, pro_pic_path=%s, purpose=%s " + query += "where idusers=%s" + userData = (itemRequest.username, + itemRequest.fnam, + itemRequest.lname, + itemRequest.type, + itemRequest.pro_pic_path, + itemRequest.purpose, + itemRequest.idusers, + ) + try: + cursor.execute(query, userData) + except Exception as error: + raise HTTPException(status_code=404, detail=error) + #return {"query": query, "message": error} + db.commit() + cursor.close() + db.close() + return {"message": "Successfully Updated Record"} + # Update User on table @router.put("/user/update/", tags=["MIH Users"]) async def Update_User_details(itemRequest : userUpdateRequest, session: SessionContainer = Depends(verify_session())):