diff --git a/Frontend/lib/mih_apis/mih_api_calls.dart b/Frontend/lib/mih_apis/mih_api_calls.dart index 071004f8..c3e61326 100644 --- a/Frontend/lib/mih_apis/mih_api_calls.dart +++ b/Frontend/lib/mih_apis/mih_api_calls.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:mzansi_innovation_hub/mih_apis/mih_file_api.dart'; import 'package:mzansi_innovation_hub/mih_apis/mih_notification_apis.dart'; import 'package:flutter/material.dart'; // import '../mih_components/mih_pop_up_messages/mih_error_message.dart'; @@ -43,7 +44,10 @@ class MIHApiCalls { /// - Business User details. /// - notifications. /// - user profile picture. - Future getProfile(int notificationAmount) async { + Future getProfile( + int notificationAmount, + BuildContext context, + ) async { AppUser userData; Business? busData; BusinessUser? bUserData; @@ -98,20 +102,8 @@ class MIHApiCalls { // userPic = "${AppEnviroment.baseFileUrl}/mih/${userData.pro_pic_path}"; // } else { - var url = - "${AppEnviroment.baseApiUrl}/minio/pull/file/${AppEnviroment.getEnv()}/${userData.pro_pic_path}"; - var response = await http.get(Uri.parse(url)); - - if (response.statusCode == 200) { - String body = response.body; - var decodedData = jsonDecode(body); - - userPic = decodedData['minioURL']; - } else { - userPic = ""; - // throw Exception( - // "Error: GetUserData status code ${response.statusCode}"); - } + userPic = + await MihFileApi.getMinioFileUrl(userData.pro_pic_path, context); } //Get Notifications diff --git a/Frontend/lib/mih_apis/mih_business_details_apis.dart b/Frontend/lib/mih_apis/mih_business_details_apis.dart new file mode 100644 index 00000000..eec80814 --- /dev/null +++ b/Frontend/lib/mih_apis/mih_business_details_apis.dart @@ -0,0 +1,65 @@ +import 'dart:convert'; + +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/mih_env/env.dart'; +import '../mih_components/mih_pop_up_messages/mih_error_message.dart'; +import 'package:supertokens_flutter/http.dart' as http; + +class MihBusinessDetailsApi { + Future updateBusinessDetails( + 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, + BuildContext context, + ) async { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle(); + }, + ); + var response = await http.put( + Uri.parse("${AppEnviroment.baseApiUrl}/business/update/"), + 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, + }), + ); + Navigator.of(context).pop(); + if (response.statusCode == 200) { + return 200; + } else { + internetConnectionPopUp(context); + return 500; + } + } + + void internetConnectionPopUp(BuildContext context) { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Internet Connection"); + }, + ); + } +} diff --git a/Frontend/lib/mih_apis/mih_file_api.dart b/Frontend/lib/mih_apis/mih_file_api.dart new file mode 100644 index 00000000..1823d987 --- /dev/null +++ b/Frontend/lib/mih_apis/mih_file_api.dart @@ -0,0 +1,143 @@ +import 'dart:convert'; + +import 'package:file_picker/file_picker.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_error_message.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_success_message.dart'; +import 'package:mzansi_innovation_hub/mih_env/env.dart'; +import 'package:flutter/material.dart'; +import 'package:supertokens_flutter/http.dart' as http; +import 'package:http/http.dart' as http2; +import 'package:supertokens_flutter/supertokens.dart'; + +class MihFileApi { + final baseAPI = AppEnviroment.baseApiUrl; + + static Future getMinioFileUrl( + String filePath, + BuildContext context, + ) async { + // loadingPopUp(context); + // print("here"); + // var url = + // "${AppEnviroment.baseApiUrl}/minio/pull/file/${AppEnviroment.getEnv()}/$filePath"; + // var response = await http.get(Uri.parse(url)); + String fileUrl = ""; + // print(response.statusCode); + // if (response.statusCode == 200) { + // String body = response.body; + // var decodedData = jsonDecode(body); + + // fileUrl = decodedData['minioURL']; + // } else { + // fileUrl = ""; + // } + // Navigator.of(context).pop(); // Pop loading dialog + // return fileUrl; + try { + var url = + "${AppEnviroment.baseApiUrl}/minio/pull/file/${AppEnviroment.getEnv()}/$filePath"; + var response = await http.get(Uri.parse(url)); + if (response.statusCode == 200) { + var decodedData = jsonDecode(response.body); + fileUrl = decodedData['minioURL']; + } else { + internetConnectionPopUp(context); + } + } catch (e) { + internetConnectionPopUp(context); + } finally { + // Navigator.of(context).pop(); // Always pop loading dialog + } + return fileUrl; + } + + static Future uploadFile( + String app_id, + String folderName, + PlatformFile? file, + BuildContext context, + ) async { + loadingPopUp(context); + var token = await SuperTokens.getAccessToken(); + var request = http2.MultipartRequest( + 'POST', Uri.parse("${AppEnviroment.baseApiUrl}/minio/upload/file/")); + request.headers['accept'] = 'application/json'; + request.headers['Authorization'] = 'Bearer $token'; + request.headers['Content-Type'] = 'multipart/form-data'; + request.fields['app_id'] = app_id; + request.fields['folder'] = folderName; + request.files.add(await http2.MultipartFile.fromBytes('file', file!.bytes!, + filename: file.name.replaceAll(RegExp(r' '), '-'))); + var response = await request.send(); + Navigator.of(context).pop(); // Pop loading dialog + return response.statusCode; + } + + static Future deleteFile( + String app_id, + String folderName, + String fileName, + BuildContext context, + ) async { + loadingPopUp(context); + var fname = fileName.replaceAll(RegExp(r' '), '-'); + var filePath = "$app_id/$folderName/$fname"; + var response = await http.delete( + Uri.parse("${AppEnviroment.baseApiUrl}/minio/delete/file/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + body: jsonEncode({"file_path": filePath}), + ); + Navigator.of(context).pop(); // Pop loading dialog + return response.statusCode; + } + + // Future signOut() async { + // await SuperTokens.signOut(completionHandler: (error) { + // print(error); + // }); + // if (await SuperTokens.doesSessionExist() == false) { + // Navigator.of(context).pop(); + // Navigator.of(context).popAndPushNamed( + // '/', + // arguments: AuthArguments(true, false), + // ); + // } + // } + +//================== POP UPS ========================================================================== + + static void internetConnectionPopUp(BuildContext context) { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage( + errorType: "Internet Connection", + ); + }, + ); + } + + static void successPopUp(String message, BuildContext context) { + showDialog( + context: context, + builder: (context) { + return MIHSuccessMessage( + successType: "Success", + successMessage: message, + ); + }, + ); + } + + static void loadingPopUp(BuildContext context) { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle(); + }, + ); + } +} diff --git a/Frontend/lib/mih_apis/mih_my_business_user_apis.dart b/Frontend/lib/mih_apis/mih_my_business_user_apis.dart new file mode 100644 index 00000000..ca85baae --- /dev/null +++ b/Frontend/lib/mih_apis/mih_my_business_user_apis.dart @@ -0,0 +1,75 @@ +import 'dart:convert'; + +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/mih_env/env.dart'; +import '../mih_components/mih_pop_up_messages/mih_error_message.dart'; +import 'package:supertokens_flutter/http.dart' as http; + +class MihMyBusinessUserApi { + /// This function updates the business user details. + Future updateBusinessDetails( + String app_id, + String business_id, + String bUserTitle, + String bUserAccess, + String signatureFileName, + BuildContext context, + ) async { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle(); + }, + ); + var response = await http.put( + Uri.parse("${AppEnviroment.baseApiUrl}/business-user/update/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + body: jsonEncode({ + "business_id": business_id, + "app_id": app_id, + "signature": signatureFileName, + "sig_path": "$app_id/business_files/$signatureFileName", + "title": bUserTitle, + "access": bUserAccess, + }), + ); + // var response = await http.put( + // Uri.parse("${AppEnviroment.baseApiUrl}/business/update/"), + // 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, + // }), + // ); + Navigator.of(context).pop(); + if (response.statusCode == 200) { + return 200; + } else { + internetConnectionPopUp(context); + return 500; + } + } + + void internetConnectionPopUp(BuildContext context) { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Internet Connection"); + }, + ); + } +} diff --git a/Frontend/lib/mih_components/mih_package_components/test/package_test.dart b/Frontend/lib/mih_components/mih_package_components/Example/package_test.dart similarity index 97% rename from Frontend/lib/mih_components/mih_package_components/test/package_test.dart rename to Frontend/lib/mih_components/mih_package_components/Example/package_test.dart index a91025a1..474a33fb 100644 --- a/Frontend/lib/mih_components/mih_package_components/test/package_test.dart +++ b/Frontend/lib/mih_components/mih_package_components/Example/package_test.dart @@ -3,8 +3,8 @@ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_app_action.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_app_alert.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_app_tools.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/test/package_tools/package_tool_one.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/test/package_tools/package_tool_two.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/Example/package_tools/package_tool_two.dart'; import 'package:mzansi_innovation_hub/mih_objects/arguments.dart'; import 'package:flutter/material.dart'; diff --git a/Frontend/lib/mih_components/mih_package_components/test/package_tools/package_tool_one.dart b/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart similarity index 68% rename from Frontend/lib/mih_components/mih_package_components/test/package_tools/package_tool_one.dart rename to Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart index bcaea3fb..ea56d9f5 100644 --- a/Frontend/lib/mih_components/mih_package_components/test/package_tools/package_tool_one.dart +++ b/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_one.dart @@ -1,11 +1,15 @@ +import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_inputs_and_buttons/mih_text_input.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_layout/mih_single_child_scroll.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih-app_tool_body.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_app_window.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_floating_menu.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_image_display.dart'; class PackageToolOne extends StatefulWidget { const PackageToolOne({super.key}); @@ -15,6 +19,11 @@ class PackageToolOne extends StatefulWidget { } class _PackageToolOneState extends State { + late ImageProvider? imagePreview; + PlatformFile? file; + PlatformFile? imageFile; + TextEditingController _fileNameController = TextEditingController(); + TextEditingController _imagefileController = TextEditingController(); void showTestFullWindow() { showDialog( context: context, @@ -49,6 +58,15 @@ class _PackageToolOneState extends State { ); } + @override + void initState() { + super.initState(); + setState(() { + imagePreview = const NetworkImage( + "https://lh3.googleusercontent.com/nW4ZZ89Q1ATz7Ht3nsAVWXL_cwNi4gNusqQZiL60UuuI3FG-VM7bTYDoJ-sUr2kDTdorfQYjxo5PjDM-0MO5rA=s512"); + }); + } + @override Widget build(BuildContext context) { return MihAppToolBody( @@ -97,7 +115,49 @@ class _PackageToolOneState extends State { MzanziInnovationHub.of(context)!.theme.primaryColor(), ), ), - ) + ), + const SizedBox(height: 10), + MihCircleAvatar( + imageFile: imagePreview, + width: 50, + editable: false, + fileNameController: _fileNameController, + userSelectedfile: file, + frameColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + onChange: (selectedImage) { + setState(() { + file = selectedImage; + }); + }, + ), + const SizedBox(height: 10), + MIHTextField( + controller: _fileNameController, + hintText: "Selected Avatar File", + editable: false, + required: false, + ), + const SizedBox(height: 10), + MihImageDisplay( + imageFile: imagePreview, + width: 300, + editable: true, + fileNameController: _imagefileController, + userSelectedfile: imageFile, + onChange: (selectedFile) { + setState(() { + imageFile = selectedFile; + }); + }, + ), + const SizedBox(height: 10), + MIHTextField( + controller: _imagefileController, + hintText: "Selected Image File", + editable: false, + required: false, + ), ], ), ), diff --git a/Frontend/lib/mih_components/mih_package_components/test/package_tools/package_tool_two.dart b/Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_two.dart similarity index 100% rename from Frontend/lib/mih_components/mih_package_components/test/package_tools/package_tool_two.dart rename to Frontend/lib/mih_components/mih_package_components/Example/package_tools/package_tool_two.dart 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 new file mode 100644 index 00000000..61674c4c --- /dev/null +++ b/Frontend/lib/mih_components/mih_package_components/mih_circle_avatar.dart @@ -0,0 +1,130 @@ +import 'dart:io'; + +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_package_components/mih_icons.dart'; + +class MihCircleAvatar extends StatefulWidget { + final ImageProvider? imageFile; + final double width; + final bool editable; + final TextEditingController fileNameController; + final onChange; + final PlatformFile? userSelectedfile; + final Color frameColor; + const MihCircleAvatar({ + super.key, + required this.imageFile, + required this.width, + required this.editable, + required this.fileNameController, + required this.userSelectedfile, + required this.frameColor, + required this.onChange, + }); + + @override + State createState() => _MihCircleAvatarState(); +} + +class _MihCircleAvatarState extends State { + late ImageProvider? imagePreview; + + @override + void initState() { + super.initState(); + setState(() { + imagePreview = widget.imageFile; + }); + } + + @override + Widget build(BuildContext context) { + return Container( + // color: Colors.white, + alignment: Alignment.center, + width: widget.width, + height: widget.width, + child: Stack( + alignment: Alignment.center, + children: [ + CircleAvatar( + radius: widget.width / 2.2, + backgroundColor: widget.frameColor, + backgroundImage: imagePreview, + ), + FittedBox( + fit: BoxFit.fill, + child: Icon( + size: widget.width, + MihIcons.mihCircleFrame, + color: widget.frameColor, + ), + ), + Visibility( + visible: widget.editable, + child: Positioned( + bottom: 0, + right: 0, + child: IconButton.filled( + onPressed: () async { + try { + FilePickerResult? result = + await FilePicker.platform.pickFiles( + type: FileType.image, + ); + // print("Here 1"); + if (MzanziInnovationHub.of(context)!.theme.getPlatform() == + "Web") { + // print("Here 2"); + if (result == null) return; + // print("Here 3"); + PlatformFile? selectedFile = result.files.first; + setState(() { + // print("Here 4"); + widget.onChange(selectedFile); + // print("Here 5"); + imagePreview = MemoryImage(selectedFile.bytes!); + }); + + setState(() { + widget.fileNameController.text = selectedFile.name; + }); + } else { + if (result != null) { + File file = File(result.files.single.path!); + PlatformFile? androidFile = PlatformFile( + path: file.path, + name: file.path.split('/').last, + size: file.lengthSync(), + bytes: await file.readAsBytes(), // Read file bytes + //extension: fileExtension, + ); + setState(() { + widget.onChange(androidFile); + imagePreview = FileImage(file); + }); + + setState(() { + widget.fileNameController.text = + file.path.split('/').last; + }); + } else { + print("here in else"); + // User canceled the picker + } + } + } catch (e) { + print("Error: $e"); + } + }, + icon: const Icon(Icons.edit), + ), + ), + ), + ], + ), + ); + } +} diff --git a/Frontend/lib/mih_components/mih_package_components/mih_image_display.dart b/Frontend/lib/mih_components/mih_package_components/mih_image_display.dart new file mode 100644 index 00000000..c7e75f81 --- /dev/null +++ b/Frontend/lib/mih_components/mih_package_components/mih_image_display.dart @@ -0,0 +1,124 @@ +import 'dart:io'; + +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/main.dart'; + +class MihImageDisplay extends StatefulWidget { + final ImageProvider? imageFile; + final double width; + final bool editable; + final TextEditingController fileNameController; + final onChange; + final PlatformFile? userSelectedfile; + const MihImageDisplay({ + super.key, + required this.imageFile, + required this.width, + required this.editable, + required this.fileNameController, + required this.userSelectedfile, + required this.onChange, + }); + + @override + State createState() => _MihImageDisplayState(); +} + +class _MihImageDisplayState extends State { + late ImageProvider? imagePreview; + + @override + void initState() { + super.initState(); + setState(() { + imagePreview = widget.imageFile; + }); + } + + @override + Widget build(BuildContext context) { + return Container( + // color: Colors.white, + alignment: Alignment.center, + width: widget.width, + child: Stack( + alignment: Alignment.center, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(widget.width * 0.1), + child: Image(image: imagePreview!), + ), + Visibility( + visible: widget.editable, + child: Positioned( + bottom: 0, + right: 0, + child: IconButton.filled( + style: IconButton.styleFrom( + backgroundColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + color: MzanziInnovationHub.of(context)!.theme.primaryColor(), + onPressed: () async { + try { + FilePickerResult? result = + await FilePicker.platform.pickFiles( + type: FileType.image, + ); + // print("Here 1"); + if (MzanziInnovationHub.of(context)!.theme.getPlatform() == + "Web") { + // print("Here 2"); + if (result == null) return; + // print("Here 3"); + PlatformFile? selectedFile = result.files.first; + setState(() { + // print("Here 4"); + widget.onChange(selectedFile); + // print("Here 5"); + imagePreview = MemoryImage(selectedFile.bytes!); + }); + + setState(() { + widget.fileNameController.text = selectedFile.name; + }); + } else { + if (result != null) { + File file = File(result.files.single.path!); + PlatformFile? androidFile = PlatformFile( + path: file.path, + name: file.path.split('/').last, + size: file.lengthSync(), + bytes: await file.readAsBytes(), // Read file bytes + //extension: fileExtension, + ); + setState(() { + widget.onChange(androidFile); + imagePreview = FileImage(file); + }); + + setState(() { + widget.fileNameController.text = + file.path.split('/').last; + }); + } else { + print("here in else"); + // User canceled the picker + } + } + } catch (e) { + print("Error: $e"); + } + }, + icon: const Icon( + Icons.edit, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/Frontend/lib/mih_packages/mih_home/components/mih_app_drawer.dart b/Frontend/lib/mih_packages/mih_home/components/mih_app_drawer.dart index df0f1845..a7c92dd5 100644 --- a/Frontend/lib/mih_packages/mih_home/components/mih_app_drawer.dart +++ b/Frontend/lib/mih_packages/mih_home/components/mih_app_drawer.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.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 '../../../main.dart'; import '../../../mih_objects/app_user.dart'; import '../../../mih_objects/arguments.dart'; import 'package:supertokens_flutter/supertokens.dart'; -import '../../../mih_components/mih_profile_picture.dart'; class MIHAppDrawer extends StatefulWidget { final AppUser signedInUser; @@ -53,17 +53,26 @@ class _MIHAppDrawerState extends State { widget.signedInUser, widget.propicFile), ); }, - child: MIHProfilePicture( - profilePictureFile: widget.propicFile, - proPicController: proPicController, - proPic: null, + child: MihCircleAvatar( + imageFile: widget.propicFile, width: 60, - radius: 27, - drawerMode: true, editable: false, + fileNameController: proPicController, + onChange: (_) {}, + userSelectedfile: null, frameColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), - onChange: (newProPic) {}, ), + // MIHProfilePicture( + // profilePictureFile: widget.propicFile, + // proPicController: proPicController, + // proPic: null, + // width: 60, + // radius: 27, + // drawerMode: true, + // editable: false, + // frameColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), + // onChange: (newProPic) {}, + // ), // Stack( // alignment: Alignment.center, diff --git a/Frontend/lib/mih_packages/mih_home/mih_home.dart b/Frontend/lib/mih_packages/mih_home/mih_home.dart index aeb6e56b..b4628fdd 100644 --- a/Frontend/lib/mih_packages/mih_home/mih_home.dart +++ b/Frontend/lib/mih_packages/mih_home/mih_home.dart @@ -2,7 +2,7 @@ import 'package:mzansi_innovation_hub/main.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_app.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_app_action.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_app_tools.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_profile_picture.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_circle_avatar.dart'; import 'package:mzansi_innovation_hub/mih_objects/app_user.dart'; import 'package:mzansi_innovation_hub/mih_objects/business.dart'; import 'package:mzansi_innovation_hub/mih_objects/business_user.dart'; @@ -101,17 +101,27 @@ class _MihHomeState extends State { return MihAppAction( icon: Padding( padding: const EdgeInsets.only(left: 5.0), - child: MIHProfilePicture( - profilePictureFile: widget.propicFile, - proPicController: proPicController, - proPic: null, - width: 45, - radius: 21, - drawerMode: false, + child: MihCircleAvatar( + imageFile: widget.propicFile, + width: 50, editable: false, + fileNameController: proPicController, + userSelectedfile: null, + // frameColor: frameColor, frameColor: MzanziInnovationHub.of(context)!.theme.secondaryColor(), - onChange: (newProPic) {}, + onChange: (_) {}, ), + // MIHProfilePicture( + // profilePictureFile: widget.propicFile, + // proPicController: proPicController, + // proPic: null, + // width: 45, + // radius: 21, + // drawerMode: false, + // editable: false, + // frameColor: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + // onChange: (newProPic) {}, + // ), ), iconSize: 45, onTap: () { diff --git a/Frontend/lib/mih_packages/mih_home/mih_profile_getter.dart b/Frontend/lib/mih_packages/mih_home/mih_profile_getter.dart index 2ed53067..95fb57c2 100644 --- a/Frontend/lib/mih_packages/mih_home/mih_profile_getter.dart +++ b/Frontend/lib/mih_packages/mih_home/mih_profile_getter.dart @@ -232,7 +232,7 @@ class _MIHProfileGetterState extends State { @override void initState() { //profile = getProfile(); - profile = MIHApiCalls().getProfile(amount); + profile = MIHApiCalls().getProfile(amount, context); super.initState(); } diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile.dart index 776be5d5..cfcdce15 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/mzansi_business_profile.dart @@ -2,10 +2,11 @@ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_app_action.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_app_tools.dart'; import 'package:mzansi_innovation_hub/mih_objects/arguments.dart'; -import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_profile.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_user_search.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/package_tools/mih_my_business_team.dart'; import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_profile/business_profile/package_tools/mih_my_business_user.dart'; class MzansiBusinessProfile extends StatefulWidget { final BusinessArguments arguments; @@ -53,16 +54,26 @@ class _MzansiBusinessProfileState extends State { _selcetedIndex = 0; }); }; - temp[const Icon(Icons.people_outline)] = () { + temp[const Icon(Icons.person)] = () { setState(() { _selcetedIndex = 1; }); }; - temp[const Icon(Icons.add)] = () { + // temp[const Icon(Icons.warning)] = () { + // setState(() { + // _selcetedIndex = 2; + // }); + // }; + temp[const Icon(Icons.people)] = () { setState(() { _selcetedIndex = 2; }); }; + temp[const Icon(Icons.add)] = () { + setState(() { + _selcetedIndex = 3; + }); + }; return MihAppTools( tools: temp, selcetedIndex: _selcetedIndex, @@ -71,7 +82,9 @@ class _MzansiBusinessProfileState extends State { List getToolBody() { List toolBodies = [ - MihBusinessProfile(arguments: widget.arguments), + MihBusinessDetails(arguments: widget.arguments), + MihMyBusinessUser(arguments: widget.arguments), + // MihBusinessProfile(arguments: widget.arguments), MihMyBusinessTeam(arguments: widget.arguments), MihBusinessUserSearch(arguments: widget.arguments), ]; 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 new file mode 100644 index 00000000..ed3a9568 --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_details.dart @@ -0,0 +1,403 @@ +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_apis/mih_business_details_apis.dart'; +import 'package:mzansi_innovation_hub/mih_apis/mih_file_api.dart'; +import 'package:mzansi_innovation_hub/mih_apis/mih_location_api.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_inputs_and_buttons/mih_button.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_inputs_and_buttons/mih_text_input.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_layout/mih_single_child_scroll.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih-app_tool_body.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_app_alert.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_components/mih_pop_up_messages/mih_error_message.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_success_message.dart'; +import 'package:mzansi_innovation_hub/mih_objects/arguments.dart'; + +class MihBusinessDetails extends StatefulWidget { + final BusinessArguments arguments; + const MihBusinessDetails({ + super.key, + required this.arguments, + }); + + @override + State createState() => _MihBusinessDetailsState(); +} + +class _MihBusinessDetailsState extends State { + late Future fileUrlFuture; + PlatformFile? imageFile; + final fileNameController = TextEditingController(); + final regController = TextEditingController(); + final nameController = TextEditingController(); + final typeController = TextEditingController(); + final practiceNoController = TextEditingController(); + final vatNoController = TextEditingController(); + final contactController = TextEditingController(); + final emailController = TextEditingController(); + final locationController = TextEditingController(); + + Future submitForm() async { + if (!isEmailValid()) { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Invalid Email"); + }, + ); + } else if (isFormFilled()) { + int statusCode = 0; + statusCode = await MihBusinessDetailsApi().updateBusinessDetails( + widget.arguments.business!.business_id, + nameController.text, + typeController.text, + regController.text, + practiceNoController.text, + vatNoController.text, + emailController.text, + contactController.text, + locationController.text, + fileNameController.text, + context, + ); + if (statusCode == 200) { + bool successfullyUploadedFile = await uploadFile(); + if (successfullyUploadedFile) { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + Navigator.of(context).pushNamed( + '/', + arguments: AuthArguments( + false, + false, + ), + ); + // File uploaded successfully + showDialog( + context: context, + builder: (context) { + return const MIHSuccessMessage( + successType: "Success", + successMessage: "Business details updated successfully", + ); + }, + ); + } else { + // File upload failed + showDialog( + context: context, + builder: (context) { + return MihAppAlert( + alertIcon: Icon( + Icons.warning_rounded, + color: MzanziInnovationHub.of(context)!.theme.errorColor(), + ), + alertTitle: "Error Updating Business Details", + alertBody: Column( + children: [ + Text( + "An error occurred while updating the business details. Please check internet connection and try again.", + style: TextStyle( + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ], + ), + alertColour: + MzanziInnovationHub.of(context)!.theme.errorColor(), + ); + }, + ); + } + } else { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Internet Connection"); + }, + ); + } + } else { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Input Error"); + }, + ); + } + } + + Future uploadFile() async { + if (imageFile != null) { + int uploadStatusCode = 0; + uploadStatusCode = await MihFileApi.uploadFile( + widget.arguments.business!.business_id, + "business_files", + imageFile!, + context, + ); + if (uploadStatusCode == 200) { + int deleteStatusCode = 0; + deleteStatusCode = await MihFileApi.deleteFile( + widget.arguments.business!.logo_path.split("/").first, + "business_files", + widget.arguments.business!.logo_path.split("/").last, + context, + ); + if (deleteStatusCode == 200) { + return true; + } else { + return false; + } + } else { + return false; + } + } else { + return true; // No file selected, so no upload needed + } + } + + bool isFileSelected() { + if (imageFile != null) { + return true; + } else { + return false; + } + } + + bool isEmailValid() { + String text = emailController.text; + var regex = RegExp(r'^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$'); + return regex.hasMatch(text); + } + + bool isFormFilled() { + if (regController.text.isEmpty || + nameController.text.isEmpty || + typeController.text.isEmpty || + practiceNoController.text.isEmpty || + vatNoController.text.isEmpty || + contactController.text.isEmpty || + emailController.text.isEmpty || + locationController.text.isEmpty) { + return false; + } else { + return true; + } + } + + @override + void dispose() { + super.dispose(); + fileNameController.dispose(); + regController.dispose(); + nameController.dispose(); + typeController.dispose(); + practiceNoController.dispose(); + vatNoController.dispose(); + contactController.dispose(); + emailController.dispose(); + locationController.dispose(); + imageFile = null; + } + + @override + void initState() { + super.initState(); + setState(() { + fileNameController.text = + widget.arguments.business!.logo_path.split("/").last; + regController.text = widget.arguments.business!.registration_no; + nameController.text = widget.arguments.business!.Name; + typeController.text = widget.arguments.business!.type; + practiceNoController.text = widget.arguments.business!.practice_no; + vatNoController.text = widget.arguments.business!.vat_no; + contactController.text = widget.arguments.business!.contact_no; + emailController.text = widget.arguments.business!.bus_email; + locationController.text = widget.arguments.business!.gps_location; + }); + fileUrlFuture = MihFileApi.getMinioFileUrl( + widget.arguments.business!.logo_path, + context, + ); + } + + @override + Widget build(BuildContext context) { + return MihAppToolBody( + borderOn: true, + bodyItem: getBody(context), + ); + } + + Widget getBody(BuildContext context) { + return MihSingleChildScroll( + child: Column( + children: [ + const Text( + "Business Details", + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + ), + ), + Divider(color: MzanziInnovationHub.of(context)?.theme.secondaryColor()), + const SizedBox(height: 10), + FutureBuilder( + future: fileUrlFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Container( + // alignment: Alignment.center, + width: 150, + height: 150, + child: FittedBox( + alignment: Alignment.center, + fit: BoxFit.fill, + child: Icon( + MihIcons.mihCircleFrame, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + ); + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData && + snapshot.data != null && + snapshot.data.toString().isNotEmpty) { + return MihCircleAvatar( + imageFile: NetworkImage(snapshot.data.toString()), + width: 150, + editable: true, + fileNameController: fileNameController, + userSelectedfile: imageFile, + frameColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + onChange: (selectedfile) { + setState(() { + imageFile = selectedfile; + }); + }, + ); + } else if (snapshot.hasError) { + return Text("Error: ${snapshot.error}"); + } else { + return const Text("Error loading image"); + } + }, + ), + Visibility( + visible: false, + child: MIHTextField( + controller: fileNameController, + hintText: "Selected File Name", + editable: false, + required: false, + ), + ), + const SizedBox(height: 20), + MIHTextField( + controller: regController, + hintText: "Registration No.", + editable: true, + required: true, + ), + const SizedBox(height: 10), + MIHTextField( + controller: nameController, + hintText: "Business Name", + editable: true, + required: true, + ), + const SizedBox(height: 10), + MIHTextField( + controller: typeController, + hintText: "Business Type", + editable: true, + required: true, + ), + const SizedBox(height: 10), + MIHTextField( + controller: practiceNoController, + hintText: "Practice Number", + editable: true, + required: true, + ), + const SizedBox(height: 10), + MIHTextField( + controller: vatNoController, + hintText: "VAT Number", + editable: true, + required: true, + ), + const SizedBox(height: 10), + MIHTextField( + controller: contactController, + hintText: "Contact Number", + editable: true, + required: true, + ), + const SizedBox(height: 10), + MIHTextField( + controller: emailController, + hintText: "Email", + editable: true, + required: true, + ), + const SizedBox(height: 10), + Row( + children: [ + Flexible( + child: MIHTextField( + controller: locationController, + hintText: "Location", + editable: false, + required: false, + ), + ), + const SizedBox(width: 10.0), + SizedBox( + width: 100.0, + height: 50.0, + child: MIHButton( + buttonText: "Set", + buttonColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + onTap: () { + MIHLocationAPI().getGPSPosition(context).then((position) { + if (position != null) { + setState(() { + locationController.text = + "${position.latitude}, ${position.longitude}"; + }); + } + }); + }, + ), + ), + ], + ), + const SizedBox(height: 15), + SizedBox( + width: 500.0, + height: 50.0, + child: MIHButton( + buttonText: "Update", + buttonColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), + onTap: () { + submitForm(); + }, + ), + ), + ], + )); + } +} diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_profile.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_profile.dart index f5791d2b..bab86b4a 100644 --- a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_profile.dart +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_business_profile.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_apis/mih_file_api.dart'; import 'package:mzansi_innovation_hub/mih_apis/mih_location_api.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_inputs_and_buttons/mih_button.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart'; @@ -11,14 +12,13 @@ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih- import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_error_message.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_success_message.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_profile_picture.dart'; import 'package:mzansi_innovation_hub/mih_env/env.dart'; import 'package:mzansi_innovation_hub/mih_objects/arguments.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:supertokens_flutter/http.dart' as http; -import 'package:http/http.dart' as http2; -import 'package:supertokens_flutter/supertokens.dart'; class MihBusinessProfile extends StatefulWidget { final BusinessArguments arguments; @@ -52,14 +52,18 @@ class _MihBusinessProfileState extends State { late PlatformFile? selectedLogo = null; late PlatformFile? selectedSignature = null; + PlatformFile? logoFile; + ImageProvider? logoPreview = null; final ValueNotifier busType = ValueNotifier(""); late String business_id; late String oldLogoPath; late String oldSigPath; + String logoUri = ""; Future updateBusinessProfileAPICall(String business_id) async { + print("inside update business profile api call"); showDialog( context: context, builder: (context) { @@ -89,9 +93,8 @@ class _MihBusinessProfileState extends State { if (response.statusCode == 200) { //var businessResponse = jsonDecode(response.body); //print(selectedLogo != null); - if (selectedLogo != null) { - uploadSelectedFile(selectedLogo, logonameController); - deleteFileApiCall(oldLogoPath); + if (logoFile != null) { + uploadSelectedFile(logoFile); } updateBusinessUserAPICall(business_id); } else { @@ -117,7 +120,7 @@ class _MihBusinessProfileState extends State { ); if (response.statusCode == 200) { if (selectedSignature != null) { - uploadSelectedFile(selectedSignature, signtureController); + uploadSelectedFile(selectedSignature); deleteFileApiCall(oldSigPath); } Navigator.of(context).pop(); @@ -138,23 +141,16 @@ class _MihBusinessProfileState extends State { } } - Future uploadSelectedFile( - PlatformFile? file, TextEditingController controller) async { - //to-do delete file when changed - var token = await SuperTokens.getAccessToken(); - //print(t); - //print("here1"); - var request = http2.MultipartRequest( - 'POST', Uri.parse("${AppEnviroment.baseApiUrl}/minio/upload/file/")); - request.headers['accept'] = 'application/json'; - request.headers['Authorization'] = 'Bearer $token'; - request.headers['Content-Type'] = 'multipart/form-data'; - request.fields['app_id'] = widget.arguments.signedInUser.app_id; - request.fields['folder'] = "business_files"; - request.files.add(await http2.MultipartFile.fromBytes('file', file!.bytes!, - filename: file.name.replaceAll(RegExp(r' '), '-'))); - var response1 = await request.send(); - if (response1.statusCode == 200) { + Future uploadSelectedFile(PlatformFile? file) async { + print("Inside upload selected file"); + var response = await MihFileApi.uploadFile( + widget.arguments.signedInUser.app_id, + "business_files", + file, + context, + ); + if (response == 200) { + deleteFileApiCall(oldLogoPath); } else { internetConnectionPopUp(); } @@ -162,16 +158,13 @@ class _MihBusinessProfileState extends State { Future deleteFileApiCall(String filePath) async { // delete file from minio - var response = await http.delete( - Uri.parse("$baseAPI/minio/delete/file/"), - headers: { - "Content-Type": "application/json; charset=UTF-8" - }, - body: jsonEncode({"file_path": filePath}), + var response = await MihFileApi.deleteFile( + widget.arguments.signedInUser.app_id, + "business_files", + filePath.split("/").last, + context, ); - //print("Here4"); - //print(response.statusCode); - if (response.statusCode == 200) { + if (response == 200) { //SQL delete } else { internetConnectionPopUp(); @@ -222,6 +215,7 @@ class _MihBusinessProfileState extends State { if (!validEmail()) { emailError(); } else if (isFieldsFilled()) { + print("inside submit form mthod"); updateBusinessProfileAPICall(business_id); } else { showDialog( @@ -263,6 +257,20 @@ class _MihBusinessProfileState extends State { ); } + ImageProvider? isPictureAvailable(String url) { + print("logo Url: $url"); + if (url == "") { + return const AssetImage( + 'lib/mih_components/mih_package_components/assets/images/i-dont-know-2.png'); + } else if (url != "") { + return NetworkImage(url); + } else { + return const AssetImage( + 'lib/mih_components/mih_package_components/assets/images/i-dont-know-2.png'); + // return null; + } + } + @override void dispose() { nameController.dispose(); @@ -306,6 +314,15 @@ class _MihBusinessProfileState extends State { practiceNoController.text = widget.arguments.business!.practice_no; vatNoController.text = widget.arguments.business!.vat_no; }); + MihFileApi.getMinioFileUrl( + widget.arguments.business!.logo_path, + context, + ).then((value) { + setState(() { + logoUri = value; + }); + logoPreview = isPictureAvailable(logoUri); + }); super.initState(); } @@ -343,8 +360,48 @@ class _MihBusinessProfileState extends State { ), ), Divider( - color: - MzanziInnovationHub.of(context)?.theme.secondaryColor(), + color: MzanziInnovationHub.of(context) + ?.theme + .secondaryColor()), + const SizedBox(height: 10.0), + MIHProfilePicture( + profilePictureFile: logoPreview, + proPicController: logonameController, + proPic: logoFile, + width: 155, + radius: 70, + drawerMode: false, + editable: true, + frameColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + onChange: (newProPic) { + setState(() { + logoFile = newProPic; + }); + print("logoFile: ${logoFile?.bytes}"); + }, + ), + const SizedBox(height: 10.0), + MIHFileField( + controller: logonameController, + hintText: "Logo", + editable: false, + required: true, + onPressed: () async { + FilePickerResult? result = + await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ['jpg', 'png'], + ); + if (result == null) return; + final selectedFile = result.files.first; + setState(() { + selectedLogo = selectedFile; + }); + setState(() { + logonameController.text = selectedFile.name; + }); + }, ), const SizedBox(height: 10.0), MIHTextField( @@ -407,28 +464,6 @@ class _MihBusinessProfileState extends State { required: true, ), const SizedBox(height: 10.0), - MIHFileField( - controller: logonameController, - hintText: "Logo", - editable: false, - required: true, - onPressed: () async { - FilePickerResult? result = - await FilePicker.platform.pickFiles( - type: FileType.custom, - allowedExtensions: ['jpg', 'png', 'pdf'], - ); - if (result == null) return; - final selectedFile = result.files.first; - setState(() { - selectedLogo = selectedFile; - }); - setState(() { - logonameController.text = selectedFile.name; - }); - }, - ), - const SizedBox(height: 10.0), Row( children: [ Flexible( @@ -489,7 +524,7 @@ class _MihBusinessProfileState extends State { MIHDropdownField( controller: titleController, hintText: "Title", - dropdownOptions: const ["Doctor", "Assistant"], + dropdownOptions: const ["Doctor", "Assistant", "Other"], required: true, editable: true, enableSearch: false, @@ -551,6 +586,7 @@ class _MihBusinessProfileState extends State { MzanziInnovationHub.of(context)!.theme.primaryColor(), onTap: () { //print(business_id); + print("submit form call"); submitForm(business_id); }, ), diff --git a/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_my_business_user.dart b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_my_business_user.dart new file mode 100644 index 00000000..eef5bbef --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_profile/business_profile/package_tools/mih_my_business_user.dart @@ -0,0 +1,399 @@ +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_apis/mih_file_api.dart'; +import 'package:mzansi_innovation_hub/mih_apis/mih_my_business_user_apis.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_inputs_and_buttons/mih_button.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_inputs_and_buttons/mih_text_input.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_layout/mih_single_child_scroll.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih-app_tool_body.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_app_alert.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_components/mih_package_components/mih_image_display.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_error_message.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_success_message.dart'; +import 'package:mzansi_innovation_hub/mih_objects/arguments.dart'; + +class MihMyBusinessUser extends StatefulWidget { + final BusinessArguments arguments; + const MihMyBusinessUser({ + super.key, + required this.arguments, + }); + + @override + State createState() => _MihMyBusinessUserState(); +} + +class _MihMyBusinessUserState extends State { + late Future userPicUrlFuture; + late Future userSignatureUrlFuture; + PlatformFile? userPicFile; + PlatformFile? userSignatureFile; + final fileNameController = TextEditingController(); + final titleDropdownController = TextEditingController(); + final titleTextController = TextEditingController(); + final fnameController = TextEditingController(); + final lnameController = TextEditingController(); + final accessController = TextEditingController(); + final signtureController = TextEditingController(); + + bool isFormFilled() { + if (signtureController.text.isEmpty || + titleDropdownController.text.isEmpty || + titleTextController.text.isEmpty || + fnameController.text.isEmpty || + lnameController.text.isEmpty || + accessController.text.isEmpty) { + return false; + } else { + return true; + } + } + + Future uploadFile() async { + if (userSignatureFile != null) { + print(userSignatureFile!.name); + print(userSignatureFile!.bytes); + int uploadStatusCode = 0; + uploadStatusCode = await MihFileApi.uploadFile( + widget.arguments.signedInUser.app_id, + "business_files", + userSignatureFile!, + context, + ); + if (uploadStatusCode == 200) { + int deleteStatusCode = 0; + deleteStatusCode = await MihFileApi.deleteFile( + widget.arguments.signedInUser.app_id, + "business_files", + widget.arguments.businessUser!.sig_path.split("/").last, + context, + ); + if (deleteStatusCode == 200) { + return true; + } else { + return false; + } + } else { + return false; + } + } else { + return true; // No file selected, so no upload needed + } + } + + Future submitForm() async { + print("Here 1"); + if (isFormFilled()) { + print("Here 1"); + int statusCode = await MihMyBusinessUserApi().updateBusinessDetails( + widget.arguments.signedInUser.app_id, + widget.arguments.businessUser!.business_id, + titleDropdownController.text, + accessController.text, + signtureController.text, + context, + ); + if (statusCode == 200) { + print("Here 1"); + bool successfullyUploadedFile = await uploadFile(); + print("Here 4"); + if (successfullyUploadedFile) { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + Navigator.of(context).pushNamed( + '/', + arguments: AuthArguments( + false, + false, + ), + ); + // File uploaded successfully + showDialog( + context: context, + builder: (context) { + return const MIHSuccessMessage( + successType: "Success", + successMessage: "Business details updated successfully", + ); + }, + ); + } else { + // File upload failed + showDialog( + context: context, + builder: (context) { + return MihAppAlert( + alertIcon: Icon( + Icons.warning_rounded, + color: MzanziInnovationHub.of(context)!.theme.errorColor(), + ), + alertTitle: "Error Updating Business User Details", + alertBody: Column( + children: [ + Text( + "An error occurred while updating the business User details. Please check internet connection and try again.", + style: TextStyle( + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ], + ), + alertColour: + MzanziInnovationHub.of(context)!.theme.errorColor(), + ); + }, + ); + } + } else { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Internet Connection"); + }, + ); + } + } else { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Input Error"); + }, + ); + } + } + + @override + void dispose() { + super.dispose(); + fileNameController.dispose(); + titleDropdownController.dispose(); + titleTextController.dispose(); + fnameController.dispose(); + lnameController.dispose(); + accessController.dispose(); + signtureController.dispose(); + userPicFile = null; + userSignatureFile = null; + } + + @override + void initState() { + super.initState(); + setState(() { + fileNameController.text = + widget.arguments.signedInUser.pro_pic_path.split("/").last; + signtureController.text = + widget.arguments.businessUser!.sig_path.split("/").last; + titleDropdownController.text = widget.arguments.businessUser!.title; + titleTextController.text = widget.arguments.businessUser!.title; + fnameController.text = widget.arguments.signedInUser.fname; + lnameController.text = widget.arguments.signedInUser.lname; + accessController.text = widget.arguments.businessUser!.access; + }); + userPicUrlFuture = MihFileApi.getMinioFileUrl( + widget.arguments.signedInUser.pro_pic_path, + context, + ); + userSignatureUrlFuture = MihFileApi.getMinioFileUrl( + widget.arguments.businessUser!.sig_path, + context, + ); + } + + @override + Widget build(BuildContext context) { + return MihAppToolBody( + borderOn: true, + bodyItem: getBody(), + ); + } + + Widget getBody() { + return MihSingleChildScroll( + child: Column( + children: [ + const Text( + "My Business User", + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + ), + ), + Divider( + color: MzanziInnovationHub.of(context)?.theme.secondaryColor()), + const SizedBox(height: 10), + FutureBuilder( + future: userPicUrlFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Container( + // alignment: Alignment.center, + width: 150, + height: 150, + child: FittedBox( + alignment: Alignment.center, + fit: BoxFit.fill, + child: Icon( + MihIcons.mihCircleFrame, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ); + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData && + snapshot.data != null && + snapshot.data.toString().isNotEmpty) { + return MihCircleAvatar( + imageFile: NetworkImage(snapshot.data.toString()), + width: 150, + editable: false, + fileNameController: fileNameController, + userSelectedfile: userPicFile, + frameColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + onChange: (_) {}, + ); + } else if (snapshot.hasError) { + return Text("Error: ${snapshot.error}"); + } else { + return const Text("Error loading image"); + } + }, + ), + Visibility( + visible: false, + child: MIHTextField( + controller: fileNameController, + hintText: "Selected File Name", + editable: false, + required: false, + ), + ), + const SizedBox(height: 20), + MIHDropdownField( + controller: titleDropdownController, + hintText: "Title", + dropdownOptions: const ["Doctor", "Assistant", "Other"], + required: true, + editable: true, + enableSearch: false, + ), + const SizedBox(height: 10), + MIHTextField( + controller: titleTextController, + hintText: "Other Title", + editable: true, + required: true, + ), + const SizedBox(height: 10), + MIHTextField( + controller: fnameController, + hintText: "Name", + editable: false, + required: true, + ), + const SizedBox(height: 10), + MIHTextField( + controller: lnameController, + hintText: "Surname", + editable: false, + required: true, + ), + const SizedBox(height: 10), + MIHTextField( + controller: accessController, + hintText: "Access Level", + editable: false, + required: true, + ), + const SizedBox(height: 10), + Container( + width: 300, + alignment: Alignment.topLeft, + child: const Text( + "Signature", + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + ), + ), + ), + FutureBuilder( + future: userSignatureUrlFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Container( + // alignment: Alignment.center, + width: 300, + height: 200, + child: FittedBox( + alignment: Alignment.center, + fit: BoxFit.fill, + child: Icon( + MihIcons.mihCircleFrame, + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + ), + ), + ); + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData && + snapshot.data != null && + snapshot.data.toString().isNotEmpty) { + return MihImageDisplay( + imageFile: NetworkImage(snapshot.data.toString()), + width: 300, + editable: true, + fileNameController: signtureController, + userSelectedfile: userSignatureFile, + onChange: (selectedFile) { + setState(() { + userSignatureFile = selectedFile; + }); + }, + ); + } else if (snapshot.hasError) { + return Text("Error: ${snapshot.error}"); + } else { + return const Text("Error loading image"); + } + }, + ), + const SizedBox(height: 10), + Visibility( + visible: false, + child: MIHTextField( + controller: signtureController, + hintText: "Selected Signature File", + editable: false, + required: true, + ), + ), + const SizedBox(height: 15), + SizedBox( + width: 500.0, + height: 50.0, + child: MIHButton( + buttonText: "Update", + buttonColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(), + onTap: () { + submitForm(); + }, + ), + ), + ], + ), + ); + } +} 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 82a8ea2d..ae7a9eac 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 @@ -1,22 +1,20 @@ import 'dart:convert'; import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_apis/mih_file_api.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_inputs_and_buttons/mih_button.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_inputs_and_buttons/mih_file_input.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_inputs_and_buttons/mih_text_input.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_layout/mih_single_child_scroll.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih-app_tool_body.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_circle_avatar.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_error_message.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; import 'package:mzansi_innovation_hub/mih_components/mih_pop_up_messages/mih_success_message.dart'; -import 'package:mzansi_innovation_hub/mih_components/mih_profile_picture.dart'; import 'package:mzansi_innovation_hub/mih_env/env.dart'; import 'package:mzansi_innovation_hub/mih_objects/arguments.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; -import 'package:supertokens_flutter/supertokens.dart'; import 'package:supertokens_flutter/http.dart' as http; -import 'package:http/http.dart' as http2; class MihPersonalProfile extends StatefulWidget { final AppProfileUpdateArguments arguments; @@ -82,33 +80,14 @@ class _MihPersonalProfileState extends State { } Future uploadSelectedFile(PlatformFile? file) async { - //print("MIH Profile Picture: $file"); - //var strem = new http.ByteStream.fromBytes(file.bytes.) - //start loading circle - showDialog( - context: context, - builder: (context) { - return const Mihloadingcircle(); - }, + var response = await MihFileApi.uploadFile( + widget.arguments.signedInUser.app_id, + "profile_files", + file, + context, ); - var token = await SuperTokens.getAccessToken(); - var request = http2.MultipartRequest( - 'POST', Uri.parse("${AppEnviroment.baseApiUrl}/minio/upload/file/")); - request.headers['accept'] = 'application/json'; - request.headers['Authorization'] = 'Bearer $token'; - request.headers['Content-Type'] = 'multipart/form-data'; - request.fields['app_id'] = widget.arguments.signedInUser.app_id; - request.fields['folder'] = "profile_files"; - request.files.add(await http2.MultipartFile.fromBytes('file', file!.bytes!, - filename: file.name.replaceAll(RegExp(r' '), '-'))); - var response1 = await request.send(); - if (response1.statusCode == 200) { + if (response == 200) { deleteFileApiCall(oldProPicName); - // end loading circle - //Navigator.of(context).pop(); - // String message = - // "The file ${file.name.replaceAll(RegExp(r' '), '-')} has been successfully generated and added to ${widget.signedInUser.fname} ${widget.signedInUser.lname}'s record. You can now access and download it for their use."; - // successPopUp(message); } else { internetConnectionPopUp(); } @@ -153,11 +132,6 @@ class _MihPersonalProfileState extends State { false, ), ); - // Navigator.of(context).pushNamed( - // '/mzansi-profile', - // arguments: AppProfileUpdateArguments( - // widget.arguments.signedInUser, widget.arguments.propicFile), - // ); String message = "${widget.arguments.signedInUser.email}'s information has been updated successfully!"; successPopUp(message); @@ -168,20 +142,13 @@ class _MihPersonalProfileState extends State { } Future deleteFileApiCall(String filename) async { - // delete file from minio - var fname = filename.replaceAll(RegExp(r' '), '-'); - var filePath = - "${widget.arguments.signedInUser.app_id}/profile_files/$fname"; - var response = await http.delete( - Uri.parse("${AppEnviroment.baseApiUrl}/minio/delete/file/"), - headers: { - "Content-Type": "application/json; charset=UTF-8" - }, - body: jsonEncode({"file_path": filePath}), + var response = await MihFileApi.deleteFile( + widget.arguments.signedInUser.app_id, + "profile_files", + filename, + context, ); - //print("Here4"); - //print(response.statusCode); - if (response.statusCode == 200) { + if (response == 200) { //SQL delete } else { internetConnectionPopUp(); @@ -258,21 +225,34 @@ class _MihPersonalProfileState extends State { child: Column( children: [ //displayProPic(), - MIHProfilePicture( - profilePictureFile: widget.arguments.propicFile, - proPicController: proPicController, - proPic: proPic, - width: 155, - radius: 70, - drawerMode: false, + MihCircleAvatar( + imageFile: propicPreview, + width: 150, editable: true, + fileNameController: proPicController, + userSelectedfile: proPic, frameColor: MzanziInnovationHub.of(context)!.theme.secondaryColor(), - onChange: (newProPic) { + onChange: (selectedImage) { setState(() { - proPic = newProPic; + proPic = selectedImage; }); }, ), + // MIHProfilePicture( + // profilePictureFile: widget.arguments.propicFile, + // proPicController: proPicController, + // proPic: proPic, + // width: 155, + // radius: 70, + // drawerMode: false, + // editable: true, + // frameColor: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + // onChange: (newProPic) { + // setState(() { + // proPic = newProPic; + // }); + // }, + // ), const SizedBox(height: 25.0), Visibility( visible: false, diff --git a/Frontend/lib/mih_router/routeGenerator.dart b/Frontend/lib/mih_router/routeGenerator.dart index 5a687d3e..05331d43 100644 --- a/Frontend/lib/mih_router/routeGenerator.dart +++ b/Frontend/lib/mih_router/routeGenerator.dart @@ -1,4 +1,4 @@ -import 'package:mzansi_innovation_hub/mih_components/mih_package_components/test/package_test.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/Example/package_test.dart'; import 'package:mzansi_innovation_hub/mih_packages/about_mih/about_mih.dart'; import 'package:mzansi_innovation_hub/mih_packages/about_mih/mih_policy_tos_ext/mih_privacy_polocy_external.dart'; import 'package:mzansi_innovation_hub/mih_packages/about_mih/mih_policy_tos_ext/mih_terms_of_service_external.dart'; diff --git a/backend/Minio_Storage/minioConnection.py b/backend/Minio_Storage/minioConnection.py index 0ec200a1..36aa03b9 100644 --- a/backend/Minio_Storage/minioConnection.py +++ b/backend/Minio_Storage/minioConnection.py @@ -1,7 +1,7 @@ from minio import Minio def minioConnect(env): - if(env == "Dev"): + # if(env == "Dev"): return Minio( "minio:9000", # "minio.mzansi-innovation-hub.co.za", @@ -9,11 +9,11 @@ def minioConnect(env): secret_key="nEED72ZlKYgDqH9Iy46fVGGT9TfabGWO", secure=False ) - else: - return Minio( - #"minio:9000", - "minio.mzansi-innovation-hub.co.za", - access_key="0RcgutfvcDq28lz7", - secret_key="nEED72ZlKYgDqH9Iy46fVGGT9TfabGWO", - secure=True - ) \ No newline at end of file + # else: + # return Minio( + # #"minio:9000", + # "minio.mzansi-innovation-hub.co.za", + # access_key="0RcgutfvcDq28lz7", + # secret_key="nEED72ZlKYgDqH9Iy46fVGGT9TfabGWO", + # secure=True + # ) \ No newline at end of file diff --git a/backend/routers/fileStorage.py b/backend/routers/fileStorage.py index e4e7e42e..8ae1c48a 100644 --- a/backend/routers/fileStorage.py +++ b/backend/routers/fileStorage.py @@ -183,7 +183,7 @@ async def upload_perscription_to_user(requestItem: claimStatementUploud, session return {"message": "Successfully Generated File"} def uploudFile(app_id, folder, fileName, extension, content): - client = Minio_Storage.minioConnection.minioConnect("Dev") + client = Minio_Storage.minioConnection.minioConnect("Prod") found = client.bucket_exists("mih") if not found: client.make_bucket("mih")