From c5e40aea8f6b70076bd6db7cbe6f35f185e2ad76 Mon Sep 17 00:00:00 2001 From: yaso-meth Date: Tue, 10 Sep 2024 16:24:42 +0200 Subject: [PATCH] add user search for adding employee --- .../builders/buildEmployeeList.dart | 62 +++++ .../components/builders/buildUserList.dart | 261 ++++++++++++++++++ .../lib/pages/manageBusinessProfile.dart | 244 +++++++++++++++- 3 files changed, 556 insertions(+), 11 deletions(-) create mode 100644 Frontend/patient_manager/lib/components/builders/buildUserList.dart diff --git a/Frontend/patient_manager/lib/components/builders/buildEmployeeList.dart b/Frontend/patient_manager/lib/components/builders/buildEmployeeList.dart index a4667f84..239388af 100644 --- a/Frontend/patient_manager/lib/components/builders/buildEmployeeList.dart +++ b/Frontend/patient_manager/lib/components/builders/buildEmployeeList.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:patient_manager/components/inputsAndButtons/mihButton.dart'; import 'package:patient_manager/components/inputsAndButtons/mihDropdownInput.dart'; import 'package:patient_manager/components/inputsAndButtons/mihTextInput.dart'; +import 'package:patient_manager/components/popUpMessages/mihDeleteMessage.dart'; import 'package:patient_manager/components/popUpMessages/mihErrorMessage.dart'; import 'package:patient_manager/components/popUpMessages/mihLoadingCircle.dart'; import 'package:patient_manager/components/popUpMessages/mihSuccessMessage.dart'; @@ -75,6 +76,39 @@ class _BuildEmployeeListState extends State { } } + Future deleteNoteApiCall(int index) async { + var response = await http.delete( + Uri.parse("$baseAPI/business-user/employees/delete/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + body: jsonEncode({ + "business_id": widget.employees[index].business_id, + "app_id": widget.employees[index].app_id, + }), + ); + //print("Here4"); + //print(response.statusCode); + if (response.statusCode == 200) { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + Navigator.of(context).pop(); + Navigator.of(context).pushNamed( + '/business-profile/manage', + arguments: BusinessArguments( + widget.arguments.signedInUser, + widget.arguments.businessUser, + widget.arguments.business, + ), + ); + String message = + "The employee has been deleted successfully. This means it will no longer have access to your business profile"; + successPopUp(message); + } else { + internetConnectionPopUp(); + } + } + void internetConnectionPopUp() { showDialog( context: context, @@ -204,12 +238,40 @@ class _BuildEmployeeListState extends State { ), ), ), + Positioned( + top: 5, + left: 5, + width: 50, + height: 50, + child: IconButton( + onPressed: () { + showDeleteWarning(index); + }, + icon: Icon( + Icons.delete, + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + size: 35, + ), + ), + ), ], ), ), ); } + void showDeleteWarning(int index) { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => MIHDeleteMessage( + deleteType: "Employee", + onTap: () { + deleteNoteApiCall(index); + })); + } + @override void dispose() { accessController.dispose(); diff --git a/Frontend/patient_manager/lib/components/builders/buildUserList.dart b/Frontend/patient_manager/lib/components/builders/buildUserList.dart new file mode 100644 index 00000000..3a5fc141 --- /dev/null +++ b/Frontend/patient_manager/lib/components/builders/buildUserList.dart @@ -0,0 +1,261 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:patient_manager/components/inputsAndButtons/mihButton.dart'; +import 'package:patient_manager/components/inputsAndButtons/mihDropdownInput.dart'; +import 'package:patient_manager/components/inputsAndButtons/mihTextInput.dart'; +import 'package:patient_manager/components/popUpMessages/mihErrorMessage.dart'; +import 'package:patient_manager/components/popUpMessages/mihLoadingCircle.dart'; +import 'package:patient_manager/components/popUpMessages/mihSuccessMessage.dart'; +import 'package:patient_manager/env/env.dart'; +import 'package:patient_manager/main.dart'; +import 'package:patient_manager/objects/appUser.dart'; +import 'package:patient_manager/objects/arguments.dart'; +import 'package:supertokens_flutter/http.dart' as http; + +class BuildUserList extends StatefulWidget { + final List users; + final BusinessArguments arguments; + + const BuildUserList({ + super.key, + required this.users, + required this.arguments, + }); + + @override + State createState() => _BuildUserListState(); +} + +class _BuildUserListState extends State { + TextEditingController accessController = TextEditingController(); + TextEditingController typeController = TextEditingController(); + TextEditingController fnameController = TextEditingController(); + TextEditingController lnameController = TextEditingController(); + + final baseAPI = AppEnviroment.baseApiUrl; + + Future createBusinessUserAPICall(int index) async { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle(); + }, + ); + var response = await http.post( + Uri.parse("$baseAPI/business-user/insert/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + body: jsonEncode({ + "business_id": widget.arguments.business!.business_id, + "app_id": widget.users[index].app_id, + "signature": "", + "sig_path": "", + "title": typeController.text, + "access": accessController.text, + }), + ); + if (response.statusCode == 201) { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + Navigator.of(context).pop(); + Navigator.of(context).pushNamed( + '/business-profile/manage', + arguments: BusinessArguments( + widget.arguments.signedInUser, + widget.arguments.businessUser, + widget.arguments.business, + ), + ); + String message = + "${widget.users[index].username} is now apart of your team with ${accessController.text} access to ${widget.arguments.business!.Name}"; + successPopUp(message); + } else { + internetConnectionPopUp(); + } + } + + void internetConnectionPopUp() { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Internet Connection"); + }, + ); + } + + void successPopUp(String message) { + showDialog( + context: context, + builder: (context) { + return MIHSuccessMessage( + successType: "Success", + successMessage: message, + ); + }, + ); + } + + String hideEmail(String email) { + var firstLetter = email[0]; + var end = email.split("@")[1]; + return "$firstLetter********@$end"; + } + + void addEmployeePopUp(int index) { + setState(() { + //accessController.text = widget.users[index].access; + //typeController.text = widget.users[index].title; + // var fnameInitial = widget.users[index].fname[0]; + // var lnameInitial = widget.users[index].lname[0]; + fnameController.text = widget.users[index].username; + lnameController.text = hideEmail(widget.users[index].email); + }); + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => Dialog( + child: Stack( + children: [ + Container( + padding: const EdgeInsets.all(10.0), + width: 700.0, + //height: 475.0, + decoration: BoxDecoration( + color: MzanziInnovationHub.of(context)!.theme.primaryColor(), + borderRadius: BorderRadius.circular(25.0), + border: Border.all( + color: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + width: 5.0), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Add Employee", + textAlign: TextAlign.center, + style: TextStyle( + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + fontSize: 35.0, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 25.0), + MIHTextField( + controller: fnameController, + hintText: "Username Name", + editable: false, + required: true, + ), + const SizedBox(height: 10.0), + MIHTextField( + controller: lnameController, + hintText: "Email", + editable: false, + required: true, + ), + const SizedBox(height: 10.0), + MIHDropdownField( + controller: typeController, + hintText: "Title", + dropdownOptions: const ["Doctor", "Assistant"], + required: true, + editable: true, + ), + const SizedBox(height: 10.0), + MIHDropdownField( + controller: accessController, + hintText: "Access", + dropdownOptions: const ["Full", "Partial"], + required: true, + editable: true, + ), + const SizedBox(height: 30.0), + SizedBox( + width: 300, + height: 50, + child: MIHButton( + buttonText: "Add", + buttonColor: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + textColor: MzanziInnovationHub.of(context)! + .theme + .primaryColor(), + onTap: () { + createBusinessUserAPICall(index); + }, + ), + ) + ], + ), + ), + ), + Positioned( + top: 5, + right: 5, + width: 50, + height: 50, + child: IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: Icon( + Icons.close, + color: MzanziInnovationHub.of(context)!.theme.errorColor(), + size: 35, + ), + ), + ), + ], + ), + ), + ); + } + + @override + void dispose() { + accessController.dispose(); + typeController.dispose(); + fnameController.dispose(); + lnameController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ListView.separated( + separatorBuilder: (BuildContext context, index) { + return Divider( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ); + }, + itemCount: widget.users.length, + itemBuilder: (context, index) { + var isYou = ""; + if (widget.arguments.signedInUser.app_id == + widget.users[index].app_id) { + isYou = "(You)"; + } + return ListTile( + title: Text("@${widget.users[index].username} $isYou"), + subtitle: Text( + "Email: ${hideEmail(widget.users[index].email)}", + style: TextStyle( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + ), + onTap: () { + addEmployeePopUp(index); + }, + ); + }, + ); + } +} diff --git a/Frontend/patient_manager/lib/pages/manageBusinessProfile.dart b/Frontend/patient_manager/lib/pages/manageBusinessProfile.dart index 5f68216a..6ec3fdc2 100644 --- a/Frontend/patient_manager/lib/pages/manageBusinessProfile.dart +++ b/Frontend/patient_manager/lib/pages/manageBusinessProfile.dart @@ -1,12 +1,16 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:patient_manager/components/builders/buildEmployeeList.dart'; +import 'package:patient_manager/components/builders/buildUserList.dart'; +import 'package:patient_manager/components/inputsAndButtons/mihSearchInput.dart'; import 'package:patient_manager/components/popUpMessages/mihLoadingCircle.dart'; import 'package:patient_manager/components/popUpMessages/mihErrorMessage.dart'; import 'package:patient_manager/components/popUpMessages/mihSuccessMessage.dart'; import 'package:patient_manager/env/env.dart'; import 'package:patient_manager/main.dart'; +import 'package:patient_manager/objects/appUser.dart'; import 'package:patient_manager/objects/arguments.dart'; import 'package:patient_manager/objects/businessEmployee.dart'; import 'package:supertokens_flutter/http.dart' as http; @@ -22,8 +26,6 @@ class ManageBusinessProfile extends StatefulWidget { State createState() => _ManageBusinessProfileState(); } -class BusinessUserScreenArguments {} - class _ManageBusinessProfileState extends State { final FocusNode _focusNode = FocusNode(); final baseAPI = AppEnviroment.baseApiUrl; @@ -32,8 +34,10 @@ class _ManageBusinessProfileState extends State { String userSearch = ""; String errorCode = ""; String errorBody = ""; + int selectionIndex = 0; late Future> employeeList; + late Future> userSearchResults; Future> fetchEmployees() async { //print("Patien manager page: $endpoint"); @@ -56,12 +60,36 @@ class _ManageBusinessProfileState extends State { } } + Future> fetchUsers(String search) async { + //TODO + 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'); + } + } + Widget employeesview(double w, double h) { return SizedBox( width: w, height: h - 157, child: Column(mainAxisSize: MainAxisSize.max, children: [ - //const SizedBox(height: 15), + const Text( + "Business Team", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 25, + ), + ), + const SizedBox(height: 15), FutureBuilder( future: employeeList, builder: (context, snapshot) { @@ -168,6 +196,162 @@ class _ManageBusinessProfileState extends State { ); } + Widget displayUserList(List userList) { + if (userList.isNotEmpty) { + return Container( + height: 500, + decoration: BoxDecoration( + color: MzanziInnovationHub.of(context)!.theme.primaryColor(), + borderRadius: BorderRadius.circular(25.0), + border: Border.all( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + width: 3.0, + ), + ), + child: BuildUserList( + users: userList, + arguments: widget.arguments, + ), + ); + } + return Container( + //height: 500, + decoration: BoxDecoration( + color: MzanziInnovationHub.of(context)!.theme.primaryColor(), + borderRadius: BorderRadius.circular(25.0), + border: Border.all( + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + width: 3.0), + ), + child: Center( + child: Text( + "Enter Username or Email to search", + style: TextStyle( + fontSize: 25, + color: MzanziInnovationHub.of(context)!.theme.messageTextColor()), + textAlign: TextAlign.center, + ), + ), + ); + } + + void submitUserForm() { + if (searchController.text != "") { + setState(() { + userSearch = searchController.text; + userSearchResults = fetchUsers(userSearch); + }); + } else { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Input Error"); + }, + ); + } + } + + Widget userSearchView(double w, double h) { + return KeyboardListener( + focusNode: _focusNode, + autofocus: true, + onKeyEvent: (event) async { + if (event is KeyDownEvent && + event.logicalKey == LogicalKeyboardKey.enter) { + submitUserForm(); + } + }, + child: SizedBox( + width: w, + height: h - 157, + child: Column(mainAxisSize: MainAxisSize.max, children: [ + const SizedBox(height: 5), + const Text( + "User Search", + style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold), + ), + //spacer + const SizedBox(height: 10), + MIHSearchField( + controller: searchController, + hintText: "Username or Email Search", + required: true, + editable: true, + onTap: () { + submitUserForm(); + }, + ), + //spacer + const SizedBox(height: 10), + FutureBuilder( + future: userSearchResults, + builder: (context, snapshot) { + //print("patient Liust ${snapshot.data}"); + if (snapshot.connectionState == ConnectionState.waiting) { + return Expanded( + child: Container( + //height: 500, + decoration: BoxDecoration( + color: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + borderRadius: BorderRadius.circular(25.0), + border: Border.all( + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + width: 3.0), + ), + child: const Mihloadingcircle(), + ), + ); + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData) { + List patientsList; + if (userSearch == "") { + patientsList = []; + } else { + patientsList = snapshot.data!; + //print(patientsList); + } + + return Expanded( + child: displayUserList(patientsList), + ); + } else { + return Expanded( + child: Container( + //height: 500, + decoration: BoxDecoration( + color: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + borderRadius: BorderRadius.circular(25.0), + border: Border.all( + color: MzanziInnovationHub.of(context)! + .theme + .secondaryColor(), + width: 3.0), + ), + child: Center( + child: Text( + "$errorCode: Error pulling Patients Data\n/patients/search/$userSearch\n$errorBody", + style: TextStyle( + fontSize: 25, + color: MzanziInnovationHub.of(context)! + .theme + .errorColor()), + textAlign: TextAlign.center, + ), + ), + ), + ); + } + }, + ), + ]), + ), + ); + } + void internetConnectionPopUp() { showDialog( context: context, @@ -198,6 +382,18 @@ class _ManageBusinessProfileState extends State { ); } + Widget showSelection( + int selectionIndex, double screenWidth, double screenHeight) { + if (selectionIndex == 0) { + return SizedBox( + //width: 660, + child: employeesview(screenWidth, screenHeight), + ); + } else { + return userSearchView(screenWidth, screenHeight); + } + } + @override void dispose() { searchController.dispose(); @@ -207,6 +403,7 @@ class _ManageBusinessProfileState extends State { @override void initState() { + userSearchResults = fetchUsers("abc"); employeeList = fetchEmployees(); super.initState(); } @@ -230,15 +427,40 @@ class _ManageBusinessProfileState extends State { mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: [ - const Text( - "Business Team", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 25, + //const SizedBox(height: 20), + SizedBox( + width: screenWidth, + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + IconButton( + onPressed: () { + setState(() { + selectionIndex = 0; + }); + }, + icon: const Icon( + Icons.people_outline, + size: 35, + ), + ), + IconButton( + onPressed: () { + setState(() { + selectionIndex = 1; + }); + }, + icon: const Icon( + Icons.add, + size: 35, + ), + ), + ], ), ), - const SizedBox(height: 20), - employeesview(screenWidth, screenHeight), + showSelection(selectionIndex, screenWidth, screenHeight), ], ), ), @@ -253,7 +475,7 @@ class _ManageBusinessProfileState extends State { }, icon: const Icon(Icons.arrow_back), ), - ) + ), ], ), ),