app files restructure

This commit is contained in:
2024-11-14 11:46:50 +02:00
parent c07f0c25c5
commit 469a4b0383
312 changed files with 592 additions and 546 deletions

106
Frontend/lib/main.dart Normal file
View File

@@ -0,0 +1,106 @@
import 'package:flutter/material.dart';
//import 'package:fpjs_pro_plugin/error.dart';
import '../mih_env/env.dart';
import '../mih_router/routeGenerator.dart';
import '../mih_theme/mih_theme.dart';
//import 'package:fpjs_pro_plugin/fpjs_pro_plugin.dart';
class MzanziInnovationHub extends StatefulWidget {
const MzanziInnovationHub({
super.key,
});
@override
State<MzanziInnovationHub> createState() => _MzanziInnovationHubState();
// ignore: library_private_types_in_public_api
static _MzanziInnovationHubState? of(BuildContext context) =>
context.findAncestorStateOfType<_MzanziInnovationHubState>();
}
class _MzanziInnovationHubState extends State<MzanziInnovationHub> {
late ThemeMode _themeMode;
late MyTheme theme;
// final noscreenshot = NoScreenshot.instance;
Color getPrimany() {
return theme.primaryColor();
}
String getTitle() {
if (AppEnviroment.getEnv() == "Dev") {
return "Mzansi Innovation Hub - Dev";
} else {
return "Mzansi Innovation Hub";
}
}
void changeTheme(ThemeMode themeMode) {
setState(() {
_themeMode = themeMode;
if (_themeMode == ThemeMode.light) {
setState(() {
theme.mode = "Light";
});
} else {
setState(() {
theme.mode = "Dark";
});
}
});
}
// void doInit() async {
// print(
// "FpjsProPlugin.initFpjs Token: ${AppEnviroment.fingerPrintPluginKey}");
// await FpjsProPlugin.initFpjs(
// AppEnviroment.fingerPrintPluginKey, // insert your actual API key here
// endpoint: "https://mzansi-innovation-hub.co.za",
// scriptUrlPattern:
// 'https://mzansi-innovation-hub.co.za/web/v<version>/<apiKey>/loader_v<loaderVersion>.js',
// );
// identify();
// }
// void identify() async {
// try {
// var visitorId = await FpjsProPlugin.getVisitorId() ?? 'Unknown';
// print(visitorId);
// // use the visitor id
// } on FingerprintProError catch (e) {
// print("Error on Init: $e");
// // process an error somehow
// // check lib/error.dart to get more info about error types
// }
// }
@override
void initState() {
_themeMode = ThemeMode.dark;
theme = MyTheme();
theme.platform = Theme.of(context).platform;
// if (theme.getPlatform() == "Android") {
// noscreenshot.screenshotOff();
// }
theme.mode = "Dark";
super.initState();
//doInit();
}
@override
Widget build(BuildContext context) {
double width = MediaQuery.sizeOf(context).width;
theme.setScreenType(width);
precacheImage(theme.loadingImage(), context);
precacheImage(theme.logoImage(), context);
precacheImage(theme.logoFrame(), context);
return MaterialApp(
title: getTitle(),
themeMode: _themeMode,
theme: theme.darkMode(),
darkTheme: theme.lightMode(),
debugShowCheckedModeBanner: false,
initialRoute: '/',
onGenerateRoute: RouteGenerator.generateRoute,
);
}
}

View File

@@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import '../mih_env/env.dart';
import '../../main.dart';
import 'package:supertokens_flutter/supertokens.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
void main() async {
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
AppEnviroment.setupEnv(Enviroment.dev);
SuperTokens.init(
apiDomain: AppEnviroment.baseApiUrl,
apiBasePath: "/auth",
);
setUrlStrategy(PathUrlStrategy());
FlutterNativeSplash.remove();
runApp(const MzanziInnovationHub());
}

View File

@@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import '../mih_env/env.dart';
import '../../main.dart';
import 'package:supertokens_flutter/supertokens.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
void main() async {
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
AppEnviroment.setupEnv(Enviroment.prod);
SuperTokens.init(
apiDomain: AppEnviroment.baseApiUrl,
apiBasePath: "/auth",
);
setUrlStrategy(PathUrlStrategy());
FlutterNativeSplash.remove();
runApp(const MzanziInnovationHub());
}

View File

@@ -0,0 +1,823 @@
import 'dart:convert';
import 'package:flutter/material.dart';
// import '../mih_components/mih_pop_up_messages/mih_error_message.dart';
// import '../mih_components/mih_pop_up_messages/mih_success_message.dart';
// import '../mih_env/env.dart';
// import '../mih_objects/app_user.dart';
// import '../mih_objects/arguments.dart';
// import '../mih_objects/business.dart';
// import '../mih_objects/business_user.dart';
// import '../mih_objects/notification.dart';
// import '../mih_objects/patient_access.dart';
// import '../mih_objects/patient_queue.dart';
// import '../mih_objects/patients.dart';
import 'package:supertokens_flutter/supertokens.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../mih_env/env.dart';
import '../mih_objects/app_user.dart';
import '../mih_objects/arguments.dart';
import '../mih_objects/business.dart';
import '../mih_objects/business_user.dart';
import '../mih_objects/notification.dart';
import '../mih_objects/patient_access.dart';
import '../mih_objects/patient_queue.dart';
import '../mih_objects/patients.dart';
class MIHApiCalls {
final baseAPI = AppEnviroment.baseApiUrl;
//================== PROFILE DATA ==========================================================================
/// This function is used to get profile details of signed in user.
///
/// Patameters: int notificationAmount which is used to get number of notifications to show.
///
/// Returns HomeArguments which contains:-
/// - Signed In user data.
/// - Business user belongs to.
/// - Business User details.
/// - notifications.
/// - user profile picture.
Future<HomeArguments> getProfile(int notificationAmount) async {
AppUser userData;
Business? busData;
BusinessUser? bUserData;
List<MIHNotification> notifi;
String userPic;
// Get Userdata
var uid = await SuperTokens.getUserId();
var responseUser = await http.get(Uri.parse("$baseAPI/user/$uid"));
if (responseUser.statusCode == 200) {
// print("here");
String body = responseUser.body;
var decodedData = jsonDecode(body);
AppUser u = AppUser.fromJson(decodedData);
userData = u;
} else {
throw Exception(
"Error: GetUserData status code ${responseUser.statusCode}");
}
// Get BusinessUserdata
var responseBUser = await http.get(
Uri.parse("$baseAPI/business-user/$uid"),
);
if (responseBUser.statusCode == 200) {
String body = responseBUser.body;
var decodedData = jsonDecode(body);
BusinessUser business_User = BusinessUser.fromJson(decodedData);
bUserData = business_User;
} else {
bUserData = null;
}
// Get Businessdata
var responseBusiness =
await http.get(Uri.parse("$baseAPI/business/app_id/$uid"));
if (responseBusiness.statusCode == 200) {
String body = responseBusiness.body;
var decodedData = jsonDecode(body);
Business business = Business.fromJson(decodedData);
busData = business;
} else {
busData = null;
}
//get profile picture
if (userData.pro_pic_path == "") {
userPic = "";
}
// else if (AppEnviroment.getEnv() == "Dev") {
// 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}");
}
}
//Get Notifications
var responseNotification = await http.get(
Uri.parse("$baseAPI/notifications/$uid?amount=$notificationAmount"));
if (responseNotification.statusCode == 200) {
String body = responseNotification.body;
// var decodedData = jsonDecode(body);
// MIHNotification notifications = MIHNotification.fromJson(decodedData);
Iterable l = jsonDecode(body);
//print("Here2");
List<MIHNotification> notifications = List<MIHNotification>.from(
l.map((model) => MIHNotification.fromJson(model)));
notifi = notifications;
} else {
notifi = [];
}
//print(userPic);
return HomeArguments(userData, bUserData, busData, notifi, userPic);
}
/// This function is used to get business details by business _id.
///
/// Patameters: String business_id & app_id (app_id of patient).
///
/// Returns List<PatientAccess> (List of access that match the above parameters).
static Future<Business?> getBusinessDetails(String business_id) async {
var responseBusiness = await http.get(Uri.parse(
"${AppEnviroment.baseApiUrl}/business/business_id/$business_id"));
if (responseBusiness.statusCode == 200) {
String body = responseBusiness.body;
var decodedData = jsonDecode(body);
Business business = Business.fromJson(decodedData);
return business;
} else {
return null;
}
}
//================== BUSINESS PATIENT/PERSONAL ACCESS ==========================================================================
/// This function is used to check if a business has access to a specific patients profile.
///
/// Patameters: String business_id & app_id (app_id of patient).
///
/// Returns List<PatientAccess> (List of access that match the above parameters).
static Future<List<PatientAccess>> checkBusinessAccessToPatient(
String business_id,
String app_id,
) async {
final response = await http.get(Uri.parse(
"${AppEnviroment.baseApiUrl}/access-requests/patient/check/$business_id?app_id=$app_id"));
// var errorCode = response.statusCode.toString();
//print(response.body);
if (response.statusCode == 200) {
Iterable l = jsonDecode(response.body);
List<PatientAccess> patientAccesses = List<PatientAccess>.from(
l.map((model) => PatientAccess.fromJson(model)));
return patientAccesses;
} else {
throw Exception('failed to pull patient access for business');
}
}
/// This function is used to get list of access the business has.
///
/// Patameters: String business_id.
///
/// Returns List<PatientAccess> (List of access that match the above parameters).
static Future<List<PatientAccess>> getPatientAccessListOfBusiness(
String business_id) async {
final response = await http.get(Uri.parse(
"${AppEnviroment.baseApiUrl}/access-requests/business/patient/$business_id"));
// var errorCode = response.statusCode.toString();
// print(response.statusCode);
// print(response.body);
if (response.statusCode == 200) {
Iterable l = jsonDecode(response.body);
List<PatientAccess> patientAccesses = List<PatientAccess>.from(
l.map((model) => PatientAccess.fromJson(model)));
return patientAccesses;
} else {
throw Exception('failed to pull patient access List for business');
}
}
/// This function is used to get list of access the business has.
///
/// Patameters: String business_id.
///
/// Returns List<PatientAccess> (List of access that match the above parameters).
static Future<List<PatientAccess>> getBusinessAccessListOfPatient(
String app_id) async {
final response = await http.get(Uri.parse(
"${AppEnviroment.baseApiUrl}/access-requests/personal/patient/$app_id"));
// var errorCode = response.statusCode.toString();
// print(response.statusCode);
// print(response.body);
if (response.statusCode == 200) {
Iterable l = jsonDecode(response.body);
List<PatientAccess> patientAccesses = List<PatientAccess>.from(
l.map((model) => PatientAccess.fromJson(model)));
return patientAccesses;
} else {
throw Exception('failed to pull patient access List for business');
}
}
/// This function is used to UPDATE access the business has.
///
/// Patameters:-
/// String business_id,
/// String business_name,
/// String app_id,
/// String status,
/// String approved_by,
/// AppUser signedInUser,
/// BuildContext context,
///
/// Returns void (on success 200 navigate to /access-review ).
static Future<void> updatePatientAccessAPICall(
String business_id,
String business_name,
String app_id,
String status,
String approved_by,
AppUser signedInUser,
BuildContext context,
) async {
var response = await http.put(
Uri.parse(
"${AppEnviroment.baseApiUrl}/access-requests/update/permission/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
// business_id: str
// app_id: str
// status: str
// approved_by: str
body: jsonEncode(<String, dynamic>{
"business_id": business_id,
"app_id": app_id,
"status": status,
"approved_by": approved_by,
}),
);
if (response.statusCode == 200) {
//Navigator.of(context).pushNamed('/home');
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed(
'/access-review',
arguments: signedInUser,
);
String message = "";
if (status == "approved") {
message =
"You've successfully approved the access request! $business_name now has access to your profile forever.";
} else {
message =
"You've declined the access request. $business_name will not have access to your profile.";
}
successPopUp(message, context);
} else {
internetConnectionPopUp(context);
}
}
/// This function is used to create patient access and trigger notification to patient
///
/// Patameters:-
/// String business_id,
/// String app_id,
/// String type,
/// String requested_by,
/// BuildContext context,
///
/// Returns void (triggers notification of success 201).
static Future<void> addPatientAccessAPICall(
String business_id,
String app_id,
String type,
String requested_by,
BusinessArguments args,
BuildContext context,
) async {
var response = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/access-requests/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
// business_id: str
// app_id: str
// type: str
// requested_by: str
body: jsonEncode(<String, dynamic>{
"business_id": business_id,
"app_id": app_id,
"type": type,
"requested_by": requested_by,
}),
);
if (response.statusCode == 201) {
addAccessRequestNotificationAPICall(app_id, requested_by, args, context);
} else {
internetConnectionPopUp(context);
}
}
//================== PATIENT DATA ==========================================================================
/// This function is used to fetch a list of patients matching a search criteria
///
/// Patameters: String dsearch.
///
/// Returns List<Patient>.
static Future<List<Patient>> fetchPatients(String search) async {
final response = await http
.get(Uri.parse("${AppEnviroment.baseApiUrl}/patients/search/$search"));
// errorCode = response.statusCode.toString();
// errorBody = response.body;
if (response.statusCode == 200) {
Iterable l = jsonDecode(response.body);
List<Patient> patients =
List<Patient>.from(l.map((model) => Patient.fromJson(model)));
return patients;
} else {
throw Exception('failed to load patients');
}
}
/// This function is used to fetch a patient matching a app_id
///
/// Patameters: String app_id.
///
/// Returns Patient.
static Future<Patient> fetchPatientByAppId(
String app_id,
) async {
final response = await http
.get(Uri.parse("${AppEnviroment.baseApiUrl}/patients/$app_id"));
// errorCode = response.statusCode.toString();
// errorBody = response.body;
//print(response.body);
if (response.statusCode == 200) {
Patient patient = Patient.fromJson(jsonDecode(response.body));
// userData = u;
// Iterable l = jsonDecode(response.body);
// List<Patient> patients =
// List<Patient>.from(l.map((model) => Patient.fromJson(model)));
return patient;
} else {
throw Exception('failed to load patient');
}
}
//================== Notifications ==========================================================================
/// This function is used to create notification to patient for access reviews
///
/// Patameters:-
/// String app_id,
/// String business_name,
/// BuildContext context,
///
/// Returns void. (ON SUCCESS 201 , NAVIGATE TO /patient-manager)
static Future<void> addAccessRequestNotificationAPICall(
String app_id,
String business_name,
BusinessArguments args,
BuildContext context,
) async {
var response = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/notifications/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"app_id": app_id,
"notification_type": "Forever Access Request",
"notification_message":
"A new Forever Access Request has been sent by $business_name in order to access your Patient Profile. Please review request.",
"action_path": "/access-review",
}),
);
if (response.statusCode == 201) {
String message =
"A request has been sent to the patient advising that you have requested access to their profile. Only once access has been granted will you be able to book an appointment.";
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed(
'/patient-manager',
arguments: BusinessArguments(
args.signedInUser,
args.businessUser,
args.business,
),
);
successPopUp(message, context);
} else {
internetConnectionPopUp(context);
}
}
/// This function is used to create notification to patient for access reviews
///
/// Patameters:-
/// String app_id,
/// String business_name,
/// String origDate_time,
/// String date,
/// String time,
/// BusinessArguments args,
/// BuildContext context,
///
/// Returns void. (ON SUCCESS 201 , NAVIGATE TO /patient-manager)
static Future<void> addRescheduledAppointmentNotificationAPICall(
String app_id,
String business_name,
String origDate_time,
String date,
String time,
BusinessArguments args,
BuildContext context,
) async {
var response = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/notifications/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"app_id": app_id,
"notification_type": "Appointment Rescheduled",
"notification_message":
"Your appointment with $business_name for the ${origDate_time.replaceAll("T", " ").substring(0, origDate_time.length - 3)} has been rescheduled to the ${date} ${time}.",
"action_path": "/appointments",
}),
);
if (response.statusCode == 201) {
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed(
'/patient-manager',
arguments: BusinessArguments(
args.signedInUser,
args.businessUser,
args.business,
),
);
String message = "The appointment has been successfully rescheduled.";
successPopUp(message, context);
} else {
internetConnectionPopUp(context);
}
}
/// This function is used to create notification to patient for access reviews
///
/// Patameters:-
/// String app_id,
/// String business_name,
/// String origDate_time,
/// String date,
/// String time,
/// BusinessArguments args,
/// BuildContext context,
///
/// Returns void. (ON SUCCESS 201 , NAVIGATE TO /patient-manager)
static Future<void> addCancelledAppointmentNotificationAPICall(
String app_id,
String date_time,
BusinessArguments args,
BuildContext context,
) async {
var response = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/notifications/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"app_id": app_id,
"notification_type": "Appointment Cancelled",
"notification_message":
"Your appointment with ${args.business!.Name} for the ${date_time.replaceAll("T", " ")} has been cancelled.",
"action_path": "/appointments",
}),
);
if (response.statusCode == 201) {
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed(
'/patient-manager',
arguments: BusinessArguments(
args.signedInUser,
args.businessUser,
args.business,
),
);
String message =
"The appointment has been cancelled successfully. This means it will no longer be visible in your waiting room and calender.";
successPopUp(message, context);
} else {
internetConnectionPopUp(context);
}
}
/// This function is used to create notification to patient for access reviews
///
/// Patameters:-
/// String app_id,
/// String business_name,
/// String origDate_time,
/// String date,
/// String time,
/// BusinessArguments args,
/// BuildContext context,
///
/// Returns void. (ON SUCCESS 201 , NAVIGATE TO /patient-manager)
static Future<void> addNewAppointmentNotificationAPICall(
String app_id,
String date,
String time,
BusinessArguments args,
BuildContext context,
) async {
var response = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/notifications/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"app_id": app_id,
"notification_type": "New Appointment Booked",
"notification_message":
"An appointment with ${args.business!.Name} has been booked for the $date $time.",
"action_path": "/appointments",
}),
);
if (response.statusCode == 201) {
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed(
'/patient-manager',
arguments: args,
);
String message =
"The appointment was been created successfully. This means it will now be visible in your waiting room and calender.";
successPopUp(message, context);
} else {
internetConnectionPopUp(context);
}
}
//================== APPOINTMENT/ PATIENT QUEUE ==========================================================================
/// This function is used to fetch a list of appointments for a doctors office for a date.
///
/// Patameters: String date & business_id .
///
/// Returns List<PatientQueue>.
static Future<List<PatientQueue>> fetchBusinessAppointmentsAPICall(
String date,
String business_id,
) async {
//print("Patien manager page: $endpoint");
final response = await http.get(Uri.parse(
"${AppEnviroment.baseApiUrl}/queue/appointments/business/$business_id?date=$date"));
// print("Here");
// print("Body: ${response.body}");
// print("Code: ${response.statusCode}");
// errorCode = response.statusCode.toString();
// errorBody = response.body;
if (response.statusCode == 200) {
//print("Here1");
Iterable l = jsonDecode(response.body);
//print("Here2");
List<PatientQueue> patientQueue = List<PatientQueue>.from(
l.map((model) => PatientQueue.fromJson(model)));
//print("Here3");
//print(patientQueue);
return patientQueue;
} else {
throw Exception('failed to fatch patient queue');
}
}
/// This function is used to fetch a list of appointments for a doctors office for a date.
///
/// Patameters: String date & business_id .
///
/// Returns List<PatientQueue>.
static Future<List<PatientQueue>> fetchPersonalAppointmentsAPICall(
String date,
String app_id,
) async {
//print("Patien manager page: $endpoint");
final response = await http.get(Uri.parse(
"${AppEnviroment.baseApiUrl}/queue/appointments/personal/$app_id?date=$date"));
// print("Here");
// print("Body: ${response.body}");
// print("Code: ${response.statusCode}");
// errorCode = response.statusCode.toString();
// errorBody = response.body;
if (response.statusCode == 200) {
//print("Here1");
Iterable l = jsonDecode(response.body);
//print("Here2");
List<PatientQueue> patientQueue = List<PatientQueue>.from(
l.map((model) => PatientQueue.fromJson(model)));
//print("Here3");
//print(patientQueue);
return patientQueue;
} else {
throw Exception('failed to fatch patient queue');
}
}
/// This function is used to UPDATE AN appointments for a doctors office for a date.
///
/// Patameters:-
/// int idpatient_queue,
/// String app_id,
/// String business_name,
/// String origDate_time,
/// String date,
/// String time,
/// BusinessArguments args,
/// BuildContext context,
///
/// Returns VOID (TRIGGERS NOTIGICATIOPN ON SUCCESS)
static Future<void> updateApointmentAPICall(
int idpatient_queue,
String app_id,
String business_name,
String origDate_time,
String date,
String time,
BusinessArguments args,
BuildContext context,
) async {
var response = await http.put(
Uri.parse("${AppEnviroment.baseApiUrl}/queue/appointment/update/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"idpatient_queue": idpatient_queue,
"date": date,
"time": time,
}),
);
if (response.statusCode == 200) {
addRescheduledAppointmentNotificationAPICall(
app_id,
business_name,
origDate_time,
date,
time,
args,
context,
);
} else {
internetConnectionPopUp(context);
}
}
/// This function is used to Delete/ cancel AN appointments for a doctors office for a date.
///
/// Patameters:-
/// int idpatient_queue,
/// PatientViewArguments args,
/// BuildContext context,
///
/// Returns VOID (TRIGGERS NOTIGICATIOPN ON SUCCESS)
static Future<void> deleteApointmentAPICall(
int idpatient_queue,
String app_id,
String date_time,
BusinessArguments args,
BuildContext context,
) async {
var response = await http.delete(
Uri.parse("${AppEnviroment.baseApiUrl}/queue/appointment/delete/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{"idpatient_queue": idpatient_queue}),
);
//print("Here4");
//print(response.statusCode);
if (response.statusCode == 200) {
addCancelledAppointmentNotificationAPICall(
app_id,
date_time,
args,
context,
);
// Navigator.of(context).pop();
// Navigator.of(context).pop();
// String message =
// "The note has been deleted successfully. This means it will no longer be visible on your and cannot be used for future appointments.";
// successPopUp(message, context);
} else {
internetConnectionPopUp(context);
}
}
/// This function is used to create AN appointments for a doctors office for a date.
///
/// Patameters:-
/// int idpatient_queue,
/// PatientViewArguments args,
/// BuildContext context,
///
/// Returns VOID (TRIGGERS NOTIGICATIOPN ON SUCCESS)
static Future<void> addAppointmentAPICall(
String business_id,
String app_id,
String date,
String time,
BusinessArguments args,
BuildContext context,
) async {
var response = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/queue/appointment/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": business_id,
"app_id": app_id,
"date": date,
"time": time,
}),
);
if (response.statusCode == 201) {
// Navigator.pushNamed(context, '/patient-manager/patient',
// arguments: widget.signedInUser);
// String message =
// "The appointment has been successfully booked!\n\nAn approval request as been sent to the patient.Once the access request has been approved, you will be able to access the patientAccesses profile. ou can check the status of your request in patient queue under the appointment.";
// "${fnameController.text} ${lnameController.text} patient profiole has been successfully added!\n";
// Navigator.pop(context);
// Navigator.pop(context);
// Navigator.pop(context);
// setState(() {
// dateController.text = "";
// timeController.text = "";
// });
// Navigator.of(context).pushNamed(
// '/patient-manager',
// arguments: BusinessArguments(
// widget.arguments.signedInUser,
// widget.arguments.businessUser,
// widget.arguments.business,
// ),
// );
// successPopUp(message);
// String app_id,
// String date,
// String time,
// BusinessArguments args,
// BuildContext context,
addNewAppointmentNotificationAPICall(
app_id,
date,
time,
args,
context,
);
} else {
internetConnectionPopUp(context);
}
}
//================== 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,
);
},
);
}
}

View File

@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import '../mih_components/mih_pop_up_messages/mih_error_message.dart';
class MIHLocationAPI {
final LocationSettings locationSettings = const LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 100,
);
///This function is to get the current location of the signed in user.
///First checks the permission, if permission is denied (new user), request permission from user.
///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<Position?> getGPSPosition(BuildContext context) async {
//Check the type of permission granted
LocationPermission permission = await Geolocator.checkPermission();
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) {
showPermissionError(context);
return null;
} else {
Position location = await Geolocator.getCurrentPosition(
locationSettings: locationSettings);
//print(location);
return location;
}
}
double getDistanceInMeaters(Position startPosition, Position endPosition) {
return Geolocator.distanceBetween(startPosition.latitude,
startPosition.longitude, endPosition.latitude, endPosition.longitude);
}
void showPermissionError(BuildContext context) {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Location Denied");
},
);
}
}

View File

@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import '../mih_components/mih_inputs_and_buttons/mih_date_input.dart';
class Medcertinput extends StatefulWidget {
final TextEditingController startDateController;
final TextEditingController endDateTextController;
final TextEditingController retDateTextController;
const Medcertinput({
super.key,
required this.startDateController,
required this.endDateTextController,
required this.retDateTextController,
});
@override
State<Medcertinput> createState() => _MedcertinputState();
}
class _MedcertinputState extends State<Medcertinput> {
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox(
//height: 325,
child: Column(
children: [
//const SizedBox(height: 50.0),
SizedBox(
width: 700,
child: MIHDateField(
controller: widget.startDateController,
lableText: "From",
required: true,
),
),
const SizedBox(height: 10.0),
SizedBox(
width: 700,
child: MIHDateField(
controller: widget.endDateTextController,
lableText: "Up to Including",
required: true,
),
),
const SizedBox(height: 10.0),
SizedBox(
width: 700,
child: MIHDateField(
controller: widget.retDateTextController,
lableText: "Return",
required: true,
),
),
],
),
);
}
}

View File

@@ -0,0 +1,202 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import '../mih_components/mih_layout/mih_window.dart';
import '../mih_packages/patient_profile/builder/build_med_list.dart';
import '../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../mih_env/env.dart';
import '../mih_objects/medicine.dart';
import 'package:supertokens_flutter/http.dart' as http;
class MedicineSearch extends StatefulWidget {
final TextEditingController searchVlaue;
const MedicineSearch({
super.key,
required this.searchVlaue,
});
@override
State<MedicineSearch> createState() => _MedicineSearchState();
}
class _MedicineSearchState extends State<MedicineSearch> {
final String endpointMeds = "${AppEnviroment.baseApiUrl}/users/medicine/";
//TextEditingController searchController = TextEditingController();
late Future<List<Medicine>> futueMeds;
//String searchString = "";
Future<List<Medicine>> getMedList(String endpoint) async {
final response = await http.get(Uri.parse(endpoint));
if (response.statusCode == 200) {
Iterable l = jsonDecode(response.body);
List<Medicine> medicines =
List<Medicine>.from(l.map((model) => Medicine.fromJson(model)));
// List<Medicine> meds = [];
// medicines.forEach((element) => meds.add(element.name));
return medicines;
} else {
internetConnectionPopUp();
throw Exception('failed to load medicine');
}
}
void internetConnectionPopUp() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Internet Connection");
},
);
}
@override
void dispose() {
super.dispose();
}
@override
void initState() {
futueMeds = getMedList(endpointMeds + widget.searchVlaue.text);
super.initState();
}
@override
Widget build(BuildContext context) {
return MIHWindow(
fullscreen: false,
windowTitle: "Select Medicine",
windowTools: [],
onWindowTapClose: () {
Navigator.pop(context);
},
windowBody: [
FutureBuilder(
future: futueMeds,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
height: 400,
child: Mihloadingcircle(),
);
} else if (snapshot.hasData && snapshot.data!.isNotEmpty) {
final medsList = snapshot.data!;
return SizedBox(
height: 400,
child: BuildMedicinesList(
contoller: widget.searchVlaue,
medicines: medsList,
//searchString: searchString,
),
);
} else {
return const SizedBox(
height: 400,
child: Center(
child: Text(
"No Match Found\nPlease close and manually capture medicine",
style: TextStyle(fontSize: 25, color: Colors.grey),
textAlign: TextAlign.center,
),
),
);
}
},
),
],
);
// return 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: SingleChildScrollView(
// padding: const EdgeInsets.symmetric(horizontal: 10),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisAlignment: MainAxisAlignment.center,
// mainAxisSize: MainAxisSize.min,
// children: [
// Text(
// "Select Medicine",
// textAlign: TextAlign.center,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// fontSize: 35.0,
// fontWeight: FontWeight.bold,
// ),
// ),
// const SizedBox(height: 25.0),
// FutureBuilder(
// future: futueMeds,
// builder: (context, snapshot) {
// if (snapshot.connectionState == ConnectionState.waiting) {
// return const SizedBox(
// height: 400,
// child: Mihloadingcircle(),
// );
// } else if (snapshot.hasData &&
// snapshot.data!.isNotEmpty) {
// final medsList = snapshot.data!;
// return SizedBox(
// height: 400,
// child: BuildMedicinesList(
// contoller: widget.searchVlaue,
// medicines: medsList,
// //searchString: searchString,
// ),
// );
// } else {
// return const SizedBox(
// height: 400,
// child: Center(
// child: Text(
// "No Match Found\nPlease close and manually capture medicine",
// style:
// TextStyle(fontSize: 25, color: Colors.grey),
// textAlign: TextAlign.center,
// ),
// ),
// );
// }
// },
// ),
// ],
// ),
// ),
// ),
// Positioned(
// top: 5,
// right: 5,
// width: 50,
// height: 50,
// child: IconButton(
// onPressed: () {
// Navigator.pop(context);
// },
// icon: const Icon(
// Icons.close,
// color: Colors.red,
// size: 35,
// ),
// ),
// ),
// ],
// ),
// );
}
}

View File

@@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
import '../../main.dart';
import 'package:table_calendar/table_calendar.dart';
class MIHCalendar extends StatefulWidget {
final double calendarWidth;
final double rowHeight;
final void Function(String) setDate;
const MIHCalendar({
super.key,
required this.calendarWidth,
required this.rowHeight,
required this.setDate,
});
@override
State<MIHCalendar> createState() => _MIHCalendarState();
}
class _MIHCalendarState extends State<MIHCalendar> {
DateTime selectedDay = DateTime.now();
CalendarFormat _calendarFormat = CalendarFormat.week;
void onDaySelected(DateTime day, DateTime focusedDay) {
setState(() {
selectedDay = day;
});
widget.setDate(selectedDay.toString().split(" ")[0]);
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: widget.calendarWidth,
child: TableCalendar(
headerStyle: HeaderStyle(
formatButtonDecoration: BoxDecoration(
border: Border.fromBorderSide(
BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
borderRadius: const BorderRadius.all(
Radius.circular(12.0),
),
),
// formatButtonTextStyle:
),
rowHeight: widget.rowHeight,
focusedDay: selectedDay,
firstDay: DateTime.utc(2024, 1, 1),
lastDay: DateTime.utc(2099, 1, 1),
onDaySelected: onDaySelected,
selectedDayPredicate: (day) => isSameDay(day, selectedDay),
calendarFormat: _calendarFormat,
onFormatChanged: (format) {
setState(() {
_calendarFormat = format;
});
},
calendarStyle: CalendarStyle(
outsideDaysVisible: false,
todayTextStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
todayDecoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.messageTextColor(),
shape: BoxShape.circle,
),
selectedTextStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
selectedDecoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
shape: BoxShape.circle,
),
weekendTextStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.messageTextColor(),
),
),
daysOfWeekStyle: DaysOfWeekStyle(
weekdayStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
weekendStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.messageTextColor(),
),
),
),
);
}
}

View File

@@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
class MIHButton extends StatefulWidget {
final void Function() onTap;
final String buttonText;
final Color buttonColor;
final Color textColor;
const MIHButton({
super.key,
required this.onTap,
required this.buttonText,
required this.buttonColor,
required this.textColor,
});
@override
State<MIHButton> createState() => _MIHButtonState();
}
class _MIHButtonState extends State<MIHButton> {
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: widget.onTap,
style: ElevatedButton.styleFrom(
backgroundColor: widget.buttonColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
),
child: Text(
widget.buttonText,
style: TextStyle(
//fontWeight: FontWeight.bold,
fontSize: 20,
color: widget.textColor,
fontWeight: FontWeight.bold,
),
),
);
}
}

View File

@@ -0,0 +1,152 @@
import 'package:flutter/material.dart';
import '../../main.dart';
class MIHDateField extends StatefulWidget {
final TextEditingController controller;
final String lableText;
final bool required;
const MIHDateField({
super.key,
required this.controller,
required this.lableText,
required this.required,
});
@override
State<MIHDateField> createState() => _MIHDateFieldState();
}
class _MIHDateFieldState extends State<MIHDateField> {
final FocusNode _focus = FocusNode();
bool startup = true;
// bool makeEditable() {
Future<void> _selectDate(BuildContext context) async {
DateTime? picked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);
if (picked != null) {
setState(() {
widget.controller.text = picked.toString().split(" ")[0];
});
}
}
Widget setRequiredText() {
if (widget.required) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"*",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor()),
),
const SizedBox(
width: 8.0,
),
Text(widget.lableText,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor())),
],
);
} else {
return Text(widget.lableText,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()));
}
}
void _onFocusChange() {
setState(() {
startup = false;
});
}
String? get _errorText {
final text = widget.controller.text;
if (startup) {
return null;
}
if (!widget.required) {
return null;
}
if (text.isEmpty) {
return "${widget.lableText} is required";
}
return null;
}
@override
void dispose() {
_focus.dispose();
super.dispose();
}
@override
void initState() {
_focus.addListener(_onFocusChange);
super.initState();
}
@override
Widget build(BuildContext context) {
return TextField(
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
controller: widget.controller,
readOnly: true,
obscureText: false,
focusNode: _focus,
onChanged: (_) => setState(() {
startup = false;
}),
decoration: InputDecoration(
errorText: _errorText,
errorStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontWeight: FontWeight.bold),
label: setRequiredText(),
//labelText: widget.lableText,
//labelStyle: const TextStyle(color: Colors.blueAccent),
prefixIcon: Icon(
Icons.calendar_today,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
fillColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
filled: true,
//hintText: hintText,
//hintStyle: TextStyle(color: Colors.blueGrey[400]),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 2.0,
),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
),
),
onTap: () {
_selectDate(context);
},
);
}
}

View File

@@ -0,0 +1,214 @@
import 'package:flutter/material.dart';
import '../../main.dart';
class MIHDropdownField extends StatefulWidget {
final TextEditingController controller;
final String hintText;
final bool required;
final List<String> dropdownOptions;
final void Function(String?)? onSelect;
final bool editable;
const MIHDropdownField({
super.key,
required this.controller,
required this.hintText,
required this.dropdownOptions,
required this.required,
required this.editable,
this.onSelect,
});
@override
State<MIHDropdownField> createState() => _MIHDropdownFieldState();
}
class _MIHDropdownFieldState extends State<MIHDropdownField> {
//var dropbownItems = ["Dr.", "Assistant"];
bool startup = true;
final FocusNode _focus = FocusNode();
late List<DropdownMenuEntry<String>> menu;
Widget setRequiredText() {
if (widget.required) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"*",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor()),
),
const SizedBox(
width: 8.0,
),
Text(widget.hintText,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor())),
],
);
} else {
return Text(widget.hintText,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()));
}
}
void _onFocusChange() {
setState(() {
startup = false;
});
}
String? get _errorText {
final text = widget.controller.text;
if (startup) {
return null;
}
if (!widget.required) {
return null;
}
if (text.isEmpty) {
return "${widget.hintText} is required";
}
return null;
}
List<DropdownMenuEntry<String>> buidMenueOptions(List<String> options) {
List<DropdownMenuEntry<String>> menueList = [];
for (final i in options) {
menueList.add(DropdownMenuEntry(
value: i,
label: i,
style: ButtonStyle(
foregroundColor: WidgetStatePropertyAll(
MzanziInnovationHub.of(context)!.theme.secondaryColor()))));
}
return menueList;
}
@override
void dispose() {
_focus.dispose();
super.dispose();
}
@override
void initState() {
menu = buidMenueOptions(widget.dropdownOptions);
_focus.addListener(_onFocusChange);
super.initState();
}
// bool makeEditable() {
@override
Widget build(BuildContext context) {
return DropdownMenu(
initialSelection: widget.controller.text,
enabled: widget.editable,
trailingIcon: Icon(
Icons.arrow_drop_down,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
selectedTrailingIcon: Icon(
Icons.arrow_drop_up,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
textStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
menuHeight: 300,
controller: widget.controller,
expandedInsets: EdgeInsets.zero,
label: setRequiredText(),
errorText: _errorText,
focusNode: _focus,
onSelected: (_) {
setState(() {
startup = false;
});
// if (widget.editable == false) {
// return false;
// }
},
leadingIcon: IconButton(
onPressed: () {
setState(() {
startup = false;
});
widget.controller.clear();
},
icon: Icon(
Icons.delete_outline_rounded,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
menuStyle: MenuStyle(
backgroundColor: WidgetStatePropertyAll(
MzanziInnovationHub.of(context)!.theme.primaryColor()),
side: WidgetStatePropertyAll(
BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 2.0),
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
errorStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontWeight: FontWeight.bold),
fillColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 2.0,
),
),
disabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 2.0,
),
),
outlineBorder: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
),
dropdownMenuEntries: menu,
// const <DropdownMenuEntry<String>>[
// DropdownMenuEntry(value: "Dr.", label: "Dr."),
// DropdownMenuEntry(value: "Assistant", label: "Assistant"),
// ],
);
}
}
// filled: true,
// hintText: hintText,
// hintStyle: TextStyle(color: Colors.blueGrey[400]),
// enabledBorder: const OutlineInputBorder(
// borderSide: BorderSide(
// color: Colors.blueAccent,
// width: 2.0,
// ),
// ),
// focusedBorder: const OutlineInputBorder(
// borderSide: BorderSide(color: Colors.blue),

View File

@@ -0,0 +1,177 @@
import 'package:flutter/material.dart';
import '../../main.dart';
class MIHFileField extends StatefulWidget {
final TextEditingController controller;
final String hintText;
final bool editable;
final bool required;
final void Function() onPressed;
const MIHFileField({
super.key,
required this.controller,
required this.hintText,
required this.editable,
required this.required,
required this.onPressed,
});
@override
State<MIHFileField> createState() => _MIHFileFieldState();
}
class _MIHFileFieldState extends State<MIHFileField> {
bool startup = true;
final FocusNode _focus = FocusNode();
bool makeEditable() {
if (widget.editable) {
return false;
} else {
return true;
}
}
String? get _errorText {
final text = widget.controller.text;
String errorMessage = '';
if (startup) {
return null;
}
if (!widget.required) {
return null;
}
if (text.isEmpty) {
return "${widget.hintText} is required";
}
if (widget.hintText == "Email" && !isEmailValid(text)) {
errorMessage += "Enter a valid email address\n";
}
if (widget.hintText == "Username" && text.length < 8) {
errorMessage += "• Username must contain at least 8 characters.\n";
}
if (widget.hintText == "Username" && !isUsernameValid(text)) {
errorMessage += "• Username can only contain '_' special Chracters.\n";
}
if (errorMessage.isEmpty) {
return null;
}
// If there are no error messages, the password is valid
return errorMessage;
}
bool isUsernameValid(String username) {
return RegExp(r'^(?=[a-zA-Z0-9._]{8,20}$)(?!.*[_.]{2})[^_.].*[^_.]$')
.hasMatch(username);
}
bool isEmailValid(String email) {
return RegExp(r'^[\w-\.]+@[a-zA-Z]+\.[a-zA-Z]{2,}$').hasMatch(email);
}
void _onFocusChange() {
setState(() {
startup = false;
});
}
Widget setRequiredText() {
if (widget.required) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"*",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor()),
),
const SizedBox(
width: 8.0,
),
Text(widget.hintText,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor())),
],
);
} else {
return Text(widget.hintText,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()));
}
}
@override
void dispose() {
_focus.dispose();
super.dispose();
}
@override
void initState() {
_focus.addListener(_onFocusChange);
super.initState();
}
@override
Widget build(BuildContext context) {
return TextField(
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
controller: widget.controller,
focusNode: _focus,
readOnly: makeEditable(),
//enabled: !makeEditable(),
obscureText: false,
onChanged: (_) => setState(() {
startup = false;
}),
decoration: InputDecoration(
suffixIcon: IconButton(
icon: const Icon(Icons.attach_file),
onPressed: widget.onPressed,
),
label: setRequiredText(),
//labelStyle: TextStyle(color: MzanziInnovationHub.of(context)!.theme.primaryColor()),
fillColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
filled: true,
errorText: _errorText,
errorStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontWeight: FontWeight.bold),
//errorBorder: const InputBorder(),
//hintText: hintText,
//hintStyle: TextStyle(color: Colors.blueGrey[400]),
disabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 2.0,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 2.0,
),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
),
),
);
}
}

View File

@@ -0,0 +1,146 @@
import 'package:flutter/material.dart';
import '../../main.dart';
class MIHMLTextField extends StatefulWidget {
final TextEditingController controller;
final String hintText;
final bool editable;
final bool required;
const MIHMLTextField({
super.key,
required this.controller,
required this.hintText,
required this.editable,
required this.required,
});
@override
State<MIHMLTextField> createState() => _MIHMLTextFieldState();
}
class _MIHMLTextFieldState extends State<MIHMLTextField> {
bool startup = true;
final FocusNode _focus = FocusNode();
bool makeEditable() {
if (widget.editable) {
return false;
} else {
return true;
}
}
String? get _errorText {
final text = widget.controller.text;
if (startup) {
return null;
}
if (!widget.required) {
return null;
}
if (text.isEmpty) {
return "${widget.hintText} is required";
}
return null;
}
void _onFocusChange() {
setState(() {
startup = false;
});
}
Widget setRequiredText() {
if (widget.required) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"*",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor()),
),
const SizedBox(
width: 8.0,
),
Text(widget.hintText,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor())),
],
);
} else {
return Text(widget.hintText,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()));
}
}
@override
void dispose() {
_focus.dispose();
super.dispose();
}
@override
void initState() {
_focus.addListener(_onFocusChange);
super.initState();
}
@override
Widget build(BuildContext context) {
return TextField(
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
textAlign: TextAlign.start,
textAlignVertical: TextAlignVertical.top,
expands: true,
maxLines: null,
controller: widget.controller,
readOnly: makeEditable(),
obscureText: false,
focusNode: _focus,
onChanged: (_) => setState(() {
startup = false;
}),
decoration: InputDecoration(
label: setRequiredText(),
errorText: _errorText,
errorStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontWeight: FontWeight.bold),
labelStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
alignLabelWithHint: true,
fillColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
filled: true,
//hintText: hintText,
//hintStyle: TextStyle(color: Colors.blueGrey[400]),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 2.0,
),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
),
),
);
}
}

View File

@@ -0,0 +1,191 @@
import 'package:flutter/material.dart';
import '../../main.dart';
class MIHPassField extends StatefulWidget {
final TextEditingController controller;
final String hintText;
final bool required;
final bool signIn;
final Iterable<String>? autoFillHintGroup;
const MIHPassField({
super.key,
required this.controller,
required this.hintText,
required this.required,
required this.signIn,
this.autoFillHintGroup,
});
@override
State<MIHPassField> createState() => _MIHPassFieldState();
}
class _MIHPassFieldState extends State<MIHPassField> {
bool startup = true;
final textFieldFocusNode = FocusNode();
bool _obscured = true;
//bool valid = false;
void _toggleObscured() {
setState(() {
_obscured = !_obscured;
if (textFieldFocusNode.hasPrimaryFocus) {
return; // If focus is on text field, dont unfocus
}
textFieldFocusNode.canRequestFocus =
false; // Prevents focus if tap on eye
});
}
String? get _errorText {
final text = widget.controller.text;
String errorMessage = '';
if (startup) {
return null;
}
if (!widget.required) {
return null;
}
if (text.isEmpty) {
return "${widget.hintText} is required";
}
// Password length greater than 8
if (text.length <= 8 && !widget.signIn) {
errorMessage += '• Password must contain at least 8 characters.\n';
}
// Contains at least one uppercase letter
if (!text.contains(RegExp(r'[A-Z]')) && !widget.signIn) {
errorMessage += '• Uppercase letter is missing.\n';
}
// Contains at least one lowercase letter
if (!text.contains(RegExp(r'[a-z]')) && !widget.signIn) {
errorMessage += '• Lowercase letter is missing.\n';
}
// Contains at least one digit
if (!text.contains(RegExp(r'[0-9]')) && !widget.signIn) {
errorMessage += '• number is missing.\n';
}
// Contains at least one special character
if (!text.contains(RegExp(r'[!@#$%^&*]')) && !widget.signIn) {
errorMessage += '• Special character is missing - !@#\$%^&*\n';
}
// Contains no errors
if (errorMessage.isEmpty) {
return null;
}
// If there are no error messages, the password is valid
return errorMessage;
}
Widget setRequiredText() {
if (widget.required) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"*",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor()),
),
const SizedBox(
width: 8.0,
),
Text(widget.hintText,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor())),
],
);
} else {
return Text(widget.hintText,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()));
}
}
void _onFocusChange() {
setState(() {
startup = false;
});
}
@override
void dispose() {
textFieldFocusNode.dispose();
super.dispose();
}
@override
void initState() {
textFieldFocusNode.addListener(_onFocusChange);
super.initState();
}
@override
Widget build(BuildContext context) {
return TextField(
autofillHints: widget.autoFillHintGroup,
controller: widget.controller,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
obscureText: _obscured,
focusNode: textFieldFocusNode,
onChanged: (_) => setState(() {
startup = false;
}),
decoration: InputDecoration(
fillColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
filled: true,
label: setRequiredText(),
//labelStyle: const TextStyle(color: Colors.blueAccent),
errorText: _errorText,
errorStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontWeight: FontWeight.bold),
//hintText: widget.hintText,
//hintStyle: TextStyle(color: Colors.blueGrey[400]),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 2.0,
),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
),
suffixIcon: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 4, 0),
child: GestureDetector(
onTap: _toggleObscured,
child: Icon(
_obscured
? Icons.visibility_rounded
: Icons.visibility_off_rounded,
size: 24,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,158 @@
import 'package:flutter/material.dart';
import '../../main.dart';
class MIHSearchField extends StatefulWidget {
final TextEditingController controller;
final String hintText;
final bool required;
final bool editable;
final void Function() onTap;
const MIHSearchField({
super.key,
required this.controller,
required this.hintText,
required this.required,
required this.editable,
required this.onTap,
});
@override
State<MIHSearchField> createState() => _MIHSearchFieldState();
}
class _MIHSearchFieldState extends State<MIHSearchField> {
bool startup = true;
final FocusNode _focus = FocusNode();
bool makeEditable() {
if (widget.editable) {
return false;
} else {
return true;
}
}
String? get _errorText {
final text = widget.controller.text;
if (startup) {
return null;
}
if (!widget.required) {
return null;
}
if (text.isEmpty) {
return "${widget.hintText} is required";
}
return null;
}
void _onFocusChange() {
setState(() {
startup = false;
});
}
Widget setRequiredText() {
if (widget.required) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"*",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor()),
),
const SizedBox(
width: 8.0,
),
Text(
widget.hintText,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
],
);
} else {
return Text(widget.hintText,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()));
}
}
@override
void dispose() {
_focus.dispose();
super.dispose();
}
@override
void initState() {
_focus.addListener(_onFocusChange);
super.initState();
}
@override
Widget build(BuildContext context) {
return TextField(
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
onChanged: (_) {
setState(() {
startup = false;
});
},
controller: widget.controller,
//style: TextStyle(color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
readOnly: makeEditable(),
focusNode: _focus,
obscureText: false,
decoration: InputDecoration(
fillColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
suffixIcon: IconButton(
icon: Icon(
Icons.search,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
onPressed: () {
setState(() {
startup = false;
});
if (widget.controller.text != "") {
widget.onTap();
}
},
),
filled: true,
label: setRequiredText(),
errorText: _errorText,
errorStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontWeight: FontWeight.bold),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 2.0,
),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
),
),
);
}
}

View File

@@ -0,0 +1,185 @@
import 'package:flutter/material.dart';
import '../../main.dart';
class MIHTextField extends StatefulWidget {
final TextEditingController controller;
final String hintText;
final bool editable;
final bool required;
final Iterable<String>? autoFillHintGroup;
const MIHTextField({
super.key,
required this.controller,
required this.hintText,
required this.editable,
required this.required,
this.autoFillHintGroup,
});
@override
State<MIHTextField> createState() => _MIHTextFieldState();
}
class _MIHTextFieldState extends State<MIHTextField> {
bool startup = true;
final FocusNode _focus = FocusNode();
bool makeEditable() {
if (widget.editable) {
return false;
} else {
return true;
}
}
String? get _errorText {
final text = widget.controller.text;
String errorMessage = '';
if (startup) {
return null;
}
if (!widget.required) {
return null;
}
if (text.isEmpty) {
return "${widget.hintText} is required";
}
if (widget.hintText == "Email" && !isEmailValid(text)) {
errorMessage += "Enter a valid email address\n";
}
if (widget.hintText == "Username" && text.length < 8) {
errorMessage += "• Username must contain at least 8 characters.\n";
}
if (widget.hintText == "Username" && !isUsernameValid(text)) {
errorMessage += "• Username can only contain '_' special Chracters.\n";
}
if (errorMessage.isEmpty) {
return null;
}
// If there are no error messages, the password is valid
return errorMessage;
}
bool isUsernameValid(String username) {
return RegExp(r'^(?=[a-zA-Z0-9._]{8,20}$)(?!.*[_.]{2})[^_.].*[^_.]$')
.hasMatch(username);
}
bool isEmailValid(String email) {
var regex = RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$');
return regex.hasMatch(email);
}
void _onFocusChange() {
setState(() {
startup = false;
});
}
// List<AutofillGroup> getAutoFillDetails(){
// if(widget.autoFillHintGroup == null){
// return [];
// }
// else{
// return widget.autoFillHintGroup!;
// }
// }
Widget setRequiredText() {
if (widget.required) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"*",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor()),
),
const SizedBox(
width: 8.0,
),
Text(widget.hintText,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor())),
],
);
} else {
return Text(widget.hintText,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()));
}
}
@override
void dispose() {
_focus.dispose();
super.dispose();
}
@override
void initState() {
_focus.addListener(_onFocusChange);
super.initState();
}
@override
Widget build(BuildContext context) {
return TextField(
autofillHints: widget.autoFillHintGroup,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
controller: widget.controller,
focusNode: _focus,
readOnly: makeEditable(),
//enabled: !makeEditable(),
obscureText: false,
onChanged: (_) => setState(() {
startup = false;
}),
decoration: InputDecoration(
label: setRequiredText(),
//labelStyle: TextStyle(color: MzanziInnovationHub.of(context)!.theme.primaryColor()),
fillColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
filled: true,
errorText: _errorText,
errorStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontWeight: FontWeight.bold),
//errorBorder: const InputBorder(),
//hintText: hintText,
//hintStyle: TextStyle(color: Colors.blueGrey[400]),
disabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 2.0,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 2.0,
),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
),
),
);
}
}

View File

@@ -0,0 +1,170 @@
import 'package:flutter/material.dart';
import '../../main.dart';
class MIHTimeField extends StatefulWidget {
final TextEditingController controller;
final String lableText;
final bool required;
const MIHTimeField({
super.key,
required this.controller,
required this.lableText,
required this.required,
});
@override
State<MIHTimeField> createState() => _MIHDateFieldState();
}
class _MIHDateFieldState extends State<MIHTimeField> {
final FocusNode _focus = FocusNode();
bool startup = true;
Future<void> _selectTime(BuildContext context) async {
TimeOfDay? picked = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
builder: (context, child) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true),
child: child as Widget,
);
},
);
if (picked != null) {
String hours = "";
String minutes = "";
setState(() {
if (picked.hour <= 9) {
hours = "0${picked.hour}";
} else {
hours = "${picked.hour}";
}
if (picked.minute <= 9) {
minutes = "0${picked.minute}";
} else {
minutes = "${picked.minute}";
}
widget.controller.text = "$hours:$minutes";
});
}
}
Widget setRequiredText() {
if (widget.required) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"*",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor()),
),
const SizedBox(
width: 8.0,
),
Text(widget.lableText,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor())),
],
);
} else {
return Text(widget.lableText,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()));
}
}
void _onFocusChange() {
setState(() {
startup = false;
});
}
String? get _errorText {
final text = widget.controller.text;
if (startup) {
return null;
}
if (!widget.required) {
return null;
}
if (text.isEmpty) {
return "${widget.lableText} is required";
}
return null;
}
@override
void dispose() {
_focus.dispose();
super.dispose();
}
@override
void initState() {
_focus.addListener(_onFocusChange);
super.initState();
}
@override
Widget build(BuildContext context) {
return TextField(
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
controller: widget.controller,
readOnly: true,
obscureText: false,
focusNode: _focus,
onChanged: (_) => setState(() {
startup = false;
}),
decoration: InputDecoration(
errorText: _errorText,
errorStyle: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontWeight: FontWeight.bold),
label: setRequiredText(),
//labelText: widget.lableText,
//labelStyle: const TextStyle(color: Colors.blueAccent),
prefixIcon: Icon(
Icons.access_alarm,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
fillColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
filled: true,
//hintText: hintText,
//hintStyle: TextStyle(color: Colors.blueGrey[400]),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 2.0,
),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 2.0,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
),
),
onTap: () {
_selectTime(context);
},
);
}
}

View File

@@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
class MIHAction extends StatefulWidget {
final void Function()? onTap;
final double iconSize;
final Widget icon;
const MIHAction({
super.key,
required this.icon,
required this.iconSize,
required this.onTap,
});
@override
State<MIHAction> createState() => _MIHActionState();
}
class _MIHActionState extends State<MIHAction> {
@override
void dispose() {
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return IconButton(
iconSize: widget.iconSize,
padding: const EdgeInsets.all(0),
onPressed: widget.onTap,
icon: widget.icon,
);
}
}

View File

@@ -0,0 +1,267 @@
import 'package:flutter/material.dart';
import '../../main.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/arguments.dart';
import 'package:supertokens_flutter/supertokens.dart';
import '../mih_profile_picture.dart';
class MIHAppDrawer extends StatefulWidget {
final AppUser signedInUser;
final ImageProvider<Object>? propicFile;
const MIHAppDrawer({
super.key,
required this.signedInUser,
required this.propicFile,
});
@override
State<MIHAppDrawer> createState() => _MIHAppDrawerState();
}
class _MIHAppDrawerState extends State<MIHAppDrawer> {
final proPicController = TextEditingController();
late Widget profilePictureLoaded;
Future<bool> signOut() async {
await SuperTokens.signOut(completionHandler: (error) {
// handle error if any
});
return true;
}
Widget displayProPic() {
// return MIHProfilePicture(
// profilePictureFile: widget.propicFile,
// proPicController: proPicController,
// proPic: null,
// width: 45,
// radius: 21,
// editable: false,
// onChange: (newProPic) {},
// ),
//print(widget.propicFile);
ImageProvider logoFrame =
MzanziInnovationHub.of(context)!.theme.logoFrame();
if (widget.propicFile != null) {
return GestureDetector(
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed(
'/user-profile',
arguments: AppProfileUpdateArguments(
widget.signedInUser, widget.propicFile),
);
},
child: MIHProfilePicture(
profilePictureFile: widget.propicFile,
proPicController: proPicController,
proPic: null,
width: 60,
radius: 27,
drawerMode: true,
editable: false,
onChange: (newProPic) {},
),
// Stack(
// alignment: Alignment.center,
// fit: StackFit.loose,
// children: [
// CircleAvatar(
// backgroundColor:
// MzanziInnovationHub.of(context)!.theme.primaryColor(),
// backgroundImage: widget.propicFile,
// //'https://media.licdn.com/dms/image/D4D03AQGd1-QhjtWWpA/profile-displayphoto-shrink_400_400/0/1671698053061?e=2147483647&v=beta&t=a3dJI5yxs5-KeXjj10LcNCFuC9IOfa8nNn3k_Qyr0CA'),
// radius: 27,
// ),
// SizedBox(
// width: 60,
// child: Image(image: logoFrame),
// )
// ],
// ),
);
} else {
return SizedBox(
width: 60,
child: Image(image: logoFrame),
);
}
}
@override
void dispose() {
proPicController.dispose();
super.dispose();
}
@override
void initState() {
setState(() {
profilePictureLoaded = displayProPic();
});
super.initState();
}
@override
Widget build(BuildContext context) {
// precacheImage(
// MzanziInnovationHub.of(context)!.theme.logoImage().image, context);
ImageProvider logoThemeSwitch =
MzanziInnovationHub.of(context)!.theme.logoImage();
return Drawer(
//backgroundColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
child: Stack(children: [
ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
child: SizedBox(
height: 400,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
profilePictureLoaded,
Text(
"${widget.signedInUser.fname} ${widget.signedInUser.lname}",
style: TextStyle(
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!
.theme
.primaryColor(),
),
),
Text(
"@${widget.signedInUser.username}",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!
.theme
.primaryColor(),
),
),
Text(
widget.signedInUser.type.toUpperCase(),
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!
.theme
.primaryColor(),
),
),
],
),
),
),
ListTile(
title: Row(
mainAxisSize: MainAxisSize.max,
children: [
Icon(
Icons.home_outlined,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
const SizedBox(width: 25.0),
Text(
"Home",
style: TextStyle(
//fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
),
],
),
onTap: () {
Navigator.of(context)
.pushNamedAndRemoveUntil('/', (route) => false);
},
),
ListTile(
title: Row(
mainAxisSize: MainAxisSize.max,
children: [
Icon(
Icons.logout,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
const SizedBox(width: 25.0),
Text(
"Sign Out",
style: TextStyle(
//fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
),
],
),
onTap: () async {
await SuperTokens.signOut(completionHandler: (error) {
//print(error);
});
if (await SuperTokens.doesSessionExist() == false) {
Navigator.of(context).popAndPushNamed('/');
}
},
)
],
),
Positioned(
top: 5,
right: 5,
width: 30,
height: 30,
child: InkWell(
onTap: () {
setState(() {
if (MzanziInnovationHub.of(context)?.theme.mode == "Dark") {
//darkm = !darkm;
MzanziInnovationHub.of(context)!.changeTheme(ThemeMode.light);
//print("Dark Mode: $darkm");
} else {
//darkm = !darkm;
MzanziInnovationHub.of(context)!.changeTheme(ThemeMode.dark);
//print("Dark Mode: $darkm");
}
Navigator.of(context).popAndPushNamed('/');
});
},
child: Image(image: logoThemeSwitch),
),
// IconButton(
// onPressed: () {
// setState(() {
// if (MzanziInnovationHub.of(context)?.theme.mode == "Dark") {
// //darkm = !darkm;
// MzanziInnovationHub.of(context)!.changeTheme(ThemeMode.light);
// //print("Dark Mode: $darkm");
// } else {
// //darkm = !darkm;
// MzanziInnovationHub.of(context)!.changeTheme(ThemeMode.dark);
// //print("Dark Mode: $darkm");
// }
// Navigator.of(context).popAndPushNamed('/');
// });
// },
// icon: Icon(
// Icons.light_mode,
// color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
// size: 35,
// ),
// ),
),
]),
);
}
}

View File

@@ -0,0 +1,103 @@
import 'package:flutter/material.dart';
import '../../main.dart';
class MIHBody extends StatefulWidget {
final bool borderOn;
final List<Widget> bodyItems;
const MIHBody({
super.key,
required this.borderOn,
required this.bodyItems,
});
@override
State<MIHBody> createState() => _MIHBodyState();
}
class _MIHBodyState extends State<MIHBody> {
//double paddingSize = 10;
double getHorizontalPaddingSize(Size screenSize) {
if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") {
if (widget.borderOn) {
return 10;
} else {
return 0;
}
} else {
// mobile
if (widget.borderOn) {
return 10;
} else {
return 0;
}
}
}
double getVerticalPaddingSize(Size screenSize) {
// mobile
if (widget.borderOn) {
return 10;
} else {
return 0;
}
}
Decoration? getBoader() {
if (widget.borderOn) {
return BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
width: 3.0),
);
} else {
return null;
}
}
@override
void dispose() {
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
Size screenSize = MediaQuery.sizeOf(context);
return Padding(
padding: EdgeInsets.only(
left: getHorizontalPaddingSize(screenSize),
right: getHorizontalPaddingSize(screenSize),
bottom: getVerticalPaddingSize(screenSize),
top: 0,
),
child: Container(
padding: EdgeInsets.only(
left: 10,
right: 10,
bottom: 10,
top: getVerticalPaddingSize(screenSize),
),
width: screenSize.width,
height: screenSize.height,
decoration: getBoader(),
child: ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: widget.bodyItems,
),
),
),
),
);
}
}

View File

@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
class MIHHeader extends StatefulWidget {
final MainAxisAlignment headerAlignment;
final List<Widget> headerItems;
const MIHHeader({
super.key,
required this.headerAlignment,
required this.headerItems,
});
@override
State<MIHHeader> createState() => _MIHHeaderState();
}
class _MIHHeaderState extends State<MIHHeader> {
@override
void dispose() {
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 50,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: widget.headerAlignment,
mainAxisSize: MainAxisSize.max,
children: widget.headerItems,
),
);
}
}

View File

@@ -0,0 +1,150 @@
import 'package:flutter/material.dart';
import 'package:patient_manager/mih_components/mih_layout/mih_app_drawer.dart';
import 'mih_body.dart';
import 'mih_header.dart';
class MIHLayoutBuilder extends StatefulWidget {
final Widget actionButton;
final Widget? secondaryActionButton;
final MIHHeader header;
final MIHBody body;
final MIHAppDrawer? actionDrawer;
final Widget? secondaryActionDrawer;
final Widget? bottomNavBar;
final bool pullDownToRefresh;
final Future<void> Function() onPullDown;
//final String type;
const MIHLayoutBuilder({
super.key,
required this.actionButton,
required this.header,
required this.secondaryActionButton,
required this.body,
required this.actionDrawer,
required this.secondaryActionDrawer,
required this.bottomNavBar,
required this.pullDownToRefresh,
required this.onPullDown,
});
@override
State<MIHLayoutBuilder> createState() => _MIHLayoutBuilderState();
}
class _MIHLayoutBuilderState extends State<MIHLayoutBuilder> {
List<Widget> getList() {
List<Widget> temp = [];
temp.add(widget.header);
temp.add(widget.body);
return temp;
}
// openTheDrawer() {
// _scaffoldKey.currentState!.openEndDrawer();
// }
Widget getLayoutHeader() {
List<Widget> temp = [];
temp.add(widget.actionButton);
temp.add(Flexible(child: widget.header));
if (widget.secondaryActionButton != null) {
temp.add(widget.secondaryActionButton!);
} else {
//print(widget.header.headerItems.length);
if (widget.header.headerItems.length == 1) {
temp.add(const SizedBox(
width: 50,
));
}
}
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: temp,
);
}
Widget getBody(double width, double height) {
if (widget.pullDownToRefresh == true) {
return LayoutBuilder(builder: (context, BoxConstraints constraints) {
double newheight = constraints.maxHeight;
//print(newheight);
return RefreshIndicator(
onRefresh: widget.onPullDown,
child: ListView.builder(
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
return SafeArea(
child: SizedBox(
width: width,
height: newheight,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(height: 5),
getLayoutHeader(),
const SizedBox(height: 5),
Expanded(child: widget.body),
],
),
),
);
},
// child: SafeArea(
// child: SizedBox(
// width: width,
// height: height,
// child: Column(
// mainAxisAlignment: MainAxisAlignment.start,
// children: [
// getLayoutHeader(),
// Expanded(child: widget.body),
// ],
// ),
// ),
// ),
),
);
});
} else {
return SafeArea(
child: SizedBox(
width: width,
height: height,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(height: 5),
getLayoutHeader(),
const SizedBox(height: 5),
Expanded(child: widget.body),
],
),
),
);
}
}
@override
void dispose() {
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
Size screenSize = MediaQuery.sizeOf(context);
return Scaffold(
//drawerEnableOpenDragGesture: true,
drawer: widget.actionDrawer,
endDrawer: widget.secondaryActionDrawer,
body: getBody(screenSize.width, screenSize.height),
bottomNavigationBar: widget.bottomNavBar,
);
}
}

View File

@@ -0,0 +1,245 @@
import 'package:flutter/material.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../mih_env/env.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/notification.dart';
import '../mih_pop_up_messages/mih_error_message.dart';
class MIHNotificationDrawer extends StatefulWidget {
final AppUser signedInUser;
final List<MIHNotification> notifications;
//final ImageProvider<Object>? propicFile;
const MIHNotificationDrawer({
super.key,
required this.signedInUser,
required this.notifications,
});
@override
State<MIHNotificationDrawer> createState() => _MIHNotificationDrawerState();
}
class _MIHNotificationDrawerState extends State<MIHNotificationDrawer> {
late List<List<String>> notificationList;
final baseAPI = AppEnviroment.baseApiUrl;
Future<void> updateNotificationAPICall(int index) async {
var response = await http.put(
Uri.parse(
"$baseAPI/notifications/update/${widget.notifications[index].idnotifications}"),
);
if (response.statusCode == 200) {
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed(
"/",
);
Navigator.of(context).pushNamed(
widget.notifications[index].action_path,
arguments: widget.signedInUser,
);
} else {
internetConnectionPopUp();
}
}
void internetConnectionPopUp() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Internet Connection");
},
);
}
List<List<String>> setTempNofitications() {
List<List<String>> temp = [];
temp.add(["Notification 1", "Notification Description 1"]);
temp.add(["Notification 2", "Notification Description 2"]);
temp.add(["Notification 3", "Notification Description 3"]);
temp.add(["Notification 4", "Notification Description 4"]);
temp.add(["Notification 5", "Notification Description 5"]);
temp.add(["Notification 6", "Notification Description 6"]);
temp.add(["Notification 7", "Notification Description 7"]);
temp.add(["Notification 8", "Notification Description 8"]);
temp.add(["Notification 9", "Notification Description 9"]);
temp.add(["Notification 10", "Notification Description 10"]);
return temp;
}
Widget displayTempNotifications(int index) {
String title = notificationList[index][0];
String subtitle = notificationList[index][1];
return ListTile(
title: Text(
title,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
subtitle: Text(
subtitle,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
onTap: () {},
);
}
Widget displayNotifications(int index) {
String title = widget.notifications[index].notification_type;
String subtitle =
"${widget.notifications[index].insert_date}\n${widget.notifications[index].notification_message}";
Widget notificationTitle;
if (widget.notifications[index].notification_read == "No") {
notificationTitle = Row(
children: [
Icon(
Icons.circle_notifications,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
const SizedBox(
width: 5,
),
Flexible(
child: Text(
title,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
),
],
);
} else {
notificationTitle = Row(
children: [
//const Icon(Icons.circle_notifications),
Flexible(
child: Text(
title,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
),
],
);
}
return ListTile(
title: notificationTitle,
subtitle: Text(
subtitle,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
onTap: () {
if (widget.notifications[index].notification_read == "No") {
updateNotificationAPICall(index);
} else {
Navigator.of(context).pushNamed(
widget.notifications[index].action_path,
arguments: widget.signedInUser,
);
}
},
);
}
Widget displayNotification() {
if (widget.notifications.isNotEmpty) {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, index) {
return Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
);
},
itemCount: widget.notifications.length,
itemBuilder: (context, index) {
//final patient = widget.patients[index].id_no.contains(widget.searchString);
//print(index);
return displayNotifications(index);
},
);
} else {
return const Padding(
padding: EdgeInsets.only(top: 35),
child: Text(
"No Notifications",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20),
),
);
}
}
@override
void dispose() {
super.dispose();
}
@override
void initState() {
setState(() {
notificationList = setTempNofitications();
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Drawer(
//backgroundColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(
"Notifications",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
],
),
),
displayNotification(),
// ListView.separated(
// shrinkWrap: true,
// physics: const NeverScrollableScrollPhysics(),
// separatorBuilder: (BuildContext context, index) {
// return Divider(
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// );
// },
// itemCount: widget.notifications.length,
// itemBuilder: (context, index) {
// //final patient = widget.patients[index].id_no.contains(widget.searchString);
// //print(index);
// return displayNotifications(index);
// },
// ),
],
),
));
}
}

View File

@@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:patient_manager/mih_components/mih_layout/mih_action.dart';
import 'package:pdf/pdf.dart';
import 'package:printing/printing.dart';
import '../../mih_objects/arguments.dart';
import '../mih_pop_up_messages/mih_loading_circle.dart';
class MIHPrintPreview extends StatefulWidget {
final PrintPreviewArguments arguments;
const MIHPrintPreview({
super.key,
required this.arguments,
});
@override
State<MIHPrintPreview> createState() => _MIHPrintPreviewState();
}
class _MIHPrintPreviewState extends State<MIHPrintPreview> {
MIHAction getActionButton() {
return MIHAction(
icon: const Icon(
Icons.close,
),
iconSize: 35,
onTap: () {
Navigator.pop(context);
},
);
}
@override
void dispose() {
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return PdfPreview(
pdfFileName: widget.arguments.fileName,
initialPageFormat: PdfPageFormat.a4,
loadingWidget: const Mihloadingcircle(),
actions: [getActionButton()],
build: (format) => widget.arguments.pdfData,
);
}
}

View File

@@ -0,0 +1,185 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../main.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
import 'mih_window.dart';
class MIHTile extends StatefulWidget {
final String tileName;
final String? videoYTLink;
final Widget tileIcon;
final void Function() onTap;
// final Widget tileIcon;
final Color p;
final Color s;
const MIHTile({
super.key,
required this.onTap,
required this.tileName,
this.videoYTLink,
required this.tileIcon,
required this.p,
required this.s,
});
@override
State<MIHTile> createState() => _MIHTileState();
}
class _MIHTileState extends State<MIHTile> {
late Color mainC;
late Color secondC;
late YoutubePlayerController videoController;
String getVideID() {
if (widget.videoYTLink != null) {
return YoutubePlayer.convertUrlToId(widget.videoYTLink!) as String;
} else {
return "";
}
}
// void listener() {
// if (_isPlayerReady && mounted && !videoController.value.isFullScreen) {
// setState(() {
// _playerState = videoController.value.playerState;
// _videoMetaData = videoController.metadata;
// });
// }
// }
@override
void dispose() {
videoController.dispose();
super.dispose();
}
@override
void initState() {
mainC = widget.p;
secondC = widget.s;
videoController = YoutubePlayerController(
initialVideoId: getVideID(),
flags: YoutubePlayerFlags(
autoPlay: false,
mute: true,
isLive: false,
));
super.initState();
}
void displayHint() {
if (widget.videoYTLink != null) {
showDialog(
context: context,
builder: (context) {
return MIHWindow(
fullscreen: false,
windowTitle: widget.tileName,
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
},
windowBody: [
YoutubePlayerBuilder(
player: YoutubePlayer(
controller: videoController,
showVideoProgressIndicator: true,
progressIndicatorColor: Colors.amber,
progressColors: ProgressBarColors(
playedColor: Colors.amber,
handleColor: Colors.amberAccent,
),
),
builder: (context, player) {
return player;
},
),
],
);
},
);
}
}
@override
Widget build(BuildContext context) {
// print(
// "Tile Name: ${widget.tileName}\nTitle Type: ${widget.tileIcon.runtimeType.toString()}");
return FittedBox(
alignment: Alignment.center,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
AnimatedContainer(
//alignment: Alignment.center,
width: 250,
height: 250,
duration: const Duration(seconds: 2),
child: Material(
color: mainC,
// shadowColor:
// MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// elevation: 5,
borderRadius: BorderRadius.circular(80),
child: InkWell(
borderRadius: BorderRadius.circular(80),
// ho
onTap: widget.onTap,
onLongPress: () {
displayHint();
},
// hoverDuration: ,
splashColor:
MzanziInnovationHub.of(context)!.theme.highlightColor(),
highlightColor:
MzanziInnovationHub.of(context)!.theme.highlightColor(),
child: widget.tileIcon,
),
),
),
// Material(
// color: mainC,
// borderRadius: BorderRadius.circular(80),
// child: Ink(
// // width: 200,
// // height: 200,
// padding: const EdgeInsets.all(20),
// child: InkWell(
// onTap: widget.onTap,
// hoverDuration: Duration(seconds: 2),
// highlightColor:
// MzanziInnovationHub.of(context)!.theme.messageTextColor(),
// child: SizedBox(
// height: 200,
// width: 200,
// child: widget.tileIcon,
// ),
// ),
// ),
// ),
const SizedBox(height: 10),
SizedBox(
width: 300,
child: Text(
widget.tileName,
textAlign: TextAlign.center,
softWrap: true,
overflow: TextOverflow.visible,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: 40.0,
fontWeight: FontWeight.bold,
),
),
)
],
),
);
}
}

View File

@@ -0,0 +1,214 @@
import 'package:flutter/material.dart';
import '../../main.dart';
class MIHWindow extends StatefulWidget {
final String windowTitle;
final List<Widget> windowBody;
final List<Widget> windowTools;
final void Function() onWindowTapClose;
final bool fullscreen;
const MIHWindow({
super.key,
required this.fullscreen,
required this.windowTitle,
required this.windowTools,
required this.onWindowTapClose,
required this.windowBody,
});
@override
State<MIHWindow> createState() => _MIHWindowState();
}
class _MIHWindowState extends State<MIHWindow> {
late double windowTitleSize;
late double horizontralWindowPadding;
late double vertticalWindowPadding;
late double windowWidth;
late double windowHeight;
late double width;
late double height;
void checkScreenSize() {
// print("screen width: $width");
// print("screen height: $height");
if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") {
setState(() {
windowTitleSize = 25;
horizontralWindowPadding = width / 7;
vertticalWindowPadding = 25;
windowWidth = width;
windowHeight = height;
});
} else {
setState(() {
windowTitleSize = 20;
horizontralWindowPadding = 10;
vertticalWindowPadding = 10;
windowWidth = width;
windowHeight = height;
});
}
}
Widget getWidnowClose() {
return Container(
alignment: Alignment.centerRight,
child: IconButton(
onPressed: widget.onWindowTapClose,
icon: Icon(
Icons.close,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
size: 35,
),
),
);
}
Widget getWidnowTools() {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: widget.windowTools,
);
}
Widget getWidnowTitle() {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Text(
widget.windowTitle,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: windowTitleSize,
fontWeight: FontWeight.bold,
),
),
),
],
);
}
Widget getWidnowHeader() {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
getWidnowTools(),
Expanded(
flex: 2,
child: getWidnowTitle(),
),
getWidnowClose(),
],
);
}
Widget getWidnowBody() {
if (widget.fullscreen) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: widget.windowBody,
),
),
);
} else {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: widget.windowBody,
),
);
}
}
Widget createWindow(Widget header, Widget body) {
Widget visibleItems;
if (widget.fullscreen) {
visibleItems = Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
header,
//const Divider(),
body,
],
);
} else {
visibleItems = SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
header,
//const Divider(),
body,
],
),
);
}
return Dialog(
insetPadding: EdgeInsets.symmetric(
horizontal: horizontralWindowPadding,
vertical: vertticalWindowPadding,
),
insetAnimationCurve: Easing.emphasizedDecelerate,
insetAnimationDuration: Durations.short1,
child: Container(
//padding: const EdgeInsets.all(10),
width: windowWidth,
//height: windowHeight,
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: visibleItems,
),
);
}
@override
void dispose() {
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
setState(() {
width = size.width;
height = size.height;
});
checkScreenSize();
return createWindow(
getWidnowHeader(),
getWidnowBody(),
);
}
}

View File

@@ -0,0 +1,328 @@
import 'package:flutter/material.dart';
import '../../main.dart';
import '../mih_inputs_and_buttons/mih_button.dart';
class MIHDeleteMessage extends StatefulWidget {
final String deleteType;
final void Function() onTap;
const MIHDeleteMessage({
super.key,
required this.deleteType,
required this.onTap,
});
@override
State<MIHDeleteMessage> createState() => _MIHDeleteMessageState();
}
class _MIHDeleteMessageState extends State<MIHDeleteMessage> {
var messageTypes = <String, Widget>{};
late double popUpWidth;
late double? popUpheight;
late double popUpTitleSize;
late double popUpSubtitleSize;
late double popUpBodySize;
late double popUpIconSize;
late double popUpPaddingSize;
late double width;
late double height;
void checkScreenSize() {
if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") {
setState(() {
popUpWidth = (width / 4) * 2;
popUpheight = null;
popUpTitleSize = 25.0;
popUpSubtitleSize = 20.0;
popUpBodySize = 15;
popUpPaddingSize = 25.0;
popUpIconSize = 100;
});
} else {
setState(() {
popUpWidth = width - (width * 0.1);
popUpheight = null;
popUpTitleSize = 20.0;
popUpSubtitleSize = 18.0;
popUpBodySize = 15;
popUpPaddingSize = 15.0;
popUpIconSize = 100;
});
}
}
void setDeleteNote() {
messageTypes["Note"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
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: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
//const SizedBox(height: 15),
Text(
"Are you sure you want to delete this?",
textAlign: TextAlign.center,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"This note will be deleted permanently. Are you certain you want to delete it?",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: widget.onTap,
buttonText: "Delete",
buttonColor: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
))
],
),
),
),
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,
),
),
),
],
);
}
void setFileNote() {
messageTypes["File"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
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: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
//const SizedBox(height: 15),
Text(
"Are you sure you want to delete this?",
textAlign: TextAlign.center,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"This file will be deleted permanently. Are you certain you want to delete it?",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: widget.onTap,
buttonText: "Delete",
buttonColor: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
))
],
),
),
),
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,
),
),
),
],
);
}
void setDeleteEmployee() {
messageTypes["Employee"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
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: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
//const SizedBox(height: 15),
Text(
"Are you sure you want to delete this?",
textAlign: TextAlign.center,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"This team member will be deleted permanently from the business profile. Are you certain you want to delete it?",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: widget.onTap,
buttonText: "Delete",
buttonColor: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
))
],
),
),
),
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,
),
),
),
],
);
}
Widget? getDeleteMessage(String type) {
return messageTypes[type];
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
setState(() {
width = size.width;
height = size.height;
});
checkScreenSize();
setDeleteNote();
setFileNote();
setDeleteEmployee();
//print(size);
// setState(() {
// width = size.width;
// height = size.height;
// });
return Dialog(child: getDeleteMessage(widget.deleteType));
}
}

View File

@@ -0,0 +1,859 @@
import 'package:flutter/material.dart';
import '../../main.dart';
class MIHErrorMessage extends StatefulWidget {
final String errorType;
const MIHErrorMessage({
super.key,
required this.errorType,
});
@override
State<MIHErrorMessage> createState() => _MIHErrorMessageState();
}
class _MIHErrorMessageState extends State<MIHErrorMessage> {
var messageTypes = <String, Widget>{};
late double popUpWidth;
late double? popUpheight;
late double popUpTitleSize;
late double popUpSubtitleSize;
late double popUpBodySize;
late double popUpIconSize;
late double popUpPaddingSize;
Size? size;
void checkScreenSize() {
if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") {
setState(() {
popUpWidth = (size!.width / 4) * 2;
popUpheight = null;
popUpTitleSize = 25.0;
popUpSubtitleSize = 20.0;
popUpBodySize = 15;
popUpPaddingSize = 25.0;
popUpIconSize = 100;
});
} else {
setState(() {
popUpWidth = size!.width - (size!.width * 0.1);
popUpheight = null;
popUpTitleSize = 20.0;
popUpSubtitleSize = 18.0;
popUpBodySize = 15;
popUpPaddingSize = 15.0;
popUpIconSize = 100;
});
}
}
void setInputError() {
messageTypes["Input Error"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
decoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 5.0),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
//const SizedBox(height: 5),
Text(
"Oops! Looks like some fields are missing.",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"We noticed that some required fields are still empty. To ensure your request is processed smoothly, please fill out all the highlighted fields before submitting the form again.",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 25),
RichText(
text: TextSpan(
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
children: <TextSpan>[
TextSpan(
text: "Here's a quick tip: ",
style: TextStyle(
fontStyle: FontStyle.italic,
color: MzanziInnovationHub.of(context)!
.theme
.errorColor())),
const TextSpan(
text: "Look for fields with an asterisk ("),
TextSpan(
text: "*",
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.errorColor())),
const TextSpan(
text: ") next to them, as these are mandatory."),
],
),
),
const SizedBox(height: 10),
],
),
),
),
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,
),
),
),
],
);
}
void setPasswordRequirementsError() {
messageTypes["Password Requirements"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
decoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 5.0),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
//const SizedBox(height: 15),
Text(
"Password Doesn't Meet Requirements",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"Oops! Your password doesn't quite meet our standards. To keep your account secure, please make sure your password meets the following requirements",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 25),
RichText(
text: TextSpan(
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
children: <TextSpan>[
TextSpan(
text: "Requirements:\n",
style: TextStyle(
fontStyle: FontStyle.italic,
fontSize: popUpBodySize,
color: MzanziInnovationHub.of(context)!
.theme
.errorColor())),
const TextSpan(
text: "1) Contailes at least 8 characters\n",
),
const TextSpan(
text: "2) Contains at least 1 uppercase letter (A-Z)\n",
),
const TextSpan(
text: "3) Contains at least 1 lowercase letter (a-z)\n",
),
const TextSpan(
text: "4) Contains at least 1 number (0-9)\n",
),
const TextSpan(
text:
"5) Contains at least 1 special character (!@#\$%^&*)\n",
),
],
),
),
const SizedBox(height: 10),
],
),
),
),
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,
),
),
),
],
);
}
void setInvalidUsernameError() {
messageTypes["Invalid Username"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
decoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 5.0),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
//const SizedBox(height: 15),
Text(
"Let's Fix That Username",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"Your username needs a little adjustment. To make sure everyone has a unique username, please start your username with a letter or number and use only letters, numbers or underscores. Let's try that again!",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 25),
],
),
),
),
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,
),
),
),
],
);
}
void setInvalidEmailError() {
messageTypes["Invalid Email"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
decoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 5.0),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
//const SizedBox(height: 15),
Text(
"Oops! Invalid Email",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"Looks like there's a little hiccup with that email address. Please double-check that you've entered it correctly, including the \"@\" symbol and a domain (like example@email.com).",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 25),
],
),
),
),
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,
),
),
),
],
);
}
void setUserExistsError() {
messageTypes["User Exists"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
decoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 5.0),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
//SizedBox(height: 15),
Text(
"Email Already Exists",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"An account is already registered with this email address. Please try logging in or use a different email.",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text(
"Here are some things to keep in mind:",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpSubtitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text(
"1) Are you sure you're using the correct email address associated with your account?\n2) Is your caps lock key on? Passwords are case-sensitive.\n3) If you've forgotten your password, no worries! Click on \"Forgot Password?\" to reset it.",
textAlign: TextAlign.left,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
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,
),
),
),
],
);
}
void setPwdMatchError() {
messageTypes["Password Match"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
decoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 5.0),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
//SizedBox(height: 15),
Text(
"Passwords Don't Match",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"The password and confirm password fields do not match. Please make sure they are identical.",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text(
"Here are some things to keep in mind:",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpSubtitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text(
"1) Are you sure you're using the correct email address associated with your account?\n2) Is your caps lock key on? Passwords are case-sensitive.\n3) If you've forgotten your password, no worries! Click on \"Forgot Password?\" to reset it.",
textAlign: TextAlign.left,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
],
),
),
),
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,
),
),
),
],
);
}
void setinvalidCredError() {
messageTypes["Invalid Credentials"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
decoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 5.0),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
//SizedBox(height: 15),
Text(
"Uh oh! Login attempt unsuccessful.",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"The email address or password you entered doesn't seem to match our records. Please double-check your information and try again.",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text(
"Here are some things to keep in mind:",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpSubtitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text(
"1) Are you sure you're using the correct email address associated with your account?\n2) Is your caps lock key on? Passwords are case-sensitive.\n3) If you've forgotten your password, no worries! Click on \"Forgot Password?\" to reset it.",
textAlign: TextAlign.left,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
],
),
),
),
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,
),
),
),
],
);
}
void setInternetError() {
messageTypes["Internet Connection"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
decoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 5.0),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
//const SizedBox(height: 15),
Text(
"Internet Connection Lost!",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"We seem to be having some trouble connecting you to the internet. This could be due to a temporary outage or an issue with your device's connection.",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text(
"Here are a few things you can try:",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpSubtitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text(
"1) Check your Wi-Fi signal strength or try connecting to a different network.\n2) Restart your device (computer, phone, etc.) and your router/modem.\n3) If you're using cellular data, ensure you have a strong signal and haven't reached your data limit.",
textAlign: TextAlign.left,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
],
),
),
),
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,
),
),
),
],
);
}
void setLocationError() {
messageTypes["Location Denied"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
decoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
width: 5.0),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
//const SizedBox(height: 15),
Text(
"Location Services Not Enabled",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"To get the most out of MIH, we need your location. Please go to the site settings of the app and enable location services. Once you do that, we can start showing you relevant information based on your location.",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
// const SizedBox(height: 15),
// Text(
// "Here are a few things you can try:",
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!.theme.errorColor(),
// fontSize: popUpSubtitleSize,
// fontWeight: FontWeight.bold,
// ),
// ),
// const SizedBox(height: 10),
// Text(
// "1) Check your Wi-Fi signal strength or try connecting to a different network.\n2) Restart your device (computer, phone, etc.) and your router/modem.\n3) If you're using cellular data, ensure you have a strong signal and haven't reached your data limit.",
// textAlign: TextAlign.left,
// style: TextStyle(
// color:
// MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// fontSize: popUpBodySize,
// fontWeight: FontWeight.bold,
// ),
// ),
const SizedBox(height: 10),
],
),
),
),
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,
),
),
),
],
);
}
Widget? getErrorMessage(String type) {
return messageTypes[type];
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
size = MediaQuery.of(context).size;
checkScreenSize();
setInputError();
setinvalidCredError();
setInternetError();
setUserExistsError();
setPwdMatchError();
setPasswordRequirementsError();
setInvalidEmailError();
setInvalidUsernameError();
setLocationError();
//print(size);
// setState(() {
// width = size.width;
// height = size.height;
// });
return Dialog(child: getErrorMessage(widget.errorType));
}
}

View File

@@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import '../../main.dart';
import 'package:gif/gif.dart';
class Mihloadingcircle extends StatefulWidget {
const Mihloadingcircle({super.key});
@override
State<Mihloadingcircle> createState() => _MihloadingcircleState();
}
class _MihloadingcircleState extends State<Mihloadingcircle>
with TickerProviderStateMixin {
late final GifController _controller;
late double popUpPaddingSize;
late double popUpWidth;
late double? popUpheight;
late double width;
late double height;
void checkScreenSize() {
if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") {
setState(() {
popUpWidth = 250;
popUpheight = 250;
popUpPaddingSize = 25.0;
});
} else {
setState(() {
popUpWidth = 250;
popUpheight = 250;
popUpPaddingSize = 15.0;
});
}
}
@override
void initState() {
_controller = GifController(vsync: this);
//_controller.animateTo(26);
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
ImageProvider loading =
MzanziInnovationHub.of(context)!.theme.loadingImage();
var size = MediaQuery.of(context).size;
setState(() {
width = size.width;
height = size.height;
});
checkScreenSize();
return Dialog(
//backgroundColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
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(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
width: 5.0),
),
child: Gif(
image: loading,
controller:
_controller, // if duration and fps is null, original gif fps will be used.
fps: 15,
//duration: const Duration(seconds: 3),
autostart: Autostart.loop,
placeholder: (context) => const Center(
child: CircularProgressIndicator(),
),
onFetchCompleted: () {
_controller.reset();
_controller.forward();
},
),
),
// Center(
// child: MzanziInnovationHub.of(context)!.theme.loadingImage()),
// ),
);
}
}

View File

@@ -0,0 +1,188 @@
import 'package:flutter/material.dart';
import '../../main.dart';
import '../../mih_objects/arguments.dart';
class MIHNotificationMessage extends StatefulWidget {
final NotificationArguments arguments;
const MIHNotificationMessage({
super.key,
required this.arguments,
});
@override
State<MIHNotificationMessage> createState() => _MIHNotificationMessageState();
}
class _MIHNotificationMessageState extends State<MIHNotificationMessage>
with SingleTickerProviderStateMixin {
//var messageTypes = <String, Widget>{};
late AnimationController _animationController;
late Animation<Offset> _scaleAnimation;
late double popUpWidth;
late double? popUpheight;
late double popUpTitleSize;
late double popUpSubtitleSize;
late double popUpBodySize;
late double popUpIconSize;
late double popUpPaddingSize;
late Color primary;
late Color secondary;
Size? size;
void checkScreenSize() {
if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") {
setState(() {
popUpWidth = (size!.width / 4) * 2;
popUpheight = 90;
popUpTitleSize = 20.0;
popUpSubtitleSize = 20.0;
popUpBodySize = 15;
popUpPaddingSize = 25.0;
popUpIconSize = 100;
});
} else {
setState(() {
popUpWidth = size!.width;
popUpheight = 90;
popUpTitleSize = 20.0;
popUpSubtitleSize = 18.0;
popUpBodySize = 15;
popUpPaddingSize = 5.0;
popUpIconSize = 100;
});
}
}
Widget notifyPopUp() {
//messageTypes["Input Error"] =
return GestureDetector(
onTap: widget.arguments.onTap,
child: Container(
padding:
EdgeInsets.symmetric(vertical: 5, horizontal: popUpPaddingSize),
alignment: Alignment.topLeft,
width: popUpWidth,
height: popUpheight,
decoration: BoxDecoration(
color: primary,
borderRadius: BorderRadius.circular(25.0),
border: Border.all(color: secondary, width: 5.0),
),
child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
//const SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.notifications,
color: secondary,
),
const SizedBox(width: 10),
Flexible(
child: Text(
widget.arguments.title,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: secondary,
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 5),
Wrap(
alignment: WrapAlignment.start,
children: [
SizedBox(
width: popUpWidth,
child: Text(
widget.arguments.body,
textAlign: TextAlign.left,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: secondary,
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
setState(() {
primary = MzanziInnovationHub.of(context)!.theme.primaryColor();
secondary = MzanziInnovationHub.of(context)!.theme.errorColor();
});
_animationController = AnimationController(
vsync: this,
duration:
const Duration(milliseconds: 300), // Adjust the duration as needed
);
_scaleAnimation = Tween<Offset>(
begin: const Offset(0, -1),
end: Offset.zero,
).animate(_animationController);
// Start the animation when
// the dialog is displayed
_animationController.forward();
}
@override
Widget build(BuildContext context) {
size = MediaQuery.of(context).size;
checkScreenSize();
//setInputError();
//print(size);
// setState(() {
// width = size.width;
// height = size.height;
// });
return SlideTransition(
position: _scaleAnimation,
child: Dialog(
insetPadding: const EdgeInsets.only(
top: 45,
left: 5,
right: 5,
),
shadowColor: secondary,
alignment: Alignment.topCenter,
child: notifyPopUp(),
),
);
// return SlideTransition(
// position: Tween<Offset>(
// begin: const Offset(0, -1),
// end: Offset.zero,
// ).animate(widget.animationController),
// child: Dialog(
// alignment: Alignment.topCenter,
// child: NotifyPopUp(),
// ),
// );
}
}

View File

@@ -0,0 +1,142 @@
import 'package:flutter/material.dart';
import '../../main.dart';
import '../mih_inputs_and_buttons/mih_button.dart';
class MIHSuccessMessage extends StatefulWidget {
final String successType;
final String successMessage;
const MIHSuccessMessage({
super.key,
required this.successType,
required this.successMessage,
});
@override
State<MIHSuccessMessage> createState() => _MIHSuccessMessageState();
}
class _MIHSuccessMessageState extends State<MIHSuccessMessage> {
var messageTypes = <String, Widget>{};
late String message;
late double popUpWidth;
late double? popUpheight;
late double popUpTitleSize;
late double popUpSubtitleSize;
late double popUpBodySize;
late double popUpIconSize;
late double popUpPaddingSize;
late Size? size;
void checkScreenSize() {
if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") {
setState(() {
popUpWidth = (size!.width / 4) * 2;
popUpheight = null;
popUpTitleSize = 25.0;
popUpSubtitleSize = 20.0;
popUpBodySize = 15;
popUpPaddingSize = 25.0;
popUpIconSize = 100;
});
} else {
setState(() {
popUpWidth = size!.width - (size!.width * 0.1);
popUpheight = null;
popUpTitleSize = 20.0;
popUpSubtitleSize = 18.0;
popUpBodySize = 15;
popUpPaddingSize = 15.0;
popUpIconSize = 100;
});
}
}
void setSuccessmessage() {
messageTypes["Success"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
decoration: BoxDecoration(
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
borderRadius: BorderRadius.circular(25.0),
border: Border.all(
color: MzanziInnovationHub.of(context)!.theme.successColor(),
width: 5.0),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.check_circle_outline_rounded,
size: popUpIconSize,
color: MzanziInnovationHub.of(context)!.theme.successColor(),
),
//const SizedBox(height: 15),
Text(
"Success!",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.successColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Center(
child: Text(
message,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 15),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: () {
Navigator.pop(context);
},
buttonText: "Dismiss",
buttonColor:
MzanziInnovationHub.of(context)!.theme.successColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
],
),
),
],
);
}
Widget? getSuccessMessage(String type) {
return messageTypes[type];
}
@override
void dispose() {
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
size = MediaQuery.of(context).size;
checkScreenSize();
message = widget.successMessage;
setSuccessmessage();
return Dialog(child: getSuccessMessage(widget.successType));
}
}

View File

@@ -0,0 +1,500 @@
import 'package:flutter/material.dart';
import '../../main.dart';
class MIHWarningMessage extends StatefulWidget {
final String warningType;
const MIHWarningMessage({
super.key,
required this.warningType,
});
@override
State<MIHWarningMessage> createState() => _MIHDeleteMessageState();
}
class _MIHDeleteMessageState extends State<MIHWarningMessage> {
var messageTypes = <String, Widget>{};
late double popUpWidth;
late double? popUpheight;
late double popUpTitleSize;
late double popUpSubtitleSize;
late double popUpBodySize;
late double popUpIconSize;
late double popUpPaddingSize;
late double width;
late double height;
void checkScreenSize() {
if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") {
setState(() {
popUpWidth = (width / 4) * 2;
popUpheight = null;
popUpTitleSize = 25.0;
popUpSubtitleSize = 20.0;
popUpBodySize = 15;
popUpPaddingSize = 25.0;
popUpIconSize = 100;
});
} else {
setState(() {
popUpWidth = width - (width * 0.1);
popUpheight = null;
popUpTitleSize = 20.0;
popUpSubtitleSize = 18.0;
popUpBodySize = 15;
popUpPaddingSize = 15.0;
popUpIconSize = 100;
});
}
}
void setNoAccess() {
messageTypes["No Access"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
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: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
//const SizedBox(height: 15),
Text(
"Access Pending",
textAlign: TextAlign.center,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"Your access request is currently being reviewed.\nOnce approved, you'll be able to view patient data.\nPlease follow up with the patient to approve your access request.",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
],
),
),
),
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,
),
),
),
],
);
}
void setNoPatientAccess() {
messageTypes["No Patient Access"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
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: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
//const SizedBox(height: 15),
Text(
"Patient Profile Access Needed",
textAlign: TextAlign.center,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"To proceed with booking an appointment, you must have access to the patient's profile. This will allow you to view their medical history and other relevant information. Please contact the appropriate administrator to request access.",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
],
),
),
),
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,
),
),
),
],
);
}
void setAppointmentCanelled() {
messageTypes["Appointment Canelled"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
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: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
//const SizedBox(height: 15),
Text(
"Appointment Cancelled",
textAlign: TextAlign.center,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"This appointment has been canceled. As a result, you no longer have access to the patient's profile. If you would like to view the patient's profile again, please book a new appointment.",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
],
),
),
),
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,
),
),
),
],
);
}
void setAccessCanelled() {
messageTypes["Access Cancelled"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
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: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
//const SizedBox(height: 15),
Text(
"Access Cancelled",
textAlign: TextAlign.center,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"This appointment has been canceled. As a result, access has been cancelled and the doctor no longer have access to the patient's profile. If you would like them to view the patient's profile again, please book a new appointment through them.",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
],
),
),
),
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,
),
),
),
],
);
}
void setAccessDeclined() {
messageTypes["Access Declined"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
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: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
//const SizedBox(height: 15),
Text(
"Access Declined",
textAlign: TextAlign.center,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"Your request to access the patient's profile has been denied. Please contact the patient directly to inquire about the reason for this restriction.",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
],
),
),
),
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,
),
),
),
],
);
}
void setExpiredAccess() {
messageTypes["Expired Access"] = Stack(
children: [
Container(
padding: EdgeInsets.all(popUpPaddingSize),
width: popUpWidth,
height: popUpheight,
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: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: popUpIconSize,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
//const SizedBox(height: 15),
Text(
"Access Expired",
textAlign: TextAlign.center,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpTitleSize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Text(
"You no longer have access to this patient profile. The authorized access period has ended. Access to a patients profile is limited to 7 days from appointment date.",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
],
),
),
),
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,
),
),
),
],
);
}
Widget? getDeleteMessage(String type) {
return messageTypes[type];
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
setState(() {
width = size.width;
height = size.height;
});
checkScreenSize();
setNoAccess();
setExpiredAccess();
setAccessDeclined();
setAppointmentCanelled();
setAccessCanelled();
//print(size);
// setState(() {
// width = size.width;
// height = size.height;
// });
return Dialog(child: getDeleteMessage(widget.warningType));
}
}

View File

@@ -0,0 +1,112 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import '../../main.dart';
// ignore: must_be_immutable
class MIHProfilePicture extends StatefulWidget {
final ImageProvider<Object>? profilePictureFile;
final TextEditingController proPicController;
PlatformFile? proPic;
final double width;
final double radius;
final bool drawerMode;
final bool editable;
final onChange;
MIHProfilePicture({
super.key,
required this.profilePictureFile,
required this.proPicController,
required this.proPic,
required this.width,
required this.radius,
required this.drawerMode,
required this.editable,
required this.onChange,
});
@override
State<MIHProfilePicture> createState() => _MIHProfilePictureState();
}
class _MIHProfilePictureState extends State<MIHProfilePicture> {
late ImageProvider<Object>? propicPreview;
//late PlatformFile proPic;
Widget displayEditableProPic() {
ImageProvider logoFrame;
if (!widget.drawerMode) {
logoFrame = MzanziInnovationHub.of(context)!.theme.altLogoFrame();
} else {
logoFrame = MzanziInnovationHub.of(context)!.theme.logoFrame();
}
if (widget.profilePictureFile != null) {
return Stack(
alignment: Alignment.center,
fit: StackFit.loose,
children: [
CircleAvatar(
backgroundColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
backgroundImage: propicPreview,
//'https://media.licdn.com/dms/image/D4D03AQGd1-QhjtWWpA/profile-displayphoto-shrink_400_400/0/1671698053061?e=2147483647&v=beta&t=a3dJI5yxs5-KeXjj10LcNCFuC9IOfa8nNn3k_Qyr0CA'),
radius: widget.radius,
),
SizedBox(
width: widget.width,
child: Image(image: logoFrame),
),
Visibility(
visible: widget.editable,
child: Positioned(
bottom: 0,
right: 0,
child: IconButton.filled(
onPressed: () async {
FilePickerResult? result =
await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['jpg', 'png'],
);
if (result == null) return;
final selectedFile = result.files.first;
setState(() {
widget.onChange(selectedFile);
widget.proPic = selectedFile;
//print("MIH Profile Picture: ${widget.proPic}");
propicPreview = MemoryImage(widget.proPic!.bytes!);
});
setState(() {
widget.proPicController.text = selectedFile.name;
});
},
icon: const Icon(Icons.edit),
),
),
),
],
);
} else {
return SizedBox(
width: widget.width,
child: Image(image: logoFrame),
);
}
}
@override
void initState() {
setState(() {
propicPreview = widget.profilePictureFile;
});
super.initState();
}
@override
Widget build(BuildContext context) {
return displayEditableProPic();
}
}

View File

@@ -0,0 +1,43 @@
enum Enviroment { dev, prod }
//
abstract class AppEnviroment {
static late String baseApiUrl;
static late String baseFileUrl;
static late String fingerPrintPluginKey;
static late Enviroment _enviroment;
static Enviroment get enviroment => _enviroment;
static setupEnv(Enviroment env) {
_enviroment = env;
switch (env) {
case Enviroment.dev:
{
baseApiUrl = "http://localhost:8080";
baseFileUrl = "http://localhost:9000";
//fingerPrintPluginKey = 'h5X7a5j14iUZCobI1ZeX';
break;
}
case Enviroment.prod:
{
baseApiUrl = "https://api.mzansi-innovation-hub.co.za";
baseFileUrl = "https://minio.mzansi-innovation-hub.co.za";
//fingerPrintPluginKey = 'h5X7a5j14iUZCobI1ZeX';
break;
}
}
}
static String getEnv() {
//_enviroment = env;
switch (_enviroment) {
case Enviroment.dev:
{
return "Dev";
}
case Enviroment.prod:
{
return "Prod";
}
}
}
}

View File

@@ -0,0 +1,55 @@
class AccessRequest {
final int idpatient_queue;
final String business_id;
final String app_id;
final String date_time;
final String access;
final String revoke_date;
final String Name;
final String type;
final String logo_path;
final String contact_no;
const AccessRequest({
required this.idpatient_queue,
required this.business_id,
required this.app_id,
required this.date_time,
required this.access,
required this.revoke_date,
required this.Name,
required this.type,
required this.logo_path,
required this.contact_no,
});
factory AccessRequest.fromJson(Map<String, dynamic> json) {
return switch (json) {
{
"idpatient_queue": int idpatient_queue,
'business_id': String business_id,
'app_id': String app_id,
'date_time': String date_time,
'access': String access,
'revoke_date': String revoke_date,
'Name': String Name,
'type': String type,
'logo_path': String logo_path,
'contact_no': String contact_no,
} =>
AccessRequest(
idpatient_queue: idpatient_queue,
business_id: business_id,
app_id: app_id,
date_time: date_time,
access: access,
revoke_date: revoke_date,
Name: Name,
type: type,
logo_path: logo_path,
contact_no: contact_no,
),
_ => throw const FormatException('Failed to load album.'),
};
}
}

View File

@@ -0,0 +1,35 @@
// ignore: file_names
class AppUser {
final int idUser;
final String email;
final String fname;
final String lname;
final String type;
final String app_id;
final String username;
final String pro_pic_path;
const AppUser(
this.idUser,
this.email,
this.fname,
this.lname,
this.type,
this.app_id,
this.username,
this.pro_pic_path,
);
factory AppUser.fromJson(dynamic json) {
return AppUser(
json['idUser'],
json['email'],
json['fname'],
json['lname'],
json['type'],
json['app_id'],
json['username'],
json['pro_pic_path'],
);
}
}

View File

@@ -0,0 +1,101 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import '../mih_objects/app_user.dart';
import '../mih_objects/business.dart';
import '../mih_objects/business_user.dart';
import '../mih_objects/notification.dart';
import '../mih_objects/patients.dart';
class NotificationArguments {
final String title;
final String body;
final void Function()? onTap;
NotificationArguments(
this.title,
this.body,
this.onTap,
);
}
class BusinessArguments {
final AppUser signedInUser;
final BusinessUser? businessUser;
final Business? business;
BusinessArguments(
this.signedInUser,
this.businessUser,
this.business,
);
}
class HomeArguments {
final AppUser signedInUser;
final BusinessUser? businessUser;
final Business? business;
final List<MIHNotification> notifi;
final String profilePicUrl;
HomeArguments(
this.signedInUser,
this.businessUser,
this.business,
this.notifi,
this.profilePicUrl,
);
}
class AppProfileUpdateArguments {
final AppUser signedInUser;
final ImageProvider<Object>? propicFile;
AppProfileUpdateArguments(this.signedInUser, this.propicFile);
}
class FileViewArguments {
final String link;
final String path;
FileViewArguments(
this.link,
this.path,
);
}
class PrintPreviewArguments {
final Uint8List pdfData;
final String fileName;
PrintPreviewArguments(
this.pdfData,
this.fileName,
);
}
class PatientViewArguments {
final AppUser signedInUser;
final Patient? selectedPatient;
final BusinessUser? businessUser;
final Business? business;
final String type;
PatientViewArguments(
this.signedInUser,
this.selectedPatient,
this.businessUser,
this.business,
this.type,
);
}
class PatientEditArguments {
final AppUser signedInUser;
final Patient selectedPatient;
PatientEditArguments(
this.signedInUser,
this.selectedPatient,
);
}

View File

@@ -0,0 +1,41 @@
// ignore: file_names
class Business {
final String business_id;
final String Name;
final String type;
final String registration_no;
final String logo_name;
final String logo_path;
final String contact_no;
final String bus_email;
final String app_id;
final String gps_location;
const Business(
this.business_id,
this.Name,
this.type,
this.registration_no,
this.logo_name,
this.logo_path,
this.contact_no,
this.bus_email,
this.app_id,
this.gps_location,
);
factory Business.fromJson(dynamic json) {
return Business(
json['business_id'],
json['Name'],
json['type'],
json['registration_no'],
json['logo_name'],
json['logo_path'],
json['contact_no'],
json['bus_email'],
json['app_id'],
json['gps_location'],
);
}
}

View File

@@ -0,0 +1,35 @@
// ignore: file_names
class BusinessEmployee {
final String business_id;
final String app_id;
final String title;
final String access;
final String fname;
final String lname;
final String email;
final String username;
const BusinessEmployee(
this.business_id,
this.app_id,
this.title,
this.access,
this.fname,
this.lname,
this.email,
this.username,
);
factory BusinessEmployee.fromJson(dynamic json) {
return BusinessEmployee(
json['business_id'],
json['app_id'],
json['title'],
json['access'],
json['fname'],
json['lname'],
json['email'],
json['username'],
);
}
}

View File

@@ -0,0 +1,32 @@
// ignore: file_names
class BusinessUser {
final int idbusiness_users;
final String business_id;
final String app_id;
final String signature;
final String sig_path;
final String title;
final String access;
const BusinessUser(
this.idbusiness_users,
this.business_id,
this.app_id,
this.signature,
this.sig_path,
this.title,
this.access,
);
factory BusinessUser.fromJson(dynamic json) {
return BusinessUser(
json['idbusiness_users'],
json['business_id'],
json['app_id'],
json['signature'],
json['sig_path'],
json['title'],
json['access'],
);
}
}

View File

@@ -0,0 +1,6 @@
class EditScreenArguments {
final String useremail;
final String selectedPatient;
EditScreenArguments({required this.useremail, required this.selectedPatient});
}

View File

@@ -0,0 +1,25 @@
class PFile {
final int idpatient_files;
final String file_path;
final String file_name;
final String insert_date;
final String app_id;
const PFile(
this.idpatient_files,
this.file_path,
this.file_name,
this.insert_date,
this.app_id,
);
factory PFile.fromJson(dynamic json) {
return PFile(
json['idpatient_files'],
json['file_path'],
json['file_name'],
json['insert_date'],
json['app_id'],
);
}
}

View File

@@ -0,0 +1,19 @@
class Medicine {
final String name;
final String unit;
final String form;
const Medicine(
this.name,
this.unit,
this.form,
);
factory Medicine.fromJson(dynamic json) {
return Medicine(
json['name'],
json['unit'],
json['dosage form'],
);
}
}

View File

@@ -0,0 +1,43 @@
class Note {
final int idpatient_notes;
final String note_name;
final String note_text;
final String insert_date;
final String doc_office;
final String doctor;
final String app_id;
const Note({
required this.idpatient_notes,
required this.note_name,
required this.note_text,
required this.insert_date,
required this.doc_office,
required this.doctor,
required this.app_id,
});
factory Note.fromJson(Map<String, dynamic> json) {
return switch (json) {
{
"idpatient_notes": int idpatient_notes,
"note_name": String note_name,
"note_text": String note_text,
"insert_date": String insert_date,
"doc_office": String doc_office,
"doctor": String doctor,
"app_id": String app_id,
} =>
Note(
idpatient_notes: idpatient_notes,
note_name: note_name,
note_text: note_text,
insert_date: insert_date,
doc_office: doc_office,
doctor: doctor,
app_id: app_id,
),
_ => throw const FormatException('Failed to load album.'),
};
}
}

View File

@@ -0,0 +1,43 @@
class MIHNotification {
final int idnotifications;
final String app_id;
final String notification_message;
final String notification_read;
final String action_path;
final String insert_date;
final String notification_type;
const MIHNotification({
required this.idnotifications,
required this.app_id,
required this.notification_message,
required this.notification_read,
required this.action_path,
required this.insert_date,
required this.notification_type,
});
factory MIHNotification.fromJson(Map<String, dynamic> json) {
return switch (json) {
{
"idnotifications": int idnotifications,
"app_id": String app_id,
"notification_message": String notification_message,
"notification_read": String notification_read,
"action_path": String action_path,
"insert_date": String insert_date,
"notification_type": String notification_type,
} =>
MIHNotification(
idnotifications: idnotifications,
app_id: app_id,
notification_message: notification_message,
notification_read: notification_read,
action_path: action_path,
insert_date: insert_date,
notification_type: notification_type,
),
_ => throw const FormatException('Failed to load Notifications.'),
};
}
}

View File

@@ -0,0 +1,63 @@
class PatientAccess {
final String business_id;
final String business_name;
final String app_id;
final String fname;
final String lname;
final String id_no;
final String type;
final String status;
final String approved_by;
final String approved_on;
final String requested_by;
final String requested_on;
const PatientAccess({
required this.business_id,
required this.business_name,
required this.app_id,
required this.fname,
required this.lname,
required this.id_no,
required this.type,
required this.status,
required this.approved_by,
required this.approved_on,
required this.requested_by,
required this.requested_on,
});
factory PatientAccess.fromJson(Map<String, dynamic> json) {
return switch (json) {
{
"business_id": String business_id,
'business_name': String business_name,
'app_id': String app_id,
'fname': String fname,
'lname': String lname,
'id_no': String id_no,
'type': String type,
'status': String status,
'approved_by': String approved_by,
'approved_on': String approved_on,
'requested_by': String requested_by,
'requested_on': String requested_on,
} =>
PatientAccess(
business_id: business_id,
business_name: business_name,
app_id: app_id,
fname: fname,
lname: lname,
id_no: id_no,
type: type,
status: status,
approved_by: approved_by,
approved_on: approved_on,
requested_by: requested_by,
requested_on: requested_on,
),
_ => throw const FormatException('Failed to load Patient Access List.'),
};
}
}

View File

@@ -0,0 +1,51 @@
class PatientQueue {
final int idpatient_queue;
final String business_id;
final String app_id;
final String date_time;
final String id_no;
final String first_name;
final String last_name;
final String medical_aid_no;
final String business_name;
const PatientQueue({
required this.idpatient_queue,
required this.business_id,
required this.app_id,
required this.date_time,
required this.id_no,
required this.first_name,
required this.last_name,
required this.medical_aid_no,
required this.business_name,
});
factory PatientQueue.fromJson(Map<String, dynamic> json) {
return switch (json) {
{
"idpatient_queue": int idpatient_queue,
'business_id': String business_id,
'app_id': String app_id,
'date_time': String date_time,
'id_no': String id_no,
'first_name': String first_name,
'last_name': String last_name,
'medical_aid_no': String medical_aid_no,
'business_name': String business_name,
} =>
PatientQueue(
idpatient_queue: idpatient_queue,
business_id: business_id,
app_id: app_id,
date_time: date_time,
id_no: id_no,
first_name: first_name,
last_name: last_name,
medical_aid_no: medical_aid_no,
business_name: business_name,
),
_ => throw const FormatException('Failed to load album.'),
};
}
}

View File

@@ -0,0 +1,75 @@
class Patient {
final int idpatients;
final String id_no;
final String first_name;
final String last_name;
final String email;
final String cell_no;
final String medical_aid;
final String medical_aid_name;
final String medical_aid_no;
final String medical_aid_main_member;
final String medical_aid_code;
final String medical_aid_scheme;
final String address;
final String app_id;
const Patient({
required this.idpatients,
required this.id_no,
required this.first_name,
required this.last_name,
required this.email,
required this.cell_no,
required this.medical_aid,
required this.medical_aid_name,
required this.medical_aid_no,
required this.medical_aid_main_member,
required this.medical_aid_code,
required this.medical_aid_scheme,
required this.address,
required this.app_id,
});
factory Patient.fromJson(Map<String, dynamic> json) {
return switch (json) {
{
"idpatients": int idpatients,
'id_no': String id_no,
'first_name': String first_name,
'last_name': String last_name,
'email': String email,
'cell_no': String cell_no,
'medical_aid': String medical_aid,
'medical_aid_name': String medical_aid_name,
'medical_aid_no': String medical_aid_no,
'medical_aid_main_member': String medical_aid_main_member,
'medical_aid_code': String medical_aid_code,
'medical_aid_scheme': String medical_aid_scheme,
'address': String address,
'app_id': String app_id,
} =>
Patient(
idpatients: idpatients,
id_no: id_no,
first_name: first_name,
last_name: last_name,
email: email,
cell_no: cell_no,
medical_aid: medical_aid,
medical_aid_name: medical_aid_name,
medical_aid_no: medical_aid_no,
medical_aid_main_member: medical_aid_main_member,
medical_aid_code: medical_aid_code,
medical_aid_scheme: medical_aid_scheme,
address: address,
app_id: app_id,
),
_ => throw const FormatException('Failed to load album.'),
};
}
String getIDNum() {
return id_no;
}
}

View File

@@ -0,0 +1,65 @@
class Perscription {
final String name;
final String unit;
final String form;
final String fullForm;
final String quantity;
final String dosage;
final String times;
final String days;
final String repeats;
const Perscription({
required this.name,
required this.unit,
required this.form,
required this.fullForm,
required this.quantity,
required this.dosage,
required this.times,
required this.days,
required this.repeats,
});
factory Perscription.fromJson(Map<String, dynamic> json) {
return switch (json) {
{
"name": String name,
'unit': String unit,
'form': String form,
'fullForm': String fullForm,
'quantity': String quantity,
'dosage': String dosage,
'times': String times,
'days': String days,
'repeats': String repeats,
} =>
Perscription(
name: name,
unit: unit,
form: form,
fullForm: fullForm,
quantity: quantity,
dosage: dosage,
times: times,
days: days,
repeats: repeats,
),
_ => throw const FormatException('Failed to load album.'),
};
}
Map<String, dynamic> toJson() {
return {
"name": name,
'unit': unit,
'form': form,
'fullForm': fullForm,
'quantity': quantity,
'dosage': dosage,
'times': times,
'days': days,
'repeats': repeats,
};
}
}

View File

@@ -0,0 +1,23 @@
class SessionST {
final String status;
final bool exists;
const SessionST({
required this.status,
required this.exists,
});
factory SessionST.fromJson(Map<String, dynamic> json) {
return switch (json) {
{
'status': String status,
'exists': bool exists,
} =>
SessionST(
status: status,
exists: exists,
),
_ => throw const FormatException('Failed to load SessionST.'),
};
}
}

View File

@@ -0,0 +1,466 @@
import 'package:flutter/material.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_layout/mih_tile.dart';
import '../../main.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import "package:universal_html/js.dart" as js;
import 'dart:html' as html;
class MIHAbout extends StatefulWidget {
const MIHAbout({
super.key,
});
@override
State<MIHAbout> createState() => _MIHAboutState();
}
class _MIHAboutState extends State<MIHAbout> {
MIHAction getActionButton() {
return MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pushNamedAndRemoveUntil(
'/',
arguments: true,
(route) => false,
);
},
);
}
MIHHeader getHeader() {
return const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"About",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: false,
bodyItems: [
SizedBox(
width: 165,
child: Image(
image: MzanziInnovationHub.of(context)!.theme.altLogoImage()),
),
const SizedBox(
height: 10,
),
const Text(
"Mzansi Innovation Hub",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 10.0),
child: Divider(),
),
// const SizedBox(
// height: 10,
// ),
Wrap(
alignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 10,
runSpacing: 10,
children: [
ourVision(),
ourMission(),
],
),
const SizedBox(
height: 10,
),
Wrap(
alignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 10,
runSpacing: 10,
children: [
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: () {
js.context.callMethod("presentAddToHome");
},
buttonText: "Install MIH",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: () {
html.window.open(
'https://www.youtube.com/playlist?list=PLuT35kJIui0H5kXjxNOZlHoOPZbQLr4qh',
'new tab');
},
buttonText: "MIH Beginners Guide",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
]),
const SizedBox(
height: 10,
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 10.0),
child: Divider(),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
// spacing: 10,
// runSpacing: 10,
children: [
founderTitle(),
founderBio(),
],
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 10.0),
child: Divider(),
),
mihSocials(),
],
);
}
Widget founderBio() {
String bio = "";
bio += "BSc Comnputer Science & Information Systems\n";
bio += "(University of the Western Cap)\n";
bio +=
"6 Year of banking experience with one of the big 5 banks of South Africa.";
ImageProvider logoFrame =
MzanziInnovationHub.of(context)!.theme.altLogoFrame();
return Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 10,
runSpacing: 10,
children: [
SizedBox(
width: 300,
child: Stack(
alignment: Alignment.center,
fit: StackFit.loose,
children: [
CircleAvatar(
backgroundColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
backgroundImage: const AssetImage("images/founder.jpg"),
//'https://media.licdn.com/dms/image/D4D03AQGd1-QhjtWWpA/profile-displayphoto-shrink_400_400/0/1671698053061?e=2147483647&v=beta&t=a3dJI5yxs5-KeXjj10LcNCFuC9IOfa8nNn3k_Qyr0CA'),
radius: 75,
),
SizedBox(
width: 165,
child: Image(image: logoFrame),
)
],
),
),
SizedBox(
width: 400,
child: Text(
bio,
textAlign: TextAlign.center,
style: const TextStyle(
//fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
const SizedBox(
height: 10,
),
],
);
}
Widget founderTitle() {
String heading = "Yasien Meth (Founder & CEO)";
return Column(
children: [
Text(
heading,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
const SizedBox(
height: 10,
),
],
);
}
Widget ourVision() {
String heading = "Our Vision";
String vision =
"Digitizing Mzansi one process at a time. Discover essential Mzansi apps to streamline your personal and professional life. Simplify your daily tasks with our user-friendly solutions.";
return SizedBox(
width: 500,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Text(
heading,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
const SizedBox(
height: 10,
),
Text(
vision,
textAlign: TextAlign.center,
style: const TextStyle(
//fontWeight: FontWeight.bold,
fontSize: 15,
),
),
],
),
);
}
Widget ourMission() {
String heading = "Our Mission";
String mission =
"Bridge the digital divide in Mzansi, ensuring that everyone can benefit from the power of technology. We empower lives by providing simple, elegant solutions that elevate daily experiences. With our user-friendly approach, we're making the digital world accessible to all, ensuring no one is left behind in the digital revolution.";
return SizedBox(
width: 500,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
heading,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
const SizedBox(
height: 10,
),
Text(
mission,
textAlign: TextAlign.center,
style: const TextStyle(
//fontWeight: FontWeight.bold,
fontSize: 15,
),
),
],
),
);
}
Widget mihSocials() {
String heading = "Follow Our Journey";
return Column(
children: [
Text(
heading,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
const SizedBox(
height: 10,
),
SizedBox(
width: 500,
height: 300,
child: GridView.builder(
padding: const EdgeInsets.only(
// left: width / 10,
// right: width / 10,
// //bottom: height / 5,
// top: 20,
),
// physics: ,
// shrinkWrap: true,
itemCount: getSocialsList().length,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
mainAxisSpacing: 15, maxCrossAxisExtent: 150),
itemBuilder: (context, index) {
return getSocialsList()[index];
},
),
),
],
);
}
List<Widget> getSocialsList() {
List<Widget> socials = [];
socials.add(MIHTile(
onTap: () {
html.window
.open('https://www.tiktok.com/@mzansi.innovation.hub', 'new tab');
},
tileName: "TikTok",
tileIcon: Center(
child: FaIcon(
FontAwesomeIcons.tiktok,
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
size: 200,
),
),
p: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
s: MzanziInnovationHub.of(context)!.theme.primaryColor(),
));
//==================================================================
socials.add(MIHTile(
onTap: () {
html.window
.open('https://www.instagram.com/mzansi.innovation.hub', 'new tab');
},
tileName: "Instagram",
tileIcon: Center(
child: FaIcon(
FontAwesomeIcons.instagram,
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
size: 200,
),
),
p: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
s: MzanziInnovationHub.of(context)!.theme.primaryColor(),
));
//==================================================================
socials.add(MIHTile(
onTap: () {
html.window
.open('https://www.youtube.com/@mzansiinnovationhub', 'new tab');
},
tileName: "YouTube",
tileIcon: Center(
child: FaIcon(
FontAwesomeIcons.youtube,
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
size: 175,
),
),
p: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
s: MzanziInnovationHub.of(context)!.theme.primaryColor(),
));
//==================================================================
socials.add(MIHTile(
onTap: () {
html.window.open('https://x.com/mzansi_inno_hub', 'new tab');
},
tileName: "X",
tileIcon: Center(
child: FaIcon(
FontAwesomeIcons.xTwitter,
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
size: 200,
),
),
p: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
s: MzanziInnovationHub.of(context)!.theme.primaryColor(),
));
//==================================================================
socials.add(MIHTile(
onTap: () {
html.window.open(
'https://www.linkedin.com/company/mzansi-innovation-hub/',
'new tab');
},
tileName: "LinkedIn",
tileIcon: Center(
child: FaIcon(
FontAwesomeIcons.linkedin,
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
size: 200,
),
),
p: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
s: MzanziInnovationHub.of(context)!.theme.primaryColor(),
));
//==================================================================
socials.add(MIHTile(
onTap: () {
html.window.open(
'https://www.facebook.com/profile.php?id=61565345762136',
'new tab');
},
tileName: "FaceBook",
tileIcon: Center(
child: FaIcon(
FontAwesomeIcons.facebook,
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
size: 200,
),
),
p: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
s: MzanziInnovationHub.of(context)!.theme.primaryColor(),
));
//==================================================================
return socials;
}
@override
void dispose() {
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MIHLayoutBuilder(
actionButton: getActionButton(),
secondaryActionButton: null,
header: getHeader(),
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
}
}

View File

@@ -0,0 +1,428 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../../main.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../../mih_components/mih_layout/mih_window.dart';
import '../../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_warning_message.dart';
import '../../../mih_env/env.dart';
import '../../../mih_objects/access_request.dart';
import '../../../mih_objects/app_user.dart';
class BuildAccessRequestList extends StatefulWidget {
final List<AccessRequest> accessRequests;
final AppUser signedInUser;
const BuildAccessRequestList({
super.key,
required this.accessRequests,
required this.signedInUser,
});
@override
State<BuildAccessRequestList> createState() => _BuildPatientsListState();
}
class _BuildPatientsListState extends State<BuildAccessRequestList> {
String baseAPI = AppEnviroment.baseApiUrl;
late double popUpWidth;
late double? popUpheight;
late double popUpButtonWidth;
late double popUpTitleSize;
late double popUpSubtitleSize;
late double popUpBodySize;
late double popUpIconSize;
late double popUpPaddingSize;
late double width;
late double height;
Future<void> updateAccessAPICall(int index, String accessType) async {
var response = await http.put(
Uri.parse("$baseAPI/access-requests/update/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": widget.accessRequests[index].business_id,
"app_id": widget.accessRequests[index].app_id,
"date_time": widget.accessRequests[index].date_time,
"access": accessType,
}),
);
if (response.statusCode == 200) {
//Navigator.of(context).pushNamed('/home');
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed(
'/patient-access-review',
arguments: widget.signedInUser,
);
String message = "";
if (accessType == "approved") {
message =
"You've successfully approved the access request! ${widget.accessRequests[index].Name} now has access to your profile until ${widget.accessRequests[index].revoke_date.substring(0, 16).replaceAll("T", " ")}.";
} else {
message =
"You've declined the access request. ${widget.accessRequests[index].Name} will not have access to your profile.";
}
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,
);
},
);
}
void accessCancelledWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "Access Cancelled");
},
);
}
Widget displayQueue(int index) {
String line1 =
"Appointment: ${widget.accessRequests[index].date_time.substring(0, 16).replaceAll("T", " ")}";
String line2 = "";
line2 += "Requestor: ${widget.accessRequests[index].Name}\n";
//subtitle += "Business Type: ${widget.accessRequests[index].type}\n";
String line3 = "Access: ";
String access = "";
var nowDate = DateTime.now();
var expireyDate = DateTime.parse(widget.accessRequests[index].revoke_date);
if (expireyDate.isBefore(nowDate)) {
access += "EXPIRED";
} else {
access += "${widget.accessRequests[index].access.toUpperCase()}";
}
String line4 = "";
if (widget.accessRequests[index].revoke_date.contains("9999")) {
line4 += "Access Expiration date: NOT SET";
} else {
line4 +=
"Access Expiration date: ${widget.accessRequests[index].revoke_date.substring(0, 10)}";
}
TextSpan accessWithColour;
if (access == "APPROVED") {
accessWithColour = TextSpan(
text: "$access\n",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.successColor()));
} else if (access == "PENDING") {
accessWithColour = TextSpan(
text: "$access\n",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.messageTextColor()));
} else {
accessWithColour = TextSpan(
text: "$access\n",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor()));
}
return ListTile(
title: Text(
line1,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
subtitle: RichText(
text: TextSpan(
text: line2,
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(text: line3),
accessWithColour,
TextSpan(text: line4),
]),
),
// Text(
// subtitle,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// ),
// ),
onTap: () {
if (access == "CANCELLED") {
accessCancelledWarning();
} else {
viewApprovalPopUp(index);
}
},
// trailing: Icon(
// Icons.arrow_forward,
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// ),
);
}
void checkScreenSize() {
if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") {
setState(() {
popUpWidth = (width / 4) * 2;
popUpheight = null;
popUpButtonWidth = 300;
popUpTitleSize = 25.0;
popUpSubtitleSize = 20.0;
popUpBodySize = 15;
popUpPaddingSize = 25.0;
popUpIconSize = 100;
});
} else {
setState(() {
popUpWidth = width - (width * 0.1);
popUpheight = null;
popUpButtonWidth = 300;
popUpTitleSize = 20.0;
popUpSubtitleSize = 18.0;
popUpBodySize = 15;
popUpPaddingSize = 15.0;
popUpIconSize = 100;
});
}
}
void viewApprovalPopUp(int index) {
String subtitle =
"Appointment: ${widget.accessRequests[index].date_time.substring(0, 16).replaceAll("T", " ")}\n";
subtitle += "Requestor: ${widget.accessRequests[index].Name}\n";
subtitle += "Business Type: ${widget.accessRequests[index].type}\n\n";
subtitle +=
"You are about to approve an access request to your patient profile.\nPlease be aware that once approved, ${widget.accessRequests[index].Name} will have access to your profile information until ${widget.accessRequests[index].revoke_date.substring(0, 16).replaceAll("T", " ")}.\nIf you are unsure about an upcoming appointment with ${widget.accessRequests[index].Name}, please contact ${widget.accessRequests[index].contact_no} for clarification before approving this request.\n";
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Update Appointment Access",
windowBody: [
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Text(
subtitle,
textAlign: TextAlign.left,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
//fontWeight: FontWeight.bold,
),
),
),
Wrap(
runSpacing: 10,
spacing: 10,
children: [
SizedBox(
width: popUpButtonWidth,
height: 50,
child: MIHButton(
onTap: () {
updateAccessAPICall(index, "declined");
},
buttonText: "Decline",
buttonColor:
MzanziInnovationHub.of(context)!.theme.errorColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
SizedBox(
width: popUpButtonWidth,
height: 50,
child: MIHButton(
onTap: () {
updateAccessAPICall(index, "approved");
},
buttonText: "Approve",
buttonColor:
MzanziInnovationHub.of(context)!.theme.successColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
],
),
const SizedBox(
height: 10,
),
],
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
}),
);
// showDialog(
// context: context,
// barrierDismissible: false,
// builder: (context) => Dialog(
// child: Stack(
// children: [
// Container(
// //padding: const EdgeInsets.all(15.0),
// width: popUpWidth,
// height: popUpheight,
// 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: SingleChildScrollView(
// padding: EdgeInsets.all(popUpPaddingSize),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// Text(
// "Update Appointment Access",
// textAlign: TextAlign.center,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// fontSize: popUpTitleSize,
// fontWeight: FontWeight.bold,
// ),
// ),
// const SizedBox(height: 15.0),
// Text(
// subtitle,
// textAlign: TextAlign.left,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// fontSize: popUpBodySize,
// //fontWeight: FontWeight.bold,
// ),
// ),
// const SizedBox(height: 10.0),
// Wrap(
// runSpacing: 10,
// spacing: 10,
// children: [
// SizedBox(
// width: popUpButtonWidth,
// height: 50,
// child: MIHButton(
// onTap: () {
// updateAccessAPICall(index, "declined");
// },
// buttonText: "Decline",
// buttonColor: MzanziInnovationHub.of(context)!
// .theme
// .errorColor(),
// textColor: MzanziInnovationHub.of(context)!
// .theme
// .primaryColor(),
// ),
// ),
// SizedBox(
// width: popUpButtonWidth,
// height: 50,
// child: MIHButton(
// onTap: () {
// updateAccessAPICall(index, "approved");
// },
// buttonText: "Approve",
// buttonColor: MzanziInnovationHub.of(context)!
// .theme
// .successColor(),
// textColor: MzanziInnovationHub.of(context)!
// .theme
// .primaryColor(),
// ),
// ),
// ],
// )
// ],
// ),
// ),
// ),
// 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() {
super.dispose();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
setState(() {
width = size.width;
height = size.height;
});
checkScreenSize();
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, index) {
return Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
);
},
itemCount: widget.accessRequests.length,
itemBuilder: (context, index) {
//final patient = widget.patients[index].id_no.contains(widget.searchString);
//print(index);
return displayQueue(index);
},
);
}
}

View File

@@ -0,0 +1,465 @@
import 'package:flutter/material.dart';
import '../../../main.dart';
import '../../../mih_apis/mih_api_calls.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../../mih_components/mih_layout/mih_window.dart';
import '../../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_warning_message.dart';
import '../../../mih_env/env.dart';
import '../../../mih_objects/app_user.dart';
import '../../../mih_objects/patient_access.dart';
class BuildBusinessAccessList extends StatefulWidget {
final List<PatientAccess> patientAccessList;
final AppUser signedInUser;
const BuildBusinessAccessList({
super.key,
required this.patientAccessList,
required this.signedInUser,
});
@override
State<BuildBusinessAccessList> createState() => _BuildPatientsListState();
}
class _BuildPatientsListState extends State<BuildBusinessAccessList> {
String baseAPI = AppEnviroment.baseApiUrl;
late double popUpWidth;
late double? popUpheight;
late double popUpButtonWidth;
late double popUpTitleSize;
late double popUpSubtitleSize;
late double popUpBodySize;
late double popUpIconSize;
late double popUpPaddingSize;
late double width;
late double height;
// Future<void> updateAccessAPICall(int index, String accessType) async {
// var response = await http.put(
// Uri.parse("$baseAPI/access-requests/update/"),
// headers: <String, String>{
// "Content-Type": "application/json; charset=UTF-8"
// },
// body: jsonEncode(<String, dynamic>{
// "business_id": widget.patientAccessList[index].business_id,
// "app_id": widget.patientAccessList[index].app_id,
// "date_time": widget.patientAccessList[index].date_time,
// "access": accessType,
// }),
// );
// if (response.statusCode == 200) {
// //Navigator.of(context).pushNamed('/home');
// Navigator.of(context).pop();
// Navigator.of(context).pop();
// Navigator.of(context).pushNamed(
// '/patient-access-review',
// arguments: widget.signedInUser,
// );
// String message = "";
// if (accessType == "approved") {
// message =
// "You've successfully approved the access request! ${widget.patientAccessList[index].Name} now has access to your profile until ${widget.patientAccessList[index].revoke_date.substring(0, 16).replaceAll("T", " ")}.";
// } else {
// message =
// "You've declined the access request. ${widget.patientAccessList[index].Name} will not have access to your profile.";
// }
// 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,
);
},
);
}
void accessCancelledWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "Access Cancelled");
},
);
}
Widget displayQueue(int index) {
String line1 =
"Business Name: ${widget.patientAccessList[index].requested_by}";
String line2 = "";
line2 +=
"Request Date: ${widget.patientAccessList[index].requested_on.substring(0, 16).replaceAll("T", " ")}\n";
line2 +=
"Profile Type: ${widget.patientAccessList[index].type.toUpperCase()}\n";
//subtitle += "Business Type: ${widget.patientAccessList[index].type}\n";
String line3 = "Status: ";
String access = widget.patientAccessList[index].status.toUpperCase();
TextSpan accessWithColour;
if (access == "APPROVED") {
accessWithColour = TextSpan(
text: "$access\n",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.successColor()));
} else if (access == "PENDING") {
accessWithColour = TextSpan(
text: "$access\n",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.messageTextColor()));
} else {
accessWithColour = TextSpan(
text: "$access\n",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor()));
}
return ListTile(
title: Text(
line1,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
subtitle: RichText(
text: TextSpan(
text: line2,
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(text: line3),
accessWithColour,
]),
),
// Text(
// subtitle,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// ),
// ),
onTap: () {
viewApprovalPopUp(index);
},
// trailing: Icon(
// Icons.arrow_forward,
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// ),
);
}
void checkScreenSize() {
if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") {
setState(() {
popUpWidth = (width / 4) * 2;
popUpheight = null;
popUpButtonWidth = 300;
popUpTitleSize = 25.0;
popUpSubtitleSize = 20.0;
popUpBodySize = 15;
popUpPaddingSize = 25.0;
popUpIconSize = 100;
});
} else {
setState(() {
popUpWidth = width - (width * 0.1);
popUpheight = null;
popUpButtonWidth = 300;
popUpTitleSize = 20.0;
popUpSubtitleSize = 18.0;
popUpBodySize = 15;
popUpPaddingSize = 15.0;
popUpIconSize = 100;
});
}
}
void viewApprovalPopUp(int index) {
String subtitle =
"Business Name: ${widget.patientAccessList[index].requested_by}\n";
subtitle +=
"Requested Date: ${widget.patientAccessList[index].requested_on.substring(0, 16).replaceAll("T", " ")}\n";
subtitle +=
"Profile Type: ${widget.patientAccessList[index].type.toUpperCase()}\n";
subtitle +=
"Status: ${widget.patientAccessList[index].status.toUpperCase()}";
if (widget.patientAccessList[index].status == 'pending') {
// "\nYou are about to approve an access request to your patient profile.\nPlease be aware that once approved, ${widget.patientAccessList[index].requested_by} will have access to your profile forever and will be able to contribute to it.\nIf you are unsure about an upcoming appointment with ${widget.patientAccessList[index].requested_by}, please contact *Add Number here* for clarification before approving this request.";
} else {
subtitle +=
"\nActioned By: ${widget.patientAccessList[index].approved_by}\n";
subtitle +=
"Actioned On: ${widget.patientAccessList[index].approved_on.substring(0, 16).replaceAll("T", " ")}";
// subtitle +=
// "You have approved this access request to your patient profile.\nPlease be aware that once approved, ${widget.patientAccessList[index].requested_by} will have access to your profile forever and will be able to contribute to it.";
}
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Profile Access",
windowBody: [
const SizedBox(
height: 10,
),
SizedBox(
width: 1000,
child: Text(
subtitle,
textAlign: TextAlign.left,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
//fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 20.0),
Visibility(
visible: widget.patientAccessList[index].status == 'pending',
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Important Notice: Approving Profile Access",
style: TextStyle(
fontWeight: FontWeight.bold,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
Text(
"You are about to accept access to your patient's profile. Please be aware of the following important points:",
style: TextStyle(
fontWeight: FontWeight.normal,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
SizedBox(
width: 700,
child: Text(
"1. Permanent Access: Once you accepts this access request, it will become permanent.",
style: TextStyle(
fontWeight: FontWeight.normal,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
),
SizedBox(
width: 700,
child: Text(
"2. Shared Information: Any updates make to youe patient profile will be visible to all who have access to the profile.",
style: TextStyle(
fontWeight: FontWeight.normal,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
),
SizedBox(
width: 700,
child: Text(
"3. Irreversible Access: Once granted, you cannot revoke access to your patient's profile.",
style: TextStyle(
fontWeight: FontWeight.normal,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
),
Text(
"By pressing the \"Approve\" button you accept the above terms.",
style: TextStyle(
fontWeight: FontWeight.bold,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
],
),
),
Visibility(
visible: widget.patientAccessList[index].status == 'approved',
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Important Notice: Approved Profile Access",
style: TextStyle(
fontWeight: FontWeight.bold,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
Text(
"You have accepted access to your patient's profile. Please be aware of the following important points:",
style: TextStyle(
fontWeight: FontWeight.normal,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
SizedBox(
width: 700,
child: Text(
"1. Permanent Access: This access is permanent.",
style: TextStyle(
fontWeight: FontWeight.normal,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
),
SizedBox(
width: 700,
child: Text(
"2. Shared Information: Any updates make to youe patient profile will be visible to all who have access to the profile.",
style: TextStyle(
fontWeight: FontWeight.normal,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
),
SizedBox(
width: 700,
child: Text(
"3. Irreversible Access: You cannot revoke this access to your patient's profile.",
style: TextStyle(
fontWeight: FontWeight.normal,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
),
],
),
),
const SizedBox(height: 20.0),
const SizedBox(
height: 20,
),
Visibility(
visible: widget.patientAccessList[index].status == 'pending',
child: Wrap(
runSpacing: 10,
spacing: 10,
children: [
SizedBox(
width: popUpButtonWidth,
height: 50,
child: MIHButton(
onTap: () {
print("request declined");
MIHApiCalls.updatePatientAccessAPICall(
widget.patientAccessList[index].business_id,
widget.patientAccessList[index].requested_by,
widget.patientAccessList[index].app_id,
"declined",
"${widget.signedInUser.fname} ${widget.signedInUser.lname}",
widget.signedInUser,
context,
);
//updateAccessAPICall(index, "declined");
},
buttonText: "Decline",
buttonColor:
MzanziInnovationHub.of(context)!.theme.errorColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
SizedBox(
width: popUpButtonWidth,
height: 50,
child: MIHButton(
onTap: () {
print("request approved");
MIHApiCalls.updatePatientAccessAPICall(
widget.patientAccessList[index].business_id,
widget.patientAccessList[index].requested_by,
widget.patientAccessList[index].app_id,
"approved",
"${widget.signedInUser.fname} ${widget.signedInUser.lname}",
widget.signedInUser,
context,
);
//updateAccessAPICall(index, "approved");
},
buttonText: "Approve",
buttonColor:
MzanziInnovationHub.of(context)!.theme.successColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
],
),
),
const SizedBox(
height: 10,
),
],
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
}),
);
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
setState(() {
width = size.width;
height = size.height;
});
checkScreenSize();
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, index) {
return Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
);
},
itemCount: widget.patientAccessList.length,
itemBuilder: (context, index) {
//final patient = widget.patients[index].id_no.contains(widget.searchString);
//print(index);
return displayQueue(index);
},
);
}
}

View File

@@ -0,0 +1,248 @@
import 'package:flutter/material.dart';
import '../../main.dart';
import '../../mih_apis/mih_api_calls.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/patient_access.dart';
import 'builder/build_business_access_list.dart';
class PatientAccessRequest extends StatefulWidget {
final AppUser signedInUser;
const PatientAccessRequest({
super.key,
required this.signedInUser,
});
@override
State<PatientAccessRequest> createState() => _PatientAccessRequestState();
}
class _PatientAccessRequestState extends State<PatientAccessRequest> {
TextEditingController filterController = TextEditingController();
String baseUrl = AppEnviroment.baseApiUrl;
String errorCode = "";
String errorBody = "";
String datefilter = "";
String accessFilter = "";
bool forceRefresh = false;
late String selectedDropdown;
late Future<List<PatientAccess>> accessList;
// Future<List<AccessRequest>> fetchAccessRequests() async {
// //print("Patien manager page: $endpoint");
// final response = await http.get(
// Uri.parse("$baseUrl/access-requests/${widget.signedInUser.app_id}"));
// // print("Here");
// // print("Body: ${response.body}");
// // print("Code: ${response.statusCode}");
// errorCode = response.statusCode.toString();
// errorBody = response.body;
// if (response.statusCode == 200) {
// //print("Here1");
// Iterable l = jsonDecode(response.body);
// //print("Here2");
// List<AccessRequest> patientQueue = List<AccessRequest>.from(
// l.map((model) => AccessRequest.fromJson(model)));
// //print("Here3");
// //print(patientQueue);
// return patientQueue;
// } else {
// throw Exception('failed to load patients');
// }
// }
List<PatientAccess> filterSearchResults(List<PatientAccess> accessList) {
List<PatientAccess> templist = [];
for (var item in accessList) {
if (filterController.text == "All") {
templist.add(item);
} else {
if (item.status.contains(filterController.text.toLowerCase())) {
templist.add(item);
}
}
}
return templist;
}
void refreshList() {
if (forceRefresh == true) {
setState(() {
accessList = MIHApiCalls.getBusinessAccessListOfPatient(
widget.signedInUser.app_id);
forceRefresh = false;
});
} else if (selectedDropdown != filterController.text) {
setState(() {
accessList = MIHApiCalls.getBusinessAccessListOfPatient(
widget.signedInUser.app_id);
selectedDropdown = filterController.text;
});
}
// setState(() {
// accessRequestResults = fetchAccessRequests();
// });
}
MIHAction getActionButton() {
return MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).popAndPushNamed(
'/',
arguments: true,
);
},
);
}
MIHHeader getHeader() {
return const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"Forever Access List",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: true,
bodyItems: [
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: MIHDropdownField(
controller: filterController,
hintText: "Access Types",
dropdownOptions: const [
"All",
"Approved",
"Pending",
"Declined",
"Cancelled"
],
required: true,
editable: true,
),
),
IconButton(
onPressed: () {
setState(() {
forceRefresh = true;
});
refreshList();
},
icon: const Icon(
Icons.refresh,
),
),
],
),
const SizedBox(height: 10),
FutureBuilder(
future: accessList,
builder: (context, snapshot) {
//print("patient Queue List ${snapshot.hasData}");
if (snapshot.connectionState == ConnectionState.waiting) {
return const Mihloadingcircle();
} else if (snapshot.connectionState == ConnectionState.done) {
List<PatientAccess> accessRequestList;
accessRequestList = filterSearchResults(snapshot.requireData);
if (accessRequestList.isNotEmpty) {
return BuildBusinessAccessList(
signedInUser: widget.signedInUser,
patientAccessList: accessRequestList,
);
} else {
return Center(
child: Text(
"No Request have been made.",
style: TextStyle(
fontSize: 25,
color: MzanziInnovationHub.of(context)!
.theme
.messageTextColor()),
textAlign: TextAlign.center,
),
);
}
// return Expanded(
// child: displayAccessRequestList(accessRequestList),
// );
} else {
return Center(
child: Text(
"$errorCode: Error pulling Patients Data\n$baseUrl/queue/patients/\n$errorBody",
style: TextStyle(
fontSize: 25,
color:
MzanziInnovationHub.of(context)!.theme.errorColor()),
textAlign: TextAlign.center,
),
);
}
},
),
],
);
}
@override
void dispose() {
filterController.dispose();
super.dispose();
}
@override
void initState() {
selectedDropdown = "All";
filterController.text = "All";
filterController.addListener(refreshList);
setState(() {
accessList = MIHApiCalls.getBusinessAccessListOfPatient(
widget.signedInUser.app_id);
});
super.initState();
}
@override
Widget build(BuildContext context) {
return MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(),
secondaryActionButton: null,
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
}
}

View File

@@ -0,0 +1,380 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../mih_apis/mih_api_calls.dart';
import '../../mih_components/mih_calendar.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/access_request.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/patient_queue.dart';
import '../access_review/builder/build_access_request_list.dart';
import 'builder/build_appointment_list.dart';
class Appointments extends StatefulWidget {
final AppUser signedInUser;
const Appointments({
super.key,
required this.signedInUser,
});
@override
State<Appointments> createState() => _PatientAccessRequestState();
}
class _PatientAccessRequestState extends State<Appointments> {
TextEditingController filterController = TextEditingController();
TextEditingController appointmentDateController = TextEditingController();
String baseUrl = AppEnviroment.baseApiUrl;
String errorCode = "";
String errorBody = "";
String datefilter = "";
String accessFilter = "";
bool forceRefresh = false;
late String selectedDropdown;
String selectedDay = DateTime.now().toString().split(" ")[0];
late Future<List<AccessRequest>> accessRequestResults;
late Future<List<PatientQueue>> personalQueueResults;
Future<List<AccessRequest>> fetchAccessRequests() async {
//print("Patien manager page: $endpoint");
final response = await http.get(
Uri.parse("$baseUrl/access-requests/${widget.signedInUser.app_id}"));
// print("Here");
// print("Body: ${response.body}");
// print("Code: ${response.statusCode}");
errorCode = response.statusCode.toString();
errorBody = response.body;
if (response.statusCode == 200) {
//print("Here1");
Iterable l = jsonDecode(response.body);
//print("Here2");
List<AccessRequest> patientQueue = List<AccessRequest>.from(
l.map((model) => AccessRequest.fromJson(model)));
//print("Here3");
//print(patientQueue);
return patientQueue;
} else {
throw Exception('failed to load patients');
}
}
List<AccessRequest> filterSearchResults(List<AccessRequest> accessList) {
List<AccessRequest> templist = [];
for (var item in accessList) {
if (filterController.text == "All") {
if (item.date_time.contains(datefilter)) {
templist.add(item);
}
} else {
if (item.date_time.contains(datefilter) &&
item.access.contains(filterController.text.toLowerCase())) {
templist.add(item);
}
}
}
return templist;
}
Widget displayAccessRequestList(List<AccessRequest> accessRequestList) {
if (accessRequestList.isNotEmpty) {
return BuildAccessRequestList(
signedInUser: widget.signedInUser,
accessRequests: accessRequestList,
// BuildPatientQueueList(
// patientQueue: patientQueueList,
// signedInUser: widget.signedInUser,
// ),
);
} else {
return Center(
child: Text(
"No Request have been made.",
style: TextStyle(
fontSize: 25,
color: MzanziInnovationHub.of(context)!.theme.messageTextColor()),
textAlign: TextAlign.center,
),
);
}
}
Widget viewAccessRequest(double w, double h) {
return Padding(
padding: const EdgeInsets.all(15.0),
child: SizedBox(
width: w,
height: h,
child: Column(mainAxisSize: MainAxisSize.max, children: [
//const SizedBox(height: 15),
const Text(
"Access Request",
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 25),
SizedBox(
width: 500,
child: MIHDropdownField(
controller: filterController,
hintText: "Access Types",
dropdownOptions: const ["All", "Approved", "Pending", "Declined"],
required: true,
editable: true,
),
),
const SizedBox(height: 10),
FutureBuilder(
future: accessRequestResults,
builder: (context, snapshot) {
//print("patient Queue List ${snapshot.hasData}");
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) {
List<AccessRequest> accessRequestList;
accessRequestList = filterSearchResults(snapshot.requireData);
if (accessRequestList.isNotEmpty) {
return BuildAccessRequestList(
signedInUser: widget.signedInUser,
accessRequests: accessRequestList,
);
} else {
return Center(
child: Text(
"No Request have been made.",
style: TextStyle(
fontSize: 25,
color: MzanziInnovationHub.of(context)!
.theme
.messageTextColor()),
textAlign: TextAlign.center,
),
);
}
// return Expanded(
// child: displayAccessRequestList(accessRequestList),
// );
} else {
return Center(
child: Text(
"$errorCode: Error pulling Patients Data\n$baseUrl/queue/patients/\n$errorBody",
style: TextStyle(
fontSize: 25,
color: MzanziInnovationHub.of(context)!
.theme
.errorColor()),
textAlign: TextAlign.center,
),
);
}
},
),
]),
),
);
}
void refreshList() {
if (forceRefresh == true) {
setState(() {
accessRequestResults = fetchAccessRequests();
forceRefresh = false;
});
} else if (selectedDropdown != filterController.text) {
setState(() {
accessRequestResults = fetchAccessRequests();
selectedDropdown = filterController.text;
});
}
// setState(() {
// accessRequestResults = fetchAccessRequests();
// });
}
Widget displayQueueList(List<PatientQueue> patientQueueList) {
if (patientQueueList.isNotEmpty) {
return Expanded(
child: BuildAppointmentList(
patientQueue: patientQueueList,
signedInUser: widget.signedInUser,
),
);
}
return Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 35.0),
child: Center(
child: Text(
"No Appointments for $selectedDay",
style: TextStyle(
fontSize: 25,
color: MzanziInnovationHub.of(context)!.theme.messageTextColor(),
),
textAlign: TextAlign.center,
softWrap: true,
),
),
),
);
}
void checkforchange() {
setState(() {
personalQueueResults = MIHApiCalls.fetchPersonalAppointmentsAPICall(
selectedDay,
widget.signedInUser.app_id,
);
});
}
MIHAction getActionButton() {
return MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).popAndPushNamed(
'/',
arguments: true,
);
},
);
}
MIHHeader getHeader() {
return const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"Appointments",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: true,
bodyItems: [
MIHCalendar(
calendarWidth: 500,
rowHeight: 35,
setDate: (value) {
setState(() {
selectedDay = value;
appointmentDateController.text = selectedDay;
});
}),
Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
FutureBuilder(
future: personalQueueResults,
builder: (context, snapshot) {
//return displayQueueList(snapshot.requireData);
if (snapshot.connectionState == ConnectionState.waiting) {
return const Expanded(
child: Center(child: Mihloadingcircle()));
} else if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
return displayQueueList(snapshot.requireData);
} else {
return Center(
child: Text(
"Error pulling appointments",
style: TextStyle(
fontSize: 25,
color: MzanziInnovationHub.of(context)!
.theme
.errorColor()),
textAlign: TextAlign.center,
),
);
}
}),
],
)
],
);
}
@override
void dispose() {
filterController.dispose();
appointmentDateController.dispose();
super.dispose();
}
@override
void initState() {
// selectedDropdown = "All";
// filterController.text = "All";
// filterController.addListener(refreshList);
// setState(() {
// accessRequestResults = fetchAccessRequests();
// });
appointmentDateController.addListener(checkforchange);
setState(() {
personalQueueResults = MIHApiCalls.fetchPersonalAppointmentsAPICall(
selectedDay,
widget.signedInUser.app_id,
);
});
super.initState();
}
@override
Widget build(BuildContext context) {
return MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(),
secondaryActionButton: null,
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
}
}

View File

@@ -0,0 +1,429 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../../main.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../../mih_components/mih_layout/mih_window.dart';
import '../../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_warning_message.dart';
import '../../../mih_env/env.dart';
import '../../../mih_objects/access_request.dart';
import '../../../mih_objects/app_user.dart';
class BuildAccessRequestList extends StatefulWidget {
final List<AccessRequest> accessRequests;
final AppUser signedInUser;
const BuildAccessRequestList({
super.key,
required this.accessRequests,
required this.signedInUser,
});
@override
State<BuildAccessRequestList> createState() => _BuildPatientsListState();
}
class _BuildPatientsListState extends State<BuildAccessRequestList> {
String baseAPI = AppEnviroment.baseApiUrl;
late double popUpWidth;
late double? popUpheight;
late double popUpButtonWidth;
late double popUpTitleSize;
late double popUpSubtitleSize;
late double popUpBodySize;
late double popUpIconSize;
late double popUpPaddingSize;
late double width;
late double height;
Future<void> updateAccessAPICall(int index, String accessType) async {
var response = await http.put(
Uri.parse("$baseAPI/access-requests/update/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": widget.accessRequests[index].business_id,
"app_id": widget.accessRequests[index].app_id,
"date_time": widget.accessRequests[index].date_time,
"access": accessType,
}),
);
if (response.statusCode == 200) {
//Navigator.of(context).pushNamed('/home');
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed(
'/patient-access-review',
arguments: widget.signedInUser,
);
String message = "";
if (accessType == "approved") {
message =
"You've successfully approved the access request! ${widget.accessRequests[index].Name} now has access to your profile until ${widget.accessRequests[index].revoke_date.substring(0, 16).replaceAll("T", " ")}.";
} else {
message =
"You've declined the access request. ${widget.accessRequests[index].Name} will not have access to your profile.";
}
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,
);
},
);
}
void accessCancelledWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "Access Cancelled");
},
);
}
Widget displayQueue(int index) {
String line1 =
"Appointment: ${widget.accessRequests[index].date_time.substring(0, 16).replaceAll("T", " ")}";
String line2 = "";
line2 += "Requestor: ${widget.accessRequests[index].Name}\n";
//subtitle += "Business Type: ${widget.accessRequests[index].type}\n";
String line3 = "Access: ";
String access = "";
var nowDate = DateTime.now();
var expireyDate = DateTime.parse(widget.accessRequests[index].revoke_date);
if (expireyDate.isBefore(nowDate)) {
access += "EXPIRED";
} else {
access += "${widget.accessRequests[index].access.toUpperCase()}";
}
String line4 = "";
if (widget.accessRequests[index].revoke_date.contains("9999")) {
line4 += "Access Expiration date: NOT SET";
} else {
line4 +=
"Access Expiration date: ${widget.accessRequests[index].revoke_date.substring(0, 10)}";
}
TextSpan accessWithColour;
if (access == "APPROVED") {
accessWithColour = TextSpan(
text: "$access\n",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.successColor()));
} else if (access == "PENDING") {
accessWithColour = TextSpan(
text: "$access\n",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.messageTextColor()));
} else {
accessWithColour = TextSpan(
text: "$access\n",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor()));
}
return ListTile(
title: Text(
line1,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
subtitle: RichText(
text: TextSpan(
text: line2,
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
TextSpan(text: line3),
accessWithColour,
TextSpan(text: line4),
]),
),
// Text(
// subtitle,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// ),
// ),
onTap: () {
if (access == "CANCELLED") {
accessCancelledWarning();
} else {
viewApprovalPopUp(index);
}
},
// trailing: Icon(
// Icons.arrow_forward,
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// ),
);
}
void checkScreenSize() {
if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") {
setState(() {
popUpWidth = (width / 4) * 2;
popUpheight = null;
popUpButtonWidth = 300;
popUpTitleSize = 25.0;
popUpSubtitleSize = 20.0;
popUpBodySize = 15;
popUpPaddingSize = 25.0;
popUpIconSize = 100;
});
} else {
setState(() {
popUpWidth = width - (width * 0.1);
popUpheight = null;
popUpButtonWidth = 300;
popUpTitleSize = 20.0;
popUpSubtitleSize = 18.0;
popUpBodySize = 15;
popUpPaddingSize = 15.0;
popUpIconSize = 100;
});
}
}
void viewApprovalPopUp(int index) {
String subtitle =
"Appointment: ${widget.accessRequests[index].date_time.substring(0, 16).replaceAll("T", " ")}\n";
subtitle += "Requestor: ${widget.accessRequests[index].Name}\n";
subtitle += "Business Type: ${widget.accessRequests[index].type}\n\n";
subtitle +=
"You are about to approve an access request to your patient profile.\nPlease be aware that once approved, ${widget.accessRequests[index].Name} will have access to your profile information until ${widget.accessRequests[index].revoke_date.substring(0, 16).replaceAll("T", " ")}.\nIf you are unsure about an upcoming appointment with ${widget.accessRequests[index].Name}, please contact ${widget.accessRequests[index].contact_no} for clarification before approving this request.\n";
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Update Appointment Access",
windowBody: [
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Text(
subtitle,
textAlign: TextAlign.left,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: popUpBodySize,
//fontWeight: FontWeight.bold,
),
),
),
Wrap(
runSpacing: 10,
spacing: 10,
children: [
SizedBox(
width: popUpButtonWidth,
height: 50,
child: MIHButton(
onTap: () {
updateAccessAPICall(index, "declined");
},
buttonText: "Decline",
buttonColor:
MzanziInnovationHub.of(context)!.theme.errorColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
SizedBox(
width: popUpButtonWidth,
height: 50,
child: MIHButton(
onTap: () {
updateAccessAPICall(index, "approved");
},
buttonText: "Approve",
buttonColor:
MzanziInnovationHub.of(context)!.theme.successColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
],
),
const SizedBox(
height: 10,
),
],
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
}),
);
// showDialog(
// context: context,
// barrierDismissible: false,
// builder: (context) => Dialog(
// child: Stack(
// children: [
// Container(
// //padding: const EdgeInsets.all(15.0),
// width: popUpWidth,
// height: popUpheight,
// 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: SingleChildScrollView(
// padding: EdgeInsets.all(popUpPaddingSize),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// mainAxisAlignment: MainAxisAlignment.center,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// Text(
// "Update Appointment Access",
// textAlign: TextAlign.center,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// fontSize: popUpTitleSize,
// fontWeight: FontWeight.bold,
// ),
// ),
// const SizedBox(height: 15.0),
// Text(
// subtitle,
// textAlign: TextAlign.left,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// fontSize: popUpBodySize,
// //fontWeight: FontWeight.bold,
// ),
// ),
// const SizedBox(height: 10.0),
// Wrap(
// runSpacing: 10,
// spacing: 10,
// children: [
// SizedBox(
// width: popUpButtonWidth,
// height: 50,
// child: MIHButton(
// onTap: () {
// updateAccessAPICall(index, "declined");
// },
// buttonText: "Decline",
// buttonColor: MzanziInnovationHub.of(context)!
// .theme
// .errorColor(),
// textColor: MzanziInnovationHub.of(context)!
// .theme
// .primaryColor(),
// ),
// ),
// SizedBox(
// width: popUpButtonWidth,
// height: 50,
// child: MIHButton(
// onTap: () {
// updateAccessAPICall(index, "approved");
// },
// buttonText: "Approve",
// buttonColor: MzanziInnovationHub.of(context)!
// .theme
// .successColor(),
// textColor: MzanziInnovationHub.of(context)!
// .theme
// .primaryColor(),
// ),
// ),
// ],
// )
// ],
// ),
// ),
// ),
// 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() {
super.dispose();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
setState(() {
width = size.width;
height = size.height;
});
checkScreenSize();
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, index) {
return Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
);
},
itemCount: widget.accessRequests.length,
itemBuilder: (context, index) {
//final patient = widget.patients[index].id_no.contains(widget.searchString);
//print(index);
return displayQueue(index);
},
);
}
}

View File

@@ -0,0 +1,136 @@
import 'package:flutter/material.dart';
import '../../../main.dart';
import '../../../mih_components/mih_pop_up_messages/mih_warning_message.dart';
import '../../../mih_env/env.dart';
import '../../../mih_objects/app_user.dart';
import '../../../mih_objects/patient_queue.dart';
class BuildAppointmentList extends StatefulWidget {
final List<PatientQueue> patientQueue;
final AppUser signedInUser;
const BuildAppointmentList({
super.key,
required this.patientQueue,
required this.signedInUser,
});
@override
State<BuildAppointmentList> createState() => _BuildPatientsListState();
}
class _BuildPatientsListState extends State<BuildAppointmentList> {
String baseAPI = AppEnviroment.baseApiUrl;
TextEditingController dateController = TextEditingController();
TextEditingController timeController = TextEditingController();
TextEditingController idController = TextEditingController();
TextEditingController fnameController = TextEditingController();
TextEditingController lnameController = TextEditingController();
TextEditingController daysExtensionController = TextEditingController();
int counter = 0;
Widget displayQueue(int index) {
String title = widget.patientQueue[index].business_name.toUpperCase();
// widget.patientQueue[index].date_time.split('T')[1].substring(0, 5);
String line234 = "";
// var nowDate = DateTime.now();
// var expireyDate = DateTime.parse(widget.patientQueue[index].revoke_date);
line234 +=
widget.patientQueue[index].date_time.split('T')[1].substring(0, 5);
return ListTile(
title: Text(
title,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
subtitle: RichText(
text: TextSpan(
text: "Time: $line234",
style: DefaultTextStyle.of(context).style,
// children: [
// TextSpan(text: line5),
// accessWithColour,
// TextSpan(text: line6),
// ]
),
),
onTap: () {},
);
}
bool isAccessExpired(String accessType) {
if (accessType == "EXPIRED") {
return true;
} else {
return false;
}
}
void noAccessWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "No Access");
},
);
}
void accessDeclinedWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "Access Declined");
},
);
}
void appointmentCancelledWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "Appointment Canelled");
},
);
}
void expiredAccessWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "Expired Access");
},
);
}
@override
void dispose() {
daysExtensionController.dispose();
dateController.dispose();
timeController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
separatorBuilder: (BuildContext context, index) {
return Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
);
},
itemCount: widget.patientQueue.length,
itemBuilder: (context, index) {
//final patient = widget.patients[index].id_no.contains(widget.searchString);
//print(index);
return displayQueue(index);
},
);
}
}

View File

@@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:supertokens_flutter/supertokens.dart';
import '../mih_home/mih_profile_getter.dart';
import 'signin_or_register.dart';
class AuthCheck extends StatefulWidget {
final bool personalSelected;
const AuthCheck({
super.key,
required this.personalSelected,
});
@override
State<AuthCheck> createState() => _AuthCheckState();
}
class _AuthCheckState extends State<AuthCheck> {
Future<bool> doesSessionExist() async {
//wait
//await Future.delayed(const Duration(seconds: 1));
bool signedIn = await SuperTokens.doesSessionExist();
return signedIn;
}
@override
void initState() {
//signedIn = doesSessionExist();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: OrientationBuilder(
builder: (BuildContext context, Orientation orientation) {
// Return a widget tree based on the orientation
return FutureBuilder(
future: doesSessionExist(),
builder: (context, snapshot) {
//print(snapshot.data);
if (snapshot.data == true) {
return MIHProfileGetter(
personalSelected: widget.personalSelected,
);
} else if (snapshot.data == false) {
return const SignInOrRegister();
} else {
return const SizedBox(width: 5, height: 5);
//const Mihloadingcircle();
}
});
},
// child: FutureBuilder(
// future: signedIn,
// builder: (context, snapshot) {
// if (snapshot.data == true) {
// return const MIHProfileGetter();
// } else {
// return const SignInOrRegister();
// }
// }),
),
);
}
}

View File

@@ -0,0 +1,367 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../main.dart';
import 'package:supabase_auth_ui/supabase_auth_ui.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_env/env.dart';
class ForgotPassword extends StatefulWidget {
const ForgotPassword({super.key});
@override
State<ForgotPassword> createState() => _ForgotPasswordState();
}
class _ForgotPasswordState extends State<ForgotPassword> {
final emailController = TextEditingController();
//bool _obscureText = true;
bool successfulForgotPassword = false;
bool acceptWarning = false;
// focus node to capture keyboard events
final FocusNode _focusNode = FocusNode();
final baseAPI = AppEnviroment.baseApiUrl;
Future<void> submitPasswodReset() async {
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
try {
var response = await http.post(
Uri.parse("$baseAPI/auth/user/password/reset/token"),
body:
'{"formFields": [{"id": "email","value": "${emailController.text}"}]}',
headers: {
'Content-type': 'application/json',
'Accept': 'application/json',
//"Authorization": "leatucczyixqwkqqdrhayiwzeofkltds"
},
);
//print(response.body[])
if (response.statusCode == 200) {
//print(response.body);
var userSignedin = jsonDecode(response.body);
if (userSignedin["status"] == "OK") {
//print("here");
setState(() {
successfulForgotPassword = true;
});
Navigator.of(context).pop();
} else {
Navigator.of(context).pop();
//loginError();
}
}
} on AuthException {
Navigator.of(context).pop();
//loginError();
}
}
Color getPrim() {
return MzanziInnovationHub.of(context)!.theme.secondaryColor();
}
Color getSec() {
return MzanziInnovationHub.of(context)!.theme.primaryColor();
}
void prePassResteWarning() {
showDialog(
context: context,
builder: (context) {
return Dialog(
child: Stack(
children: [
Container(
padding: const EdgeInsets.all(10.0),
width: 500.0,
height: 450,
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: SingleChildScrollView(
child: Column(
//mainAxisSize: MainAxisSize.max,
children: [
Icon(
Icons.warning_amber_rounded,
size: 100,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
const SizedBox(height: 15),
Text(
" Password Reset Confirmation",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: 25.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Text(
"Before you reset your password, please be aware that you'll receive an email with a link to confirm your identity and set a new password. Make sure to check your inbox, including spam or junk folders. If you don't receive the email within a few minutes, please try resending the reset request.",
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 25),
SizedBox(
width: 500.0,
height: 50.0,
child: MIHButton(
buttonText: "Continue",
buttonColor: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!
.theme
.primaryColor(),
onTap: () {
setState(() {
acceptWarning = true;
});
Navigator.of(context).pop();
validateInput();
},
),
),
],
),
),
),
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,
),
),
),
],
),
);
},
);
}
void loginError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Invalid Credentials");
},
);
}
void resetLinkSentSuccessfully() {
showDialog(
context: context,
builder: (context) {
return const MIHSuccessMessage(
successType: "Success",
successMessage:
"We've sent a password reset link to your email address. Please check your inbox, including spam or junk folders.\n\nOnce you find the email, click on the link to reset your password.\n\nIf you don't receive the email within a few minutes, please try resending the reset request.\n\nThe reset link will expire after 2 hours");
},
);
}
void validateInput() async {
if (emailController.text.isEmpty) {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
} else {
await submitPasswodReset();
if (successfulForgotPassword) {
Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false);
resetLinkSentSuccessfully();
}
}
}
MIHAction getActionButton() {
return MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pop();
},
);
}
MIHHeader getHeader() {
return const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: false,
bodyItems: [
KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
validateInput();
}
},
child: SafeArea(
child: Center(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(25.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//logo
Icon(
Icons.lock,
size: 100,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
//spacer
const SizedBox(height: 10),
//Heading
Text(
'Forgot Password',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
),
//spacer
const SizedBox(height: 25),
//email input
SizedBox(
width: 500.0,
child: MIHTextField(
controller: emailController,
hintText: 'Email',
editable: true,
required: true,
),
),
//spacer
const SizedBox(height: 30),
// sign in button
SizedBox(
width: 500.0,
height: 50.0,
child: MIHButton(
buttonText: "Reset Password",
buttonColor: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!
.theme
.primaryColor(),
onTap: () {
prePassResteWarning();
},
),
),
],
),
),
),
),
),
),
],
);
}
@override
void dispose() {
emailController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(),
secondaryActionButton: null,
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
}
}

View File

@@ -0,0 +1,429 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../main.dart';
//import '../objects/sessionST.dart';
import 'package:supabase_auth_ui/supabase_auth_ui.dart';
//import 'package:supertokens_flutter/supertokens.dart';
import 'package:supertokens_flutter/http.dart' as http;
import 'package:supertokens_flutter/supertokens.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_pass_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_env/env.dart';
class Register extends StatefulWidget {
final Function()? onTap;
const Register({super.key, required this.onTap});
@override
State<Register> createState() => _RegisterState();
}
class _RegisterState extends State<Register> {
final emailController = TextEditingController();
final passwordController = TextEditingController();
final confirmPasswordController = TextEditingController();
final officeID = TextEditingController();
final baseAPI = AppEnviroment.baseApiUrl;
final FocusNode _focusNode = FocusNode();
bool _obscureText = true;
bool successfulSignUp = false;
Future<void> addUserAPICall(String email, String uid) async {
//await getOfficeIdByUser(docOfficeIdApiUrl + widget.userEmail);
//print(futureDocOfficeId.toString());
var response = await http.post(
Uri.parse("$baseAPI/user/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"email": email,
"app_id": uid,
}),
);
if (response.statusCode == 201) {
Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false);
signUpSuccess();
// setState(() {
// successfulSignUp = true;
// });
} else {
internetConnectionPopUp();
}
}
//sign user in
Future<void> signUserUp() async {
if (!validEmail()) {
emailError();
} else if (passwordController.text != confirmPasswordController.text) {
passwordError();
} else {
//var _backgroundColor = Colors.transparent;
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
try {
Uri uri = Uri.parse(
"$baseAPI/auth/emailpassword/email/exists?email=${emailController.text}");
//print("Here");
var response = await http.get(uri);
//print(response.body);
//print("response 1: ${response.statusCode}");
if (response.statusCode == 200) {
var userExists = jsonDecode(response.body);
if (userExists["exists"]) {
signUpError();
} else {
var response2 = await http.post(
Uri.parse("$baseAPI/auth/signup"),
body:
'{"formFields": [{"id": "email","value": "${emailController.text}"}, {"id": "password","value": "${passwordController.text}"}]}',
headers: {
'Content-type': 'application/json',
'Accept': 'application/json',
"Authorization": "leatucczyixqwkqqdrhayiwzeofkltds"
},
);
//print("response 2: ${response2.statusCode}");
if (response2.statusCode == 200) {
//print("response 2: ${response2.body}");
var userCreated = jsonDecode(response2.body);
//print("Created user $userCreated");
if (userCreated["status"] == "OK") {
//print("Here1");
//Creat user in db
String uid = await SuperTokens.getUserId();
//print("uid: $uid");
addUserAPICall(emailController.text, uid);
Navigator.of(context).pop();
//print("Here1");
} else if (userCreated["status"] == "FIELD_ERROR") {
Navigator.of(context).pop();
passwordReqError();
} else {
Navigator.of(context).pop();
internetConnectionPopUp();
}
}
}
}
} on AuthException catch (error) {
Navigator.of(context).pop();
loginError(error.message);
emailController.clear();
passwordController.clear();
confirmPasswordController.clear();
}
}
}
bool validEmail() {
String text = emailController.text;
var regex = RegExp(r'^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$');
return regex.hasMatch(text);
}
void internetConnectionPopUp() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Internet Connection");
},
);
}
void signUpSuccess() {
showDialog(
context: context,
builder: (context) {
return const MIHSuccessMessage(
successType: "Success",
successMessage:
"Congratulations! Your account has been created successfully. You are logged in and can start exploring.\n\nPlease note: more apps will appear once you update your profile.");
},
);
}
void signUpError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "User Exists");
},
);
}
void passwordError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Password Match");
},
);
}
void emailError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Invalid Email");
},
);
}
void passwordReqError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Password Requirements");
},
);
}
void loginError(error) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(error),
);
},
);
}
void validateInput() async {
if (emailController.text.isEmpty ||
passwordController.text.isEmpty ||
confirmPasswordController.text.isEmpty) {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
} else {
await signUserUp();
}
}
void toggle() {
setState(() {
_obscureText = !_obscureText;
});
}
MIHAction getActionButton() {
return MIHAction(
icon: Padding(
padding: const EdgeInsets.all(10.0),
child: SizedBox(
height: 50,
child: Image.asset('images/logo_light.png'),
),
),
iconSize: 35,
onTap: () {
Navigator.of(context).pushNamed(
'/about',
//arguments: widget.signedInUser,
);
},
);
}
MIHHeader getHeader() {
return const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: false,
bodyItems: [
KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
validateInput();
}
},
child: SafeArea(
child: Center(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(25.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//logo
Icon(
Icons.lock,
size: 100,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
//spacer
const SizedBox(height: 10),
//Heading
Text(
'Register',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
),
//spacer
const SizedBox(height: 25),
//email input
SizedBox(
width: 500.0,
child: MIHTextField(
controller: emailController,
hintText: 'Email',
editable: true,
required: true,
),
),
//spacer
const SizedBox(height: 10),
//password input
SizedBox(
width: 500.0,
child: MIHPassField(
controller: passwordController,
hintText: 'Password',
required: true,
signIn: false,
),
),
//spacer
const SizedBox(height: 10),
//password input
SizedBox(
width: 500.0,
child: MIHPassField(
controller: confirmPasswordController,
hintText: 'Confirm Password',
required: true,
signIn: false,
),
),
//spacer
const SizedBox(height: 30),
// sign up button
SizedBox(
width: 500.0,
height: 50.0,
child: MIHButton(
buttonText: "Sign Up",
buttonColor: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!
.theme
.primaryColor(),
onTap: () async {
validateInput();
},
),
),
const SizedBox(height: 10),
//register text
SizedBox(
width: 500.0,
//height: 100.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const Text(
'Already a User?',
style: TextStyle(fontSize: 18, color: Colors.grey),
),
const SizedBox(
width: 6,
),
GestureDetector(
onTap: widget.onTap,
child: Text(
'Sign In',
style: TextStyle(
fontSize: 18,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontWeight: FontWeight.bold,
),
),
)
],
),
)
],
),
),
),
)),
),
],
);
}
@override
void dispose() {
emailController.dispose();
passwordController.dispose();
officeID.dispose();
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(),
secondaryActionButton: null,
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
}
}

View File

@@ -0,0 +1,431 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../main.dart';
import 'package:supabase_auth_ui/supabase_auth_ui.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_pass_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_env/env.dart';
class ResetPassword extends StatefulWidget {
final String? token;
const ResetPassword({
super.key,
required this.token,
});
@override
State<ResetPassword> createState() => _ResetPasswordState();
}
class _ResetPasswordState extends State<ResetPassword> {
final passwordController = TextEditingController();
final confirmPasswordController = TextEditingController();
//bool _obscureText = true;
bool successfulResetPassword = false;
bool acceptWarning = false;
// focus node to capture keyboard events
final FocusNode _focusNode = FocusNode();
final baseAPI = AppEnviroment.baseApiUrl;
Future<void> submitPasswodReset() async {
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
try {
var callBody =
'{"method": "token","formFields": [{"id": "password","value": "${passwordController.text}"}],"token": "${widget.token}"}';
//print(callBody);
var response = await http.post(
Uri.parse("$baseAPI/auth/user/password/reset"),
body: callBody,
headers: {
'Content-type': 'application/json; charset=utf-8',
// 'Accept': 'application/json',
//"Authorization": "leatucczyixqwkqqdrhayiwzeofkltds"
},
);
//print(response.body[])
if (response.statusCode == 200) {
//print(response.body);
var userPassReset = jsonDecode(response.body);
if (userPassReset["status"] == "OK") {
//print("here");
setState(() {
successfulResetPassword = true;
});
Navigator.of(context).pop();
Navigator.of(context).pushNamed('/');
} else if (userPassReset["status"] == "FIELD_ERROR") {
Navigator.of(context).pop();
passwordReqError();
} else {
Navigator.of(context).pop();
loginError();
}
}
} on AuthException {
Navigator.of(context).pop();
//loginError();
}
}
void passwordReqError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Password Requirements");
},
);
}
Color getPrim() {
return MzanziInnovationHub.of(context)!.theme.secondaryColor();
}
Color getSec() {
return MzanziInnovationHub.of(context)!.theme.primaryColor();
}
void prePassResteWarning() {
showDialog(
context: context,
builder: (context) {
return Dialog(
child: Stack(
children: [
Container(
padding: const EdgeInsets.all(10.0),
width: 500.0,
height: 450,
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: SingleChildScrollView(
child: Column(
//mainAxisSize: MainAxisSize.max,
children: [
Icon(
Icons.warning_amber_rounded,
size: 100,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
const SizedBox(height: 15),
Text(
"Password Reset Confirmation",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: 25.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Text(
"Before you reset your password, please be aware that you'll receive an email with a link to confirm your identity and set a new password. Make sure to check your inbox, including spam or junk folders. If you don't receive the email within a few minutes, please try resending the reset request.",
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 25),
SizedBox(
width: 500.0,
height: 50.0,
child: MIHButton(
buttonText: "Continue",
buttonColor: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!
.theme
.primaryColor(),
onTap: () {
setState(() {
acceptWarning = true;
});
Navigator.of(context).pop();
validateInput();
},
),
),
],
),
),
),
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,
),
),
),
],
),
);
},
);
}
void loginError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Invalid Credentials");
},
);
}
void resetSuccessfully() {
showDialog(
context: context,
builder: (context) {
return const MIHSuccessMessage(
successType: "Success",
successMessage:
"Great news! Your password reset is complete. You can now log in to Mzansi Innovation Hub using your new password.");
},
);
}
void passwordError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Password Match");
},
);
}
void validateInput() async {
if (passwordController.text.isEmpty ||
confirmPasswordController.text.isEmpty) {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
} else if (passwordController.text != confirmPasswordController.text) {
passwordError();
} else {
await submitPasswodReset();
if (successfulResetPassword) {
resetSuccessfully();
}
}
}
MIHAction getActionButton() {
return MIHAction(
icon: Padding(
padding: const EdgeInsets.all(10.0),
child: SizedBox(
height: 50,
child: Image.asset('images/logo_light.png'),
),
),
iconSize: 35,
onTap: () {
// Navigator.of(context).pushNamed(
// '/about',
// //arguments: widget.signedInUser,
// );
},
);
}
MIHHeader getHeader() {
return const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: false,
bodyItems: [
KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
validateInput();
}
},
child: SafeArea(
child: Center(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(25.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//logo
Icon(
Icons.lock,
size: 100,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
//spacer
const SizedBox(height: 10),
//Heading
Text(
'Reset Password',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
),
//spacer
// const SizedBox(height: 15),
// Text(
// 'token: ${widget.token}',
// style: TextStyle(
// fontSize: 15,
// fontWeight: FontWeight.bold,
// color: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// ),
// ),
//spacer
const SizedBox(height: 25),
//email input
SizedBox(
width: 500.0,
child: MIHPassField(
controller: passwordController,
hintText: 'New Password',
required: true,
signIn: false,
),
),
//spacer
const SizedBox(height: 10),
//password input
SizedBox(
width: 500.0,
child: MIHPassField(
controller: confirmPasswordController,
hintText: 'Confirm New Password',
required: true,
signIn: false,
),
),
//spacer
const SizedBox(height: 30),
// sign in button
SizedBox(
width: 500.0,
height: 50.0,
child: MIHButton(
buttonText: "Reset Password",
buttonColor: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!
.theme
.primaryColor(),
onTap: () {
validateInput();
},
),
),
],
),
),
),
),
),
),
],
);
}
@override
void dispose() {
passwordController.dispose();
confirmPasswordController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(),
secondaryActionButton: null,
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
}
}

View File

@@ -0,0 +1,618 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../main.dart';
import 'package:supabase_auth_ui/supabase_auth_ui.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_pass_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_layout/mih_tile.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_env/env.dart';
class SignIn extends StatefulWidget {
final Function()? onTap;
const SignIn({super.key, required this.onTap});
@override
State<SignIn> createState() => _SignInState();
}
class _SignInState extends State<SignIn> {
final emailController = TextEditingController();
final passwordController = TextEditingController();
//bool _obscureText = true;
bool successfulSignIn = false;
bool showProfiles = false;
// focus node to capture keyboard events
final FocusNode _focusNode = FocusNode();
final baseAPI = AppEnviroment.baseApiUrl;
late List<MIHTile> sandboxProfileList = [];
//sign user in
Future<void> signUserIn() async {
//var _backgroundColor = Colors.transparent;
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
try {
var response = await http.post(
Uri.parse("$baseAPI/auth/signin"),
body:
'{"formFields": [{"id": "email","value": "${emailController.text}"}, {"id": "password","value": "${passwordController.text}"}]}',
headers: {
'Content-type': 'application/json',
'Accept': 'application/json',
"Authorization": "leatucczyixqwkqqdrhayiwzeofkltds"
},
);
//print(response.body[])
if (response.statusCode == 200) {
//print(response.body);
var userSignedin = jsonDecode(response.body);
if (userSignedin["status"] == "OK") {
//print("here");
setState(() {
successfulSignIn = true;
});
Navigator.of(context).pop();
} else {
Navigator.of(context).pop();
loginError();
passwordController.clear();
}
}
} on AuthException {
Navigator.of(context).pop();
loginError();
passwordController.clear();
}
}
Color getPrim() {
return MzanziInnovationHub.of(context)!.theme.secondaryColor();
}
Color getSec() {
return MzanziInnovationHub.of(context)!.theme.primaryColor();
}
void setSandboxProfiles(List<MIHTile> tileList) {
tileList.add(MIHTile(
onTap: () {
setState(() {
emailController.text = "testpatient@mzansi-innovation-hub.co.za";
passwordController.text = "Testprofile@1234";
});
validateInput();
},
tileName: "Patient",
tileIcon: Icon(
Icons.perm_identity_rounded,
color: getSec(),
size: 200,
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
setState(() {
emailController.text = "testdoctor@mzansi-innovation-hub.co.za";
passwordController.text = "Testprofile@1234";
});
validateInput();
},
tileName: "Doctor",
tileIcon: Icon(
Icons.medical_services,
color: getSec(),
size: 200,
),
p: getPrim(),
s: getSec(),
));
//if (AppEnviroment.getEnv() == "Dev") {
tileList.add(MIHTile(
onTap: () {
setState(() {
emailController.text = "test-business@mzansi-innovation-hub.co.za";
passwordController.text = "Testprofile@1234";
});
validateInput();
},
tileName: "Business",
tileIcon: Icon(
Icons.business,
color: getSec(),
size: 200,
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
setState(() {
emailController.text = "test@mzansi-innovation-hub.co.za";
passwordController.text = "Testprofile@1234";
});
validateInput();
},
tileName: "Test",
tileIcon: Icon(
Icons.warning_amber_rounded,
color: getSec(),
size: 200,
),
p: getPrim(),
s: getSec(),
));
//}
}
void loginError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Invalid Credentials");
},
);
}
void validateInput() async {
if (emailController.text.isEmpty || passwordController.text.isEmpty) {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
} else {
await signUserIn();
if (successfulSignIn) {
TextInput.finishAutofillContext();
Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false);
}
}
}
void showSandboxProfiles() {
showDialog(
context: context,
builder: (context) {
return Dialog(
//backgroundColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
child: Stack(
children: [
Container(
padding: const EdgeInsets.all(10.0),
width: 500.0,
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: 5.0),
),
child: Column(
//mainAxisSize: MainAxisSize.max,
children: [
Text(
"Sandbox Profiles",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: 25.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text(
"NB: These accounts are used for test purposes. Please do not store personal information on these profiles.",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 15),
Expanded(
child: GridView.builder(
// physics: ,
// shrinkWrap: true,
itemCount: sandboxProfileList.length,
gridDelegate:
const SliverGridDelegateWithMaxCrossAxisExtent(
mainAxisSpacing: 10, maxCrossAxisExtent: 100),
itemBuilder: (context, index) {
return sandboxProfileList[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,
),
),
),
],
),
);
},
);
}
MIHAction getActionButton() {
return MIHAction(
icon: Padding(
padding: const EdgeInsets.all(10.0),
child: SizedBox(
height: 50,
child: Image.asset('images/logo_light.png'),
),
),
iconSize: 35,
onTap: () {
Navigator.of(context).pushNamed(
'/about',
//arguments: widget.signedInUser,
);
},
);
}
MIHHeader getHeader() {
return const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: false,
bodyItems: [
KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
validateInput();
}
},
child: SafeArea(
child: Center(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(25.0),
child: AutofillGroup(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//logo
Icon(
Icons.lock,
size: 100,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
//spacer
const SizedBox(height: 10),
//Heading
Text(
'Sign In',
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
),
//spacer
const SizedBox(height: 25),
// SizedBox(
// width: 500.0,
// //height: 100.0,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.start,
// children: [
// GestureDetector(
// onTap: () {
// showSandboxProfiles();
// },
// child: Text(
// 'Sandbox Profile',
// style: TextStyle(
// fontSize: 18,
// color: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// fontWeight: FontWeight.bold,
// ),
// ),
// ),
// ],
// ),
// ),
// const SizedBox(height: 10),
//email input
SizedBox(
width: 500.0,
child: MIHTextField(
controller: emailController,
hintText: 'Email',
editable: true,
required: true,
autoFillHintGroup: const [AutofillHints.email],
),
),
//spacer
const SizedBox(height: 10),
//password input
SizedBox(
width: 500.0,
child: MIHPassField(
controller: passwordController,
hintText: 'Password',
required: true,
signIn: true,
autoFillHintGroup: const [AutofillHints.password],
),
),
SizedBox(
width: 500.0,
//height: 100.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(
'/forgot-password',
);
},
child: Text(
'Forgot Password?',
style: TextStyle(
fontSize: 15,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontWeight: FontWeight.bold,
),
),
),
],
),
),
//spacer
const SizedBox(height: 30),
// sign in button
SizedBox(
width: 500.0,
height: 50.0,
child: MIHButton(
buttonText: "Sign In",
buttonColor: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!
.theme
.primaryColor(),
onTap: () async {
validateInput();
},
),
),
//spacer
const SizedBox(height: 10),
//register text
SizedBox(
width: 500.0,
//height: 100.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
'New User?',
style: TextStyle(
fontSize: 18,
color: MzanziInnovationHub.of(context)!
.theme
.messageTextColor()),
),
const SizedBox(
width: 6,
),
GestureDetector(
onTap: widget.onTap,
child: Text(
'Register Now',
style: TextStyle(
fontSize: 18,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontWeight: FontWeight.bold,
),
),
),
],
),
),
//spacer
const SizedBox(height: 15),
SizedBox(
width: 500.0,
//height: 100.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Flexible(
flex: 1,
child: Padding(
padding: EdgeInsets.only(right: 10.0),
child: Divider(),
),
),
Flexible(
flex: 1,
child: GestureDetector(
child: Text(
'Use Sandox Profile',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor()),
),
onTap: () {
setState(() {
showProfiles = !showProfiles;
});
},
),
),
const Flexible(
flex: 1,
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: Divider(),
),
),
],
),
),
const SizedBox(height: 10),
Visibility(
visible: showProfiles,
child: SizedBox(
width: 500,
child: Column(
//mainAxisSize: MainAxisSize.max,
children: [
GridView.builder(
// physics: ,
shrinkWrap: true,
itemCount: sandboxProfileList.length,
gridDelegate:
const SliverGridDelegateWithMaxCrossAxisExtent(
mainAxisSpacing: 10,
maxCrossAxisExtent: 100),
itemBuilder: (context, index) {
return sandboxProfileList[index];
},
),
const SizedBox(height: 20),
Text(
"NB: These accounts are used for test purposes. Please do not store personal information on these profiles.",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
],
),
),
),
),
),
),
),
],
);
}
@override
void dispose() {
emailController.dispose();
passwordController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
setState(() {
setSandboxProfiles(sandboxProfileList);
});
super.initState();
}
@override
Widget build(BuildContext context) {
return MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(),
secondaryActionButton: null,
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
}
}

View File

@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'register.dart';
import 'signin.dart';
class SignInOrRegister extends StatefulWidget {
const SignInOrRegister({super.key});
@override
State<SignInOrRegister> createState() => _SignInOrRegisterState();
}
class _SignInOrRegisterState extends State<SignInOrRegister> {
bool showSignInPage = true;
void togglePages() {
setState(() {
showSignInPage = !showSignInPage;
});
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
if (showSignInPage) {
return SignIn(onTap: togglePages);
} else {
return Register(onTap: togglePages);
}
}
}

View File

@@ -0,0 +1,416 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../../main.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../../mih_components/mih_layout/mih_window.dart';
import '../../../mih_components/mih_pop_up_messages/mih_delete_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../../mih_env/env.dart';
import '../../../mih_objects/arguments.dart';
import '../../../mih_objects/business_employee.dart';
class BuildEmployeeList extends StatefulWidget {
final List<BusinessEmployee> employees;
final BusinessArguments arguments;
const BuildEmployeeList({
super.key,
required this.employees,
required this.arguments,
});
@override
State<BuildEmployeeList> createState() => _BuildEmployeeListState();
}
class _BuildEmployeeListState extends State<BuildEmployeeList> {
TextEditingController accessController = TextEditingController();
TextEditingController typeController = TextEditingController();
TextEditingController fnameController = TextEditingController();
TextEditingController lnameController = TextEditingController();
final baseAPI = AppEnviroment.baseApiUrl;
Future<void> updateEmployeeAPICall(int index) async {
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
var response = await http.put(
Uri.parse("$baseAPI/business-user/employees/update/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": widget.employees[index].business_id,
"app_id": widget.employees[index].app_id,
"title": typeController.text,
"access": accessController.text,
}),
);
if (response.statusCode == 200) {
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pop();
//setState(() {});
Navigator.of(context).pushNamed(
'/business-profile/manage',
arguments: BusinessArguments(
widget.arguments.signedInUser,
widget.arguments.businessUser,
widget.arguments.business,
),
);
String message = "Your employees details have been updated.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
}
Future<void> deleteNoteApiCall(int index) async {
var response = await http.delete(
Uri.parse("$baseAPI/business-user/employees/delete/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"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,
builder: (context) {
return const MIHErrorMessage(errorType: "Internet Connection");
},
);
}
void successPopUp(String message) {
showDialog(
context: context,
builder: (context) {
return MIHSuccessMessage(
successType: "Success",
successMessage: message,
);
},
);
}
bool isRequiredFieldsCaptured() {
if (accessController.text.isEmpty || typeController.text.isEmpty) {
return false;
} else {
return true;
}
}
void updateEmployeePopUp(int index) {
setState(() {
accessController.text = widget.employees[index].access;
typeController.text = widget.employees[index].title;
fnameController.text = widget.employees[index].fname;
lnameController.text = widget.employees[index].lname;
});
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Employee Details",
windowTools: [
IconButton(
onPressed: () {
showDeleteWarning(index);
},
icon: Icon(
Icons.delete,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
size: 35,
),
),
],
onWindowTapClose: () {
Navigator.pop(context);
},
windowBody: [
MIHTextField(
controller: fnameController,
hintText: "First Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: lnameController,
hintText: "Surname",
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: 15.0),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
buttonText: "Update",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
if (isRequiredFieldsCaptured()) {
updateEmployeeAPICall(index);
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
},
),
)
],
),
);
// 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: SingleChildScrollView(
// padding: const EdgeInsets.symmetric(horizontal: 10),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// Text(
// "Employee Details",
// 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: "First Name",
// editable: false,
// required: true,
// ),
// const SizedBox(height: 10.0),
// MIHTextField(
// controller: lnameController,
// hintText: "Surname",
// 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: "Update",
// buttonColor: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// textColor: MzanziInnovationHub.of(context)!
// .theme
// .primaryColor(),
// onTap: () {
// if (isRequiredFieldsCaptured()) {
// updateEmployeeAPICall(index);
// } else {
// showDialog(
// context: context,
// builder: (context) {
// return const MIHErrorMessage(
// errorType: "Input Error");
// },
// );
// }
// },
// ),
// )
// ],
// ),
// ),
// ),
// 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,
// ),
// ),
// ),
// 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();
typeController.dispose();
fnameController.dispose();
lnameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, index) {
return Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
);
},
itemCount: widget.employees.length,
itemBuilder: (context, index) {
//final patient = widget.patients[index].id_no.contains(widget.searchString);
//print(index);
var isMe = "";
if (widget.arguments.signedInUser.app_id ==
widget.employees[index].app_id) {
isMe = "(You)";
}
return ListTile(
title: Text(
"${widget.employees[index].fname} ${widget.employees[index].lname} - ${widget.employees[index].title} $isMe"),
subtitle: Text(
"${widget.employees[index].username}\n${widget.employees[index].email}\nAccess: ${widget.employees[index].access}",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
onTap: () {
updateEmployeePopUp(index);
},
);
},
);
}
}

View File

@@ -0,0 +1,351 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../../main.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../../mih_components/mih_layout/mih_window.dart';
import '../../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../../mih_env/env.dart';
import '../../../mih_objects/app_user.dart';
import '../../../mih_objects/arguments.dart';
class BuildUserList extends StatefulWidget {
final List<AppUser> users;
final BusinessArguments arguments;
const BuildUserList({
super.key,
required this.users,
required this.arguments,
});
@override
State<BuildUserList> createState() => _BuildUserListState();
}
class _BuildUserListState extends State<BuildUserList> {
TextEditingController accessController = TextEditingController();
TextEditingController typeController = TextEditingController();
TextEditingController fnameController = TextEditingController();
TextEditingController lnameController = TextEditingController();
final baseAPI = AppEnviroment.baseApiUrl;
Future<void> createBusinessUserAPICall(int index) async {
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
var response = await http.post(
Uri.parse("$baseAPI/business-user/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"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();
}
}
bool isRequiredFieldsCaptured() {
if (accessController.text.isEmpty || typeController.text.isEmpty) {
return false;
} else {
return true;
}
}
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) => MIHWindow(
fullscreen: false,
windowTitle: "Add Employee",
windowBody: [
const SizedBox(height: 10.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: 15.0),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
buttonText: "Add",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
if (isRequiredFieldsCaptured()) {
createBusinessUserAPICall(index);
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(
errorType: "Input Error");
},
);
}
},
),
),
const SizedBox(height: 10.0),
],
windowTools: [],
onWindowTapClose: () {
Navigator.pop(context);
}));
// 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: SingleChildScrollView(
// 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: () {
// if (isRequiredFieldsCaptured()) {
// createBusinessUserAPICall(index);
// } else {
// showDialog(
// context: context,
// builder: (context) {
// return const MIHErrorMessage(
// errorType: "Input Error");
// },
// );
// }
// },
// ),
// )
// ],
// ),
// ),
// ),
// 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(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
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);
},
);
},
);
}
}

View File

@@ -0,0 +1,538 @@
import 'dart:convert';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import 'package:supertokens_flutter/supertokens.dart';
import 'package:http/http.dart' as http2;
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_file_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/arguments.dart';
class BusinessAbout extends StatefulWidget {
final BusinessArguments arguments;
const BusinessAbout({
super.key,
required this.arguments,
});
@override
State<BusinessAbout> createState() => _BusinessAboutState();
}
class BusinessUserScreenArguments {}
class _BusinessAboutState extends State<BusinessAbout> {
final FocusNode _focusNode = FocusNode();
final baseAPI = AppEnviroment.baseApiUrl;
final nameController = TextEditingController();
final typeController = TextEditingController();
final regController = TextEditingController();
final logonameController = TextEditingController();
final fnameController = TextEditingController();
final lnameController = TextEditingController();
final titleController = TextEditingController();
final signtureController = TextEditingController();
final accessController = TextEditingController();
final contactController = TextEditingController();
final emailController = TextEditingController();
late PlatformFile? selectedLogo = null;
late PlatformFile? selectedSignature = null;
// late Future<BusinessUser?> futureBusinessUser;
// BusinessUser? businessUser;
// late Future<Business?> futureBusiness;
// Business? business;
late String business_id;
late String oldLogoPath;
late String oldSigPath;
Future<void> deleteFileApiCall(String filePath) async {
// delete file from minio
var response = await http.delete(
Uri.parse("$baseAPI/minio/delete/file/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{"file_path": filePath}),
);
//print("Here4");
//print(response.statusCode);
if (response.statusCode == 200) {
//SQL delete
} else {
internetConnectionPopUp();
}
}
// Future<BusinessUser?> getBusinessUserDetails() async {
// var response = await http
// .get(Uri.parse("$baseAPI/business-user/${widget.signedInUser.app_id}"));
// if (response.statusCode == 200) {
// String body = response.body;
// var decodedData = jsonDecode(body);
// BusinessUser business_User = BusinessUser.fromJson(decodedData);
// return business_User;
// } else {
// return null;
// }
// }
// Future<Business?> getBusinessAbout() async {
// var response = await http.get(
// Uri.parse("$baseAPI/business/app_id/${widget.signedInUser.app_id}"));
// if (response.statusCode == 200) {
// String body = response.body;
// var decodedData = jsonDecode(body);
// Business business = Business.fromJson(decodedData);
// return business;
// } else {
// return null;
// }
// }
Future<void> 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) {
} else {
internetConnectionPopUp();
}
}
Future<void> updateBusinessUserAPICall(String business_id) async {
var response = await http.put(
Uri.parse("$baseAPI/business-user/update/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": business_id,
"app_id": widget.arguments.signedInUser.app_id,
"signature": signtureController.text,
"sig_path":
"${widget.arguments.signedInUser.app_id}/business_files/${signtureController.text}",
"title": titleController.text,
"access": accessController.text,
}),
);
if (response.statusCode == 200) {
if (selectedSignature != null) {
uploadSelectedFile(selectedSignature, signtureController);
deleteFileApiCall(oldSigPath);
}
Navigator.of(context).pushNamed('/');
String message =
"Your business profile is now live! You can now start connecting with customers and growing your business.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
}
Future<void> updateBusinessProfileAPICall(String business_id) async {
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
var response = await http.put(
Uri.parse("$baseAPI/business/update/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": business_id,
"Name": nameController.text,
"type": typeController.text,
"registration_no": regController.text,
"logo_name": logonameController.text,
"logo_path":
"${widget.arguments.signedInUser.app_id}/business_files/${logonameController.text}",
"contact_no": contactController.text,
"bus_email": emailController.text,
}),
);
if (response.statusCode == 200) {
//var businessResponse = jsonDecode(response.body);
//print(selectedLogo != null);
if (selectedLogo != null) {
uploadSelectedFile(selectedLogo, logonameController);
deleteFileApiCall(oldLogoPath);
}
updateBusinessUserAPICall(business_id);
} 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,
);
},
);
}
bool isFieldsFilled() {
if (nameController.text.isEmpty ||
typeController.text.isEmpty ||
regController.text.isEmpty ||
logonameController.text.isEmpty ||
fnameController.text.isEmpty ||
lnameController.text.isEmpty ||
titleController.text.isEmpty ||
signtureController.text.isEmpty ||
accessController.text.isEmpty ||
contactController.text.isEmpty ||
emailController.text.isEmpty) {
return false;
} else {
return true;
}
}
void submitForm(String business_id) {
if (!validEmail()) {
emailError();
} else if (isFieldsFilled()) {
updateBusinessProfileAPICall(business_id);
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
}
void emailError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Invalid Email");
},
);
}
bool validEmail() {
String text = emailController.text;
var regex = RegExp(r'^[a-zA-Z0-9]+@[a-zA-Z.-]+\.[a-zA-Z]{2,}$');
return regex.hasMatch(text);
}
bool isFullAccess() {
if (widget.arguments.businessUser!.access == "Partial") {
return false;
} else {
return true;
}
}
@override
void dispose() {
nameController.dispose();
typeController.dispose();
regController.dispose();
logonameController.dispose();
fnameController.dispose();
lnameController.dispose();
titleController.dispose();
signtureController.dispose();
accessController.dispose();
contactController.dispose();
emailController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
setState(() {
//businessUser = results;
titleController.text = widget.arguments.businessUser!.title;
fnameController.text = widget.arguments.signedInUser.fname;
lnameController.text = widget.arguments.signedInUser.lname;
signtureController.text = widget.arguments.businessUser!.signature;
titleController.text = widget.arguments.businessUser!.title;
accessController.text = widget.arguments.businessUser!.access;
oldSigPath = widget.arguments.businessUser!.sig_path;
//business = results;
business_id = widget.arguments.business!.business_id;
regController.text = widget.arguments.business!.registration_no;
nameController.text = widget.arguments.business!.Name;
typeController.text = widget.arguments.business!.type;
logonameController.text = widget.arguments.business!.logo_name;
oldLogoPath = widget.arguments.business!.logo_path;
contactController.text = widget.arguments.business!.contact_no;
emailController.text = widget.arguments.business!.bus_email;
});
super.initState();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
//print(business_id);
submitForm(business_id);
}
},
child: SingleChildScrollView(
child: Column(
children: [
Visibility(
visible: isFullAccess(),
child: Column(
children: [
const Text(
"Business Profile",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
Divider(
color: MzanziInnovationHub.of(context)
?.theme
.secondaryColor(),
),
const SizedBox(height: 10.0),
MIHTextField(
controller: regController,
hintText: "Registration No.",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: nameController,
hintText: "Business Name",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHDropdownField(
controller: typeController,
hintText: "Business Type",
dropdownOptions: const ["Doctors Office", "Other"],
required: true,
editable: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: contactController,
hintText: "Contact Number",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: emailController,
hintText: "Email",
editable: true,
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: 15.0),
],
),
),
Column(
children: [
//const SizedBox(height: 15.0),
const Text(
"My Business User",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
Divider(
color:
MzanziInnovationHub.of(context)?.theme.secondaryColor(),
),
const SizedBox(height: 10.0),
MIHDropdownField(
controller: titleController,
hintText: "Title",
dropdownOptions: const ["Doctor", "Assistant"],
required: true,
editable: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: fnameController,
hintText: "Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: lnameController,
hintText: "Surname",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHFileField(
controller: signtureController,
hintText: "Signature",
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(() {
selectedSignature = selectedFile;
});
setState(() {
signtureController.text = selectedFile.name;
});
},
),
const SizedBox(height: 15.0),
MIHDropdownField(
controller: accessController,
hintText: "Access",
dropdownOptions: const ["Full", "Partial"],
required: true,
editable: false,
),
// const SizedBox(height: 15.0),
// const Text(
// "My Test Data",
// style: TextStyle(
// fontWeight: FontWeight.bold,
// fontSize: 25,
// ),
// ),
// Divider(
// color:
// MzanziInnovationHub.of(context)?.theme.secondaryColor(),
// ),
// const SizedBox(height: 10.0),
// MIHTextField(
// controller: typeController,
// hintText: widget.arguments.business!.type,
// editable: false,
// required: true,
// ),
// const SizedBox(height: 15.0),
// MIHTextField(
// controller: titleController,
// hintText: widget.arguments.businessUser!.title,
// editable: false,
// required: true,
// ),
// const SizedBox(height: 15.0),
// MIHTextField(
// controller: accessController,
// hintText: widget.arguments.businessUser!.access,
// editable: false,
// required: true,
// ),
//const SizedBox(height: 15.0),
const SizedBox(height: 30.0),
SizedBox(
width: 500.0,
height: 50.0,
child: MIHButton(
buttonText: "Update",
buttonColor: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
//print(business_id);
submitForm(business_id);
},
),
),
],
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,578 @@
import 'dart:convert';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import 'package:supertokens_flutter/supertokens.dart';
import 'package:http/http.dart' as http2;
import '../../mih_apis/mih_location_api.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_file_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/arguments.dart';
class BusinessDetails extends StatefulWidget {
final BusinessArguments arguments;
const BusinessDetails({
super.key,
required this.arguments,
});
@override
State<BusinessDetails> createState() => _BusinessDetailsState();
}
class BusinessUserScreenArguments {}
class _BusinessDetailsState extends State<BusinessDetails> {
final FocusNode _focusNode = FocusNode();
final baseAPI = AppEnviroment.baseApiUrl;
final nameController = TextEditingController();
final typeController = TextEditingController();
final regController = TextEditingController();
final logonameController = TextEditingController();
final fnameController = TextEditingController();
final lnameController = TextEditingController();
final titleController = TextEditingController();
final signtureController = TextEditingController();
final accessController = TextEditingController();
final contactController = TextEditingController();
final emailController = TextEditingController();
final locationController = TextEditingController();
late PlatformFile? selectedLogo = null;
late PlatformFile? selectedSignature = null;
// late Future<BusinessUser?> futureBusinessUser;
// BusinessUser? businessUser;
// late Future<Business?> futureBusiness;
// Business? business;
late String business_id;
late String oldLogoPath;
late String oldSigPath;
Future<void> deleteFileApiCall(String filePath) async {
// delete file from minio
var response = await http.delete(
Uri.parse("$baseAPI/minio/delete/file/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{"file_path": filePath}),
);
//print("Here4");
//print(response.statusCode);
if (response.statusCode == 200) {
//SQL delete
} else {
internetConnectionPopUp();
}
}
// Future<BusinessUser?> getBusinessUserDetails() async {
// var response = await http
// .get(Uri.parse("$baseAPI/business-user/${widget.signedInUser.app_id}"));
// if (response.statusCode == 200) {
// String body = response.body;
// var decodedData = jsonDecode(body);
// BusinessUser business_User = BusinessUser.fromJson(decodedData);
// return business_User;
// } else {
// return null;
// }
// }
// Future<Business?> getBusinessDetails() async {
// var response = await http.get(
// Uri.parse("$baseAPI/business/app_id/${widget.signedInUser.app_id}"));
// if (response.statusCode == 200) {
// String body = response.body;
// var decodedData = jsonDecode(body);
// Business business = Business.fromJson(decodedData);
// return business;
// } else {
// return null;
// }
// }
Future<void> 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) {
} else {
internetConnectionPopUp();
}
}
Future<void> updateBusinessUserAPICall(String business_id) async {
var response = await http.put(
Uri.parse("$baseAPI/business-user/update/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": business_id,
"app_id": widget.arguments.signedInUser.app_id,
"signature": signtureController.text,
"sig_path":
"${widget.arguments.signedInUser.app_id}/business_files/${signtureController.text}",
"title": titleController.text,
"access": accessController.text,
}),
);
if (response.statusCode == 200) {
if (selectedSignature != null) {
uploadSelectedFile(selectedSignature, signtureController);
deleteFileApiCall(oldSigPath);
}
Navigator.of(context).pushNamed('/');
String message =
"Your business profile is now live! You can now start connecting with customers and growing your business.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
}
Future<void> updateBusinessProfileAPICall(String business_id) async {
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
var response = await http.put(
Uri.parse("$baseAPI/business/update/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": business_id,
"Name": nameController.text,
"type": typeController.text,
"registration_no": regController.text,
"logo_name": logonameController.text,
"logo_path":
"${widget.arguments.signedInUser.app_id}/business_files/${logonameController.text}",
"contact_no": contactController.text,
"bus_email": emailController.text,
"gps_location": locationController.text,
}),
);
if (response.statusCode == 200) {
//var businessResponse = jsonDecode(response.body);
//print(selectedLogo != null);
if (selectedLogo != null) {
uploadSelectedFile(selectedLogo, logonameController);
deleteFileApiCall(oldLogoPath);
}
updateBusinessUserAPICall(business_id);
} 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,
);
},
);
}
bool isFieldsFilled() {
if (nameController.text.isEmpty ||
typeController.text.isEmpty ||
regController.text.isEmpty ||
logonameController.text.isEmpty ||
fnameController.text.isEmpty ||
lnameController.text.isEmpty ||
titleController.text.isEmpty ||
signtureController.text.isEmpty ||
accessController.text.isEmpty ||
contactController.text.isEmpty ||
emailController.text.isEmpty) {
return false;
} else {
return true;
}
}
void submitForm(String business_id) {
if (!validEmail()) {
emailError();
} else if (isFieldsFilled()) {
updateBusinessProfileAPICall(business_id);
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
}
void emailError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Invalid Email");
},
);
}
bool validEmail() {
String text = emailController.text;
var regex = RegExp(r'^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$');
return regex.hasMatch(text);
}
bool isFullAccess() {
if (widget.arguments.businessUser!.access == "Partial") {
return false;
} else {
return true;
}
}
@override
void dispose() {
nameController.dispose();
typeController.dispose();
regController.dispose();
logonameController.dispose();
fnameController.dispose();
lnameController.dispose();
titleController.dispose();
signtureController.dispose();
accessController.dispose();
contactController.dispose();
emailController.dispose();
locationController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
setState(() {
//businessUser = results;
titleController.text = widget.arguments.businessUser!.title;
fnameController.text = widget.arguments.signedInUser.fname;
lnameController.text = widget.arguments.signedInUser.lname;
signtureController.text = widget.arguments.businessUser!.signature;
titleController.text = widget.arguments.businessUser!.title;
accessController.text = widget.arguments.businessUser!.access;
oldSigPath = widget.arguments.businessUser!.sig_path;
//business = results;
business_id = widget.arguments.business!.business_id;
regController.text = widget.arguments.business!.registration_no;
nameController.text = widget.arguments.business!.Name;
typeController.text = widget.arguments.business!.type;
logonameController.text = widget.arguments.business!.logo_name;
oldLogoPath = widget.arguments.business!.logo_path;
contactController.text = widget.arguments.business!.contact_no;
emailController.text = widget.arguments.business!.bus_email;
locationController.text = widget.arguments.business!.gps_location;
});
super.initState();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
//print(business_id);
submitForm(business_id);
}
},
child: Column(
children: [
Visibility(
visible: isFullAccess(),
child: Column(
children: [
const Text(
"Business Profile",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
Divider(
color:
MzanziInnovationHub.of(context)?.theme.secondaryColor(),
),
const SizedBox(height: 10.0),
MIHTextField(
controller: regController,
hintText: "Registration No.",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: nameController,
hintText: "Business Name",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHDropdownField(
controller: typeController,
hintText: "Business Type",
dropdownOptions: const ["Doctors Office", "Other"],
required: true,
editable: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: contactController,
hintText: "Contact Number",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: emailController,
hintText: "Email",
editable: true,
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(
child: MIHTextField(
controller: locationController,
hintText: "Location",
editable: false,
required: false,
),
),
const SizedBox(width: 10.0),
SizedBox(
width: 80.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.0),
],
),
),
Column(
children: [
//const SizedBox(height: 15.0),
const Text(
"My Business User",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
Divider(
color:
MzanziInnovationHub.of(context)?.theme.secondaryColor(),
),
const SizedBox(height: 10.0),
MIHDropdownField(
controller: titleController,
hintText: "Title",
dropdownOptions: const ["Doctor", "Assistant"],
required: true,
editable: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: fnameController,
hintText: "Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: lnameController,
hintText: "Surname",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHFileField(
controller: signtureController,
hintText: "Signature",
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(() {
selectedSignature = selectedFile;
});
setState(() {
signtureController.text = selectedFile.name;
});
},
),
const SizedBox(height: 15.0),
MIHDropdownField(
controller: accessController,
hintText: "Access",
dropdownOptions: const ["Full", "Partial"],
required: true,
editable: false,
),
// const SizedBox(height: 15.0),
// const Text(
// "My Test Data",
// style: TextStyle(
// fontWeight: FontWeight.bold,
// fontSize: 25,
// ),
// ),
// Divider(
// color:
// MzanziInnovationHub.of(context)?.theme.secondaryColor(),
// ),
// const SizedBox(height: 10.0),
// MIHTextField(
// controller: typeController,
// hintText: widget.arguments.business!.type,
// editable: false,
// required: true,
// ),
// const SizedBox(height: 15.0),
// MIHTextField(
// controller: titleController,
// hintText: widget.arguments.businessUser!.title,
// editable: false,
// required: true,
// ),
// const SizedBox(height: 15.0),
// MIHTextField(
// controller: accessController,
// hintText: widget.arguments.businessUser!.access,
// editable: false,
// required: true,
// ),
//const SizedBox(height: 15.0),
const SizedBox(height: 30.0),
SizedBox(
width: 500.0,
height: 50.0,
child: MIHButton(
buttonText: "Update",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
//print(business_id);
submitForm(business_id);
},
),
),
],
),
],
),
),
);
}
}

View File

@@ -0,0 +1,538 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_swipe_detector/flutter_swipe_detector.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../mih_components/mih_inputs_and_buttons/mih_search_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/arguments.dart';
import '../../mih_objects/business_employee.dart';
import 'builder/build_employee_list.dart';
import 'builder/build_user_list.dart';
import 'business_details.dart';
class ManageBusinessProfile extends StatefulWidget {
final BusinessArguments arguments;
const ManageBusinessProfile({
super.key,
required this.arguments,
});
@override
State<ManageBusinessProfile> createState() => _ManageBusinessProfileState();
}
class _ManageBusinessProfileState extends State<ManageBusinessProfile> {
final FocusNode _focusNode = FocusNode();
final baseAPI = AppEnviroment.baseApiUrl;
final TextEditingController searchController = TextEditingController();
String userSearch = "";
String errorCode = "";
String errorBody = "";
int selectionIndex = 0;
late Future<List<BusinessEmployee>> employeeList;
late Future<List<AppUser>> userSearchResults;
Future<List<BusinessEmployee>> fetchEmployees() async {
//print("Patien manager page: $endpoint");
final response = await http.get(Uri.parse(
"${AppEnviroment.baseApiUrl}/business-user/employees/${widget.arguments.businessUser!.business_id}"));
errorCode = response.statusCode.toString();
errorBody = response.body;
if (response.statusCode == 200) {
//print("Here1");
Iterable l = jsonDecode(response.body);
//print("Here2");
List<BusinessEmployee> patientQueue = List<BusinessEmployee>.from(
l.map((model) => BusinessEmployee.fromJson(model)));
//print("Here3");
//print(patientQueue);
return patientQueue;
} else {
throw Exception('failed to load employees');
}
}
Future<List<AppUser>> 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<AppUser> users =
List<AppUser>.from(l.map((model) => AppUser.fromJson(model)));
return users;
} else {
throw Exception('failed to load patients');
}
}
Widget employeesview() {
return Column(mainAxisSize: MainAxisSize.max, children: [
const Text(
"Business Team",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
Divider(color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
const SizedBox(height: 10),
FutureBuilder(
future: employeeList,
builder: (context, snapshot) {
//print("patient Queue List ${snapshot.hasData}");
if (snapshot.connectionState == ConnectionState.waiting) {
return const Mihloadingcircle();
} else if (snapshot.connectionState == ConnectionState.done) {
//List<BusinessEmployee> employeeListResults;
// if (searchString == "") {
// patientQueueList = [];
// } else {
// print(patientQueueList);
// }
return displayEmployeeList(snapshot.requireData);
} else {
return Center(
child: Text(
"$errorCode: Error pulling Patients Data\n${AppEnviroment.baseApiUrl}/business-user/users/${widget.arguments.businessUser!.business_id}\n$errorBody",
style: TextStyle(
fontSize: 25,
color: MzanziInnovationHub.of(context)!.theme.errorColor()),
textAlign: TextAlign.center,
),
);
}
},
),
]);
}
Widget displayEmployeeList(List<BusinessEmployee> employeeList) {
if (employeeList.isNotEmpty) {
return BuildEmployeeList(
employees: employeeList,
arguments: widget.arguments,
);
}
return Center(
child: Text(
"",
style: TextStyle(
fontSize: 25,
color: MzanziInnovationHub.of(context)!.theme.messageTextColor()),
textAlign: TextAlign.center,
),
);
}
Widget displayUserList(List<AppUser> userList) {
if (userList.isNotEmpty) {
return BuildUserList(
users: userList,
arguments: widget.arguments,
);
}
return 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() {
return KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
submitUserForm();
}
},
child: Column(mainAxisSize: MainAxisSize.max, children: [
const Text(
"User Search",
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
),
//spacer
Divider(color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
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 const Mihloadingcircle();
} else if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
List<AppUser> patientsList;
if (userSearch == "") {
patientsList = [];
} else {
patientsList = snapshot.data!;
//print(patientsList);
}
return displayUserList(patientsList);
} else {
return 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,
builder: (context) {
return const MIHErrorMessage(errorType: "Internet Connection");
},
);
}
void successPopUp(String message) {
showDialog(
context: context,
builder: (context) {
return MIHSuccessMessage(
successType: "Success",
successMessage: message,
);
},
);
}
void emailError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Invalid Email");
},
);
}
Widget showSelection(int selectionIndex) {
// if (selectionIndex == 0) {
// return BusinessDetails(arguments: widget.arguments);
// } else
if (selectionIndex == 0) {
return BusinessDetails(arguments: widget.arguments);
} else if (selectionIndex == 1) {
return employeesview();
} else {
return userSearchView();
}
}
MIHAction getActionButton() {
return MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).popAndPushNamed(
'/',
arguments: false,
);
},
);
}
MIHHeader getHeader() {
bool isFullAccess = false;
if (widget.arguments.businessUser!.access == "Full") {
isFullAccess = true;
}
return MIHHeader(
headerAlignment: MainAxisAlignment.end,
headerItems: [
// IconButton(
// onPressed: () {
// setState(() {
// selectionIndex = 0;
// });
// },
// icon: const Icon(
// Icons.info_outline,
// size: 35,
// ),
// ),
//============ Business Details ================
Visibility(
visible: selectionIndex != 0,
child: IconButton(
onPressed: () {
setState(() {
selectionIndex = 0;
});
},
icon: const Icon(
Icons.business,
size: 35,
),
),
),
Visibility(
visible: selectionIndex == 0,
child: IconButton.filled(
onPressed: () {
setState(() {
selectionIndex = 0;
});
},
icon: const Icon(
Icons.business,
size: 35,
),
),
),
//============ Team Manager ================
Visibility(
visible: isFullAccess && selectionIndex != 1,
child: IconButton(
onPressed: () {
setState(() {
selectionIndex = 1;
});
},
icon: const Icon(
Icons.people_outline,
size: 35,
),
),
),
Visibility(
visible: isFullAccess && selectionIndex == 1,
child: IconButton.filled(
onPressed: () {
setState(() {
selectionIndex = 1;
});
},
icon: const Icon(
Icons.people_outline,
size: 35,
),
),
),
//============ Add Team member ================
Visibility(
visible: isFullAccess && selectionIndex != 2,
child: IconButton(
onPressed: () {
setState(() {
selectionIndex = 2;
});
},
icon: const Icon(
Icons.add,
size: 35,
),
),
),
Visibility(
visible: isFullAccess && selectionIndex == 2,
child: IconButton.filled(
onPressed: () {
setState(() {
selectionIndex = 2;
});
},
icon: const Icon(
Icons.add,
size: 35,
),
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: true,
bodyItems: [
showSelection(selectionIndex),
],
);
}
@override
void dispose() {
searchController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
userSearchResults = fetchUsers("abc");
employeeList = fetchEmployees();
super.initState();
}
@override
Widget build(BuildContext context) {
return SwipeDetector(
onSwipeLeft: (offset) {
if (selectionIndex < 2) {
setState(() {
selectionIndex += 1;
});
}
//print("swipe left");
},
onSwipeRight: (offset) {
if (selectionIndex > 0) {
setState(() {
selectionIndex -= 1;
});
}
//print("swipe right");
},
child: MIHLayoutBuilder(
actionButton: getActionButton(),
secondaryActionButton: null,
header: getHeader(),
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
),
);
// return Scaffold(
// // appBar: const MIHAppBar(
// // barTitle: "Business Profile",
// // propicFile: null,
// // ),
// //drawer: MIHAppDrawer(signedInUser: widget.arguments.signedInUser),
// body: SafeArea(
// child: Stack(
// children: [
// SingleChildScrollView(
// padding: const EdgeInsets.all(15),
// child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
// mainAxisSize: MainAxisSize.max,
// children: [
// //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,
// ),
// ),
// ],
// ),
// ),
// showSelection(selectionIndex, screenWidth, screenHeight),
// ],
// ),
// ),
// Positioned(
// top: 10,
// left: 5,
// width: 50,
// height: 50,
// child: IconButton(
// onPressed: () {
// Navigator.of(context).pop();
// },
// icon: const Icon(Icons.arrow_back),
// ),
// ),
// ],
// ),
// ),
// );
}
}

View File

@@ -0,0 +1,685 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import 'package:supertokens_flutter/supertokens.dart';
import 'package:file_picker/file_picker.dart';
import 'package:http/http.dart' as http2;
import '../../mih_apis/mih_location_api.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_file_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/app_user.dart';
class ProfileBusinessAdd extends StatefulWidget {
//final BusinessUserScreenArguments arguments;
final AppUser signedInUser;
const ProfileBusinessAdd({
super.key,
required this.signedInUser,
});
@override
State<ProfileBusinessAdd> createState() => _ProfileBusinessAddState();
}
class _ProfileBusinessAddState extends State<ProfileBusinessAdd> {
final FocusNode _focusNode = FocusNode();
final baseAPI = AppEnviroment.baseApiUrl;
final nameController = TextEditingController();
final typeController = TextEditingController();
final regController = TextEditingController();
final addressController = TextEditingController();
final logonameController = TextEditingController();
final fnameController = TextEditingController();
final lnameController = TextEditingController();
final titleController = TextEditingController();
final signtureController = TextEditingController();
final accessController = TextEditingController();
final contactController = TextEditingController();
final emailController = TextEditingController();
final locationController = TextEditingController();
late PlatformFile selectedLogo;
late PlatformFile selectedSignature;
Future<void> uploadSelectedFile(
PlatformFile file, TextEditingController controller) async {
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.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) {
} else {
internetConnectionPopUp();
}
}
Future<void> createBusinessUserAPICall(String business_id) async {
var response = await http.post(
Uri.parse("$baseAPI/business-user/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": business_id,
"app_id": widget.signedInUser.app_id,
"signature": signtureController.text,
"sig_path":
"${widget.signedInUser.app_id}/business_files/${signtureController.text}",
"title": titleController.text,
"access": accessController.text,
}),
);
if (response.statusCode == 201) {
uploadSelectedFile(selectedSignature, signtureController);
Navigator.of(context).pushNamed('/');
String message =
"Your business profile is now live! You can now start connecting with customers and growing your business.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
}
Future<void> createBusinessProfileAPICall() async {
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
var response = await http.post(
Uri.parse("$baseAPI/business/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"Name": nameController.text,
"type": typeController.text,
"registration_no": regController.text,
"logo_name": logonameController.text,
"logo_path":
"${widget.signedInUser.app_id}/business_files/${logonameController.text}",
"contact_no": contactController.text,
"bus_email": emailController.text,
"gps_location": locationController.text,
}),
);
if (response.statusCode == 201) {
var businessResponse = jsonDecode(response.body);
createBusinessUserAPICall(businessResponse['business_id']);
uploadSelectedFile(selectedLogo, logonameController);
} 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,
);
},
);
}
bool isFieldsFilled() {
if (nameController.text.isEmpty ||
typeController.text.isEmpty ||
regController.text.isEmpty ||
logonameController.text.isEmpty ||
fnameController.text.isEmpty ||
lnameController.text.isEmpty ||
titleController.text.isEmpty ||
signtureController.text.isEmpty ||
accessController.text.isEmpty ||
contactController.text.isEmpty ||
emailController.text.isEmpty) {
return false;
} else {
return true;
}
}
void submitForm() {
if (!validEmail()) {
emailError();
} else if (isFieldsFilled()) {
createBusinessProfileAPICall();
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
}
void emailError() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Invalid Email");
},
);
}
bool validEmail() {
String text = emailController.text;
var regex = RegExp(r'^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$');
return regex.hasMatch(text);
}
MIHAction getActionButton() {
return MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pop();
},
);
}
MIHHeader getHeader() {
return const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"Add Business Profile",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: true,
bodyItems: [
KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
submitForm();
}
},
child: SingleChildScrollView(
child: Column(
children: [
//const SizedBox(height: 15),
const Text(
"My Business Details",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
Divider(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor()),
const SizedBox(height: 10.0),
MIHTextField(
controller: regController,
hintText: "Registration No.",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: nameController,
hintText: "Business Name",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHDropdownField(
controller: typeController,
hintText: "Business Type",
dropdownOptions: const ["Doctors Office", "Other"],
required: true,
editable: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: contactController,
hintText: "Contact Number",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: emailController,
hintText: "Email",
editable: true,
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(
child: MIHTextField(
controller: locationController,
hintText: "Location",
editable: false,
required: false,
),
),
const SizedBox(width: 10.0),
SizedBox(
width: 80.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.0),
Divider(
color:
MzanziInnovationHub.of(context)?.theme.secondaryColor(),
),
//const SizedBox(height: 15.0),
const Text(
"My Business User",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 22,
),
),
Divider(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor()),
const SizedBox(height: 10.0),
MIHDropdownField(
controller: titleController,
hintText: "Title",
dropdownOptions: const ["Doctor", "Assistant"],
required: true,
editable: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: fnameController,
hintText: "Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: lnameController,
hintText: "Surname",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHFileField(
controller: signtureController,
hintText: "Signature",
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(() {
selectedSignature = selectedFile;
});
setState(() {
signtureController.text = selectedFile.name;
});
},
),
const SizedBox(height: 15.0),
MIHDropdownField(
controller: accessController,
hintText: "Access",
dropdownOptions: const ["Full", "Partial"],
required: true,
editable: false,
),
const SizedBox(height: 30.0),
SizedBox(
width: 500.0,
height: 50.0,
child: MIHButton(
buttonText: "Add",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
submitForm();
},
),
),
],
),
),
),
],
);
}
@override
void dispose() {
nameController.dispose();
typeController.dispose();
regController.dispose();
logonameController.dispose();
fnameController.dispose();
lnameController.dispose();
titleController.dispose();
signtureController.dispose();
accessController.dispose();
contactController.dispose();
emailController.dispose();
locationController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
setState(() {
fnameController.text = widget.signedInUser.fname;
lnameController.text = widget.signedInUser.lname;
accessController.text = "Full";
});
super.initState();
}
@override
Widget build(BuildContext context) {
return MIHLayoutBuilder(
actionButton: getActionButton(),
secondaryActionButton: null,
header: getHeader(),
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
// return Scaffold(
// // appBar: const MIHAppBar(
// // barTitle: "Add Business",
// // propicFile: null,
// // ),
// //drawer: MIHAppDrawer(signedInUser: widget.signedInUser),
// body: SafeArea(
// child: Stack(
// children: [
// KeyboardListener(
// focusNode: _focusNode,
// autofocus: true,
// onKeyEvent: (event) async {
// if (event is KeyDownEvent &&
// event.logicalKey == LogicalKeyboardKey.enter) {
// submitForm();
// }
// },
// child: SingleChildScrollView(
// padding: const EdgeInsets.all(15),
// child: Column(
// children: [
// //const SizedBox(height: 15),
// const Text(
// "Add Business Profile",
// style: TextStyle(
// fontWeight: FontWeight.bold,
// fontSize: 25,
// ),
// ),
// const SizedBox(height: 25.0),
// MIHTextField(
// controller: regController,
// hintText: "Registration No.",
// editable: true,
// required: true,
// ),
// const SizedBox(height: 10.0),
// MIHTextField(
// controller: nameController,
// hintText: "Business Name",
// editable: true,
// required: true,
// ),
// const SizedBox(height: 10.0),
// MIHDropdownField(
// controller: typeController,
// hintText: "Business Type",
// dropdownOptions: const ["Doctors Office", "Other"],
// required: true,
// editable: true,
// ),
// const SizedBox(height: 10.0),
// MIHTextField(
// controller: contactController,
// hintText: "Contact Number",
// editable: true,
// required: true,
// ),
// const SizedBox(height: 10.0),
// MIHTextField(
// controller: emailController,
// hintText: "Email",
// editable: true,
// 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: 15.0),
// Divider(
// color: MzanziInnovationHub.of(context)
// ?.theme
// .secondaryColor(),
// ),
// //const SizedBox(height: 15.0),
// const Text(
// "My Business User",
// style: TextStyle(
// fontWeight: FontWeight.bold,
// fontSize: 25,
// ),
// ),
// const SizedBox(height: 25.0),
// MIHDropdownField(
// controller: titleController,
// hintText: "Title",
// dropdownOptions: const ["Doctor", "Assistant"],
// required: true,
// editable: true,
// ),
// const SizedBox(height: 10.0),
// MIHTextField(
// controller: fnameController,
// hintText: "Name",
// editable: false,
// required: true,
// ),
// const SizedBox(height: 10.0),
// MIHTextField(
// controller: lnameController,
// hintText: "Surname",
// editable: false,
// required: true,
// ),
// const SizedBox(height: 10.0),
// MIHFileField(
// controller: signtureController,
// hintText: "Signature",
// 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(() {
// selectedSignature = selectedFile;
// });
// setState(() {
// signtureController.text = selectedFile.name;
// });
// },
// ),
// const SizedBox(height: 15.0),
// MIHDropdownField(
// controller: accessController,
// hintText: "Access",
// dropdownOptions: const ["Full", "Partial"],
// required: true,
// editable: false,
// ),
// const SizedBox(height: 30.0),
// SizedBox(
// width: 500.0,
// height: 50.0,
// child: MIHButton(
// buttonText: "Add",
// buttonColor: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// textColor: MzanziInnovationHub.of(context)!
// .theme
// .primaryColor(),
// onTap: () {
// submitForm();
// },
// ),
// ),
// ],
// ),
// ),
// ),
// Positioned(
// top: 10,
// left: 5,
// width: 50,
// height: 50,
// child: IconButton(
// onPressed: () {
// Navigator.of(context).pop();
// },
// icon: const Icon(Icons.arrow_back),
// ),
// )
// ],
// ),
// ),
// );
}
}

View File

@@ -0,0 +1,970 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_swipe_detector/flutter_swipe_detector.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:google_nav_bar/google_nav_bar.dart';
import '../../main.dart';
//import 'package:supertokens_flutter/http.dart' as http;
import "package:universal_html/html.dart" as html;
import '../../mih_apis/mih_location_api.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_search_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_app_drawer.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_layout/mih_notification_drawer.dart';
import '../../mih_components/mih_layout/mih_tile.dart';
import '../../mih_components/mih_layout/mih_window.dart';
import '../../mih_components/mih_pop_up_messages/mih_delete_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_components/mih_pop_up_messages/mih_notification_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_warning_message.dart';
import '../../mih_components/mih_profile_picture.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/arguments.dart';
import '../../mih_objects/business.dart';
import '../../mih_objects/business_user.dart';
import '../../mih_objects/notification.dart';
import '../test/test.dart';
class MIHHome extends StatefulWidget {
final AppUser signedInUser;
final BusinessUser? businessUser;
final Business? business;
final List<MIHNotification> notifications;
final ImageProvider<Object>? propicFile;
final bool isUserNew;
final bool isBusinessUser;
final bool isBusinessUserNew;
final bool isDevActive;
final bool personalSelected;
const MIHHome({
super.key,
required this.signedInUser,
required this.businessUser,
required this.business,
required this.notifications,
required this.propicFile,
required this.isUserNew,
required this.isBusinessUser,
required this.isBusinessUserNew,
required this.isDevActive,
required this.personalSelected,
});
@override
State<MIHHome> createState() => _MIHHomeState();
}
class _MIHHomeState extends State<MIHHome> {
final proPicController = TextEditingController();
final TextEditingController searchController = TextEditingController();
final FocusNode _focusNode = FocusNode();
late List<MIHTile> persHTList = [];
late List<MIHTile> busHTList = [];
late List<List<MIHTile>> pbswitch;
late List<MIHNotification> notifiList;
late bool businessUserSwitch;
int _selectedIndex = 0;
String appSearch = "";
int amount = 10;
final baseAPI = AppEnviroment.baseApiUrl;
void setAppsNewPersonal(List<MIHTile> tileList) {
if (widget.signedInUser.fname == "") {
tileList.add(MIHTile(
onTap: () {
Navigator.of(context).pushNamed('/user-profile',
arguments: AppProfileUpdateArguments(
widget.signedInUser, widget.propicFile));
},
tileName: "Setup Profie",
tileIcon: Icon(
Icons.perm_identity,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
}
}
void setAppsNewBusiness(List<MIHTile> tileList) {
tileList.add(MIHTile(
onTap: () {
Navigator.of(context).popAndPushNamed(
'/business-profile/set-up',
arguments: widget.signedInUser,
);
},
tileName: "Setup Business",
tileIcon: Icon(
Icons.add_business_outlined,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
}
void setAppsPersonal(List<MIHTile> tileList) {
ImageProvider logo = MzanziInnovationHub.of(context)!.theme.logoImage();
tileList.add(MIHTile(
videoYTLink: "https://www.youtube.com/watch?v=P2bM9eosJ_A",
onTap: () {
Navigator.of(context).pushNamed(
'/user-profile',
arguments:
AppProfileUpdateArguments(widget.signedInUser, widget.propicFile),
);
},
tileName: "Mzansi Profile",
tileIcon: Padding(
padding: const EdgeInsets.all(15.0),
child: Image(image: logo),
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
Navigator.of(context).pushNamed('/patient-profile',
arguments: PatientViewArguments(
widget.signedInUser, null, null, null, "personal"));
},
tileName: "Patient Profile",
tileIcon: Center(
child: FaIcon(
FontAwesomeIcons.bookMedical,
color: getSec(),
size: 200,
),
),
// Icon(
// Icons.medication,
// color: getSec(),
// size: 200,
// ),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
Navigator.of(context).pushNamed(
'/appointments',
arguments: widget.signedInUser,
);
},
tileName: "Appointments",
tileIcon: Center(
child: FaIcon(
FontAwesomeIcons.calendarDays,
color: getSec(),
size: 200,
),
),
// Icon(
// Icons.calendar_month,
// color: getSec(),
// size: 230,
// ),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
Navigator.of(context).pushNamed(
'/access-review',
arguments: widget.signedInUser,
);
},
tileName: "Access",
tileIcon: Center(
child: FaIcon(
FontAwesomeIcons.userCheck,
color: getSec(),
size: 170,
),
),
// Icon(
// Icons.check_box_outlined,
// color: getSec(),
// size: 200,
// ),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
Navigator.of(context).pushNamed(
'/about',
arguments: widget.signedInUser,
);
},
tileName: "About MIH",
tileIcon: Center(
child: FaIcon(
FontAwesomeIcons.circleInfo,
color: getSec(),
size: 200,
),
),
// Icon(
// Icons.info_outline,
// color: getSec(),
// size: 230,
// ),
p: getPrim(),
s: getSec(),
));
}
void setAppsBusiness(List<MIHTile> tileList) {
tileList.add(MIHTile(
onTap: () {
Navigator.of(context).pushNamed(
'/business-profile/manage',
arguments: BusinessArguments(
widget.signedInUser,
widget.businessUser,
widget.business,
),
);
},
tileName: "Business Profile",
tileIcon: Center(
child: FaIcon(
FontAwesomeIcons.buildingUser,
color: getSec(),
size: 165,
),
),
// Icon(
// Icons.business,
// color: getSec(),
// size: 230,
// ),
p: getPrim(),
s: getSec(),
));
// tileList.add(MIHTile(
// onTap: () {
// Navigator.of(context).pushNamed(
// '/access-review',
// arguments: widget.signedInUser,
// );
// },
// tileName: "Access",
// tileIcon: Icon(
// Icons.check_box_outlined,
// color: getSec(),
// size: 200,
// ),
// p: getPrim(),
// s: getSec(),
// ));
tileList.add(MIHTile(
onTap: () {
Navigator.of(context).pushNamed(
'/patient-manager',
arguments: BusinessArguments(
widget.signedInUser,
widget.businessUser,
widget.business,
),
);
},
tileName: "Manage Patient",
tileIcon: Center(
child: FaIcon(
FontAwesomeIcons.bookMedical,
color: getSec(),
size: 200,
),
),
// Icon(
// Icons.medication,
// color: getSec(),
// size: 230,
// ),
p: getPrim(),
s: getSec(),
));
}
void setAppsDev(List<MIHTile> tileList) {
if (AppEnviroment.getEnv() == "Dev") {
tileList.add(MIHTile(
onTap: () {
showDialog(
barrierColor: const Color(0x01000000),
context: context,
builder: (context) {
return const MIHTest();
},
);
},
tileName: "video - Dev",
tileIcon: Icon(
Icons.video_file,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
showDialog(
barrierColor: const Color(0x01000000),
context: context,
builder: (context) {
return Builder(builder: (context) {
return MIHNotificationMessage(
arguments: NotificationArguments(
"Testing",
"Testing the new MIH Notification",
() {
Navigator.of(context).pop();
//Scaffold.of(context).openEndDrawer();
},
),
);
});
},
);
},
tileName: "Notify - Dev",
tileIcon: Icon(
Icons.notifications,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
},
tileName: "Loading - Dev",
tileIcon: Icon(
Icons.change_circle,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
Navigator.of(context).pushNamed(
'/business-profile/set-up',
arguments: widget.signedInUser,
);
},
tileName: "Setup Bus - Dev",
tileIcon: Icon(
Icons.add_business_outlined,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
Navigator.of(context).pushNamed('/patient-profile/set-up',
arguments: widget.signedInUser);
},
tileName: "Add Pat - Dev",
tileIcon: Icon(
Icons.add_circle_outline,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
showDialog(
context: context,
builder: (context) {
// return const MIHWarningMessage(warningType: "No Access");
return const MIHWarningMessage(warningType: "Expired Access");
},
);
},
tileName: "Warn - Dev",
tileIcon: Icon(
Icons.warning_amber_rounded,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
showDialog(
context: context,
builder: (context) {
// return const MIHErrorMessage(errorType: "Input Error");
// return const MIHErrorMessage(errorType: "Password Requirements");
// return const MIHErrorMessage(errorType: "Invalid Username");
// return const MIHErrorMessage(errorType: "Invalid Email");
// return const MIHErrorMessage(errorType: "User Exists");
// return const MIHErrorMessage(errorType: "Password Match");
// return const MIHErrorMessage(errorType: "Invalid Credentials");
return const MIHErrorMessage(errorType: "Internet Connection");
},
);
},
tileName: "Error - Dev",
tileIcon: Icon(
Icons.error_outline_outlined,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
showDialog(
context: context,
builder: (context) {
return const MIHSuccessMessage(
successType: "Success",
successMessage:
"Congratulations! Your account has been created successfully. You are log in and can start exploring.\n\nPlease note: more apps will appear once you update your profile.");
},
);
},
tileName: "Success - Dev",
tileIcon: Icon(
Icons.check_circle_outline_outlined,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
showDialog(
context: context,
builder: (context) {
// return MIHDeleteMessage(deleteType: "Note", onTap: () {});
return MIHDeleteMessage(deleteType: "File", onTap: () {});
},
);
},
tileName: "Delete - Dev",
tileIcon: Icon(
Icons.delete_forever_outlined,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
// return const MIHErrorMessage(errorType: "Input Error");
// return const MIHErrorMessage(errorType: "Password Requirements");
// return const MIHErrorMessage(errorType: "Invalid Username");
// return const MIHErrorMessage(errorType: "Invalid Email");
// return const MIHErrorMessage(errorType: "User Exists");
// return const MIHErrorMessage(errorType: "Password Match");
// return const MIHErrorMessage(errorType: "Invalid Credentials");
return MIHWindow(
fullscreen: false,
windowTitle:
"Test Window title that is too large for mobile devices",
windowBody: const [
SizedBox(
height: 250,
)
],
windowTools: [
IconButton(
onPressed: () {
//deleteFilePopUp(filePath, fileID);
},
icon: Icon(
Icons.delete,
size: 35,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
),
IconButton(
onPressed: () {
//deleteFilePopUp(filePath, fileID);
},
icon: Icon(
Icons.wallet,
size: 35,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
),
],
onWindowTapClose: () {
Navigator.pop(context);
},
);
},
);
},
tileName: "Window - Dev",
tileIcon: Icon(
Icons.window,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
tileList.add(MIHTile(
onTap: () {
MIHLocationAPI().getGPSPosition(context).then((position) {
if (position != null) {
print(position);
print(
"Distance: ${MIHLocationAPI().getDistanceInMeaters(position, position)}m");
}
});
},
tileName: "Location - Dev",
tileIcon: Icon(
Icons.location_pin,
color: getSec(),
size: 230,
),
p: getPrim(),
s: getSec(),
));
}
}
List<MIHTile> searchApp(List<MIHTile> appList, String searchString) {
if (searchString == "") {
return appList;
} else {
List<MIHTile> temp = [];
for (var item in appList) {
if (item.tileName.toLowerCase().contains(appSearch.toLowerCase())) {
temp.add(item);
}
}
return temp;
}
}
List<List<MIHTile>> setApps(
List<MIHTile> personalTileList, List<MIHTile> businessTileList) {
if (widget.isUserNew) {
setAppsNewPersonal(personalTileList);
} else if (!widget.isBusinessUser) {
setAppsPersonal(personalTileList);
} else if (widget.isBusinessUserNew) {
setAppsPersonal(personalTileList);
setAppsNewBusiness(businessTileList);
} else {
setAppsPersonal(personalTileList);
setAppsBusiness(businessTileList);
}
if (widget.isDevActive) {
setAppsDev(personalTileList);
setAppsDev(businessTileList);
}
return [personalTileList, businessTileList];
}
Color getPrim() {
return MzanziInnovationHub.of(context)!.theme.secondaryColor();
}
Color getSec() {
return MzanziInnovationHub.of(context)!.theme.primaryColor();
}
bool isBusinessUser(AppUser signedInUser) {
if (signedInUser.type == "personal") {
return false;
} else {
return true;
}
}
String getHeading(int index) {
if (index == 0) {
return "Personal Apps";
} else {
return "Business Apps";
}
}
void onDragStart(DragStartDetails startDrag) {
Scaffold.of(context).openDrawer();
print(startDrag.globalPosition.dx);
}
Widget getActionButton() {
return Builder(builder: (context) {
return MIHAction(
icon: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: MIHProfilePicture(
profilePictureFile: widget.propicFile,
proPicController: proPicController,
proPic: null,
width: 45,
radius: 21,
drawerMode: false,
editable: false,
onChange: (newProPic) {},
),
),
// const Icon(Icons.apps),
iconSize: 45,
onTap: () {
setState(() {
appSearch = "";
searchController.clear();
});
//key.currentState.o
Scaffold.of(context).openDrawer();
},
);
});
}
Widget getSecondaryActionButton() {
Widget notIIcon;
if (hasNewNotifications()) {
notIIcon = Stack(
children: [
const Icon(Icons.notifications),
Positioned(
right: 0,
top: 0,
child: Icon(
Icons.circle,
size: 10,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
)
],
);
} else {
notIIcon = const Icon(Icons.notifications);
}
return Builder(builder: (context) {
return MIHAction(
icon: notIIcon,
iconSize: 35,
onTap: () {
setState(() {
appSearch = "";
searchController.clear();
});
//key.currentState.o
Scaffold.of(context).openEndDrawer();
},
);
});
}
MIHHeader getHeader() {
return const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"Mzanzi Innovation Hub",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
],
);
}
MIHBody getBody(double width, double height) {
return MIHBody(
borderOn: false,
bodyItems: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
flex: 4,
child: KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
setState(() {
appSearch = searchController.text;
});
}
},
child: SizedBox(
child: MIHSearchField(
controller: searchController,
hintText: "Search Mzansi Apps",
required: false,
editable: true,
onTap: () {
setState(() {
appSearch = searchController.text;
});
},
),
),
),
),
Flexible(
flex: 1,
child: IconButton(
//padding: const EdgeInsets.all(0),
onPressed: () {
setState(() {
appSearch = "";
searchController.clear();
});
},
icon: const Icon(
Icons.filter_alt_off,
size: 30,
),
),
),
],
),
const SizedBox(height: 10),
GridView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.only(
left: width / 10,
right: width / 10,
//bottom: height / 5,
//top: 20,
),
// physics: ,
// shrinkWrap: true,
itemCount: searchApp(pbswitch[_selectedIndex], appSearch).length,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
mainAxisSpacing: 15, maxCrossAxisExtent: 200),
itemBuilder: (context, index) {
return searchApp(pbswitch[_selectedIndex], appSearch)[index];
},
),
],
);
}
MIHAppDrawer getActionDrawer() {
return MIHAppDrawer(
signedInUser: widget.signedInUser,
propicFile: widget.propicFile,
);
}
MIHNotificationDrawer getSecondaryActionDrawer() {
return MIHNotificationDrawer(
signedInUser: widget.signedInUser,
notifications: widget.notifications,
);
}
Widget getBottomNavBar() {
return Visibility(
visible: isBusinessUser(widget.signedInUser),
child: Padding(
padding: const EdgeInsets.only(
left: 10.0, right: 10.0, bottom: 10.0, top: 0),
child: GNav(
//hoverColor: Colors.lightBlueAccent,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
iconSize: 35.0,
activeColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
tabBackgroundColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
//gap: 20,
//padding: EdgeInsets.all(15),
tabs: [
GButton(
icon: Icons.perm_identity,
text: "Personal",
onPressed: () {
setState(() {
_selectedIndex = 0;
});
},
),
GButton(
icon: Icons.business_center,
text: "Business",
onPressed: () {
setState(() {
_selectedIndex = 1;
});
},
),
],
selectedIndex: _selectedIndex,
),
),
);
}
bool hasNewNotifications() {
//print(widget.notifications.toString());
if (notifiList.map((item) => item.notification_read).contains("No")) {
//print("New Notification Available");
return true;
} else {
//print("No New Notification Available");
return false;
}
}
Future<void> refreshNotifications() async {
html.window.location.reload();
// var responseNotification = await http.get(Uri.parse(
// "$baseAPI/notifications/${widget.signedInUser.app_id}?amount=$amount"));
// List<MIHNotification> notifi;
// if (responseNotification.statusCode == 200) {
// String body = responseNotification.body;
// // var decodedData = jsonDecode(body);
// // MIHNotification notifications = MIHNotification.fromJson(decodedData);
// Iterable l = jsonDecode(body);
// //print("Here2");
// List<MIHNotification> notifications = List<MIHNotification>.from(
// l.map((model) => MIHNotification.fromJson(model)));
// notifi = notifications;
// } else {
// notifi = [];
// }
// setState(() {
// notifiList = notifi;
// });
// notificationPopUp();
}
void notificationPopUp() {
if (hasNewNotifications()) {
showDialog(
barrierColor: const Color(0x01000000),
context: context,
builder: (context) {
return MIHNotificationMessage(
arguments: NotificationArguments(
"Unread Notifications",
"You have unread notifications waiting for you.",
() {
Navigator.of(context).pop();
//Scaffold.of(context).openEndDrawer();
},
),
);
},
);
}
}
@override
void dispose() {
searchController.dispose();
proPicController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
setState(() {
pbswitch = setApps(persHTList, busHTList);
businessUserSwitch = false;
notifiList = widget.notifications;
});
if (!widget.personalSelected) {
setState(() {
_selectedIndex = 1;
});
}
WidgetsBinding.instance.addPostFrameCallback((_) {
notificationPopUp();
});
}
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.sizeOf(context);
final double width = size.width;
final double height = size.height;
return SwipeDetector(
onSwipeLeft: (offset) {
if (_selectedIndex == 0) {
setState(() {
_selectedIndex = 1;
});
}
//print("swipe left");
},
onSwipeRight: (offset) {
if (_selectedIndex == 1) {
setState(() {
_selectedIndex = 0;
});
}
//print("swipe right");
},
child: MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(),
secondaryActionButton: getSecondaryActionButton(),
body: getBody(width, height),
actionDrawer: getActionDrawer(),
secondaryActionDrawer: getSecondaryActionDrawer(),
bottomNavBar: getBottomNavBar(),
pullDownToRefresh: true,
onPullDown: refreshNotifications,
),
);
}
}

View File

@@ -0,0 +1,155 @@
import 'package:flutter/material.dart';
import '../../mih_apis/mih_api_calls.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/arguments.dart';
import '../../mih_objects/business_user.dart';
import 'mih_home.dart';
class MIHProfileGetter extends StatefulWidget {
final bool personalSelected;
const MIHProfileGetter({
super.key,
required this.personalSelected,
});
@override
State<MIHProfileGetter> createState() => _MIHProfileGetterState();
}
class _MIHProfileGetterState extends State<MIHProfileGetter> {
String useremail = "";
int amount = 10;
final baseAPI = AppEnviroment.baseApiUrl;
late Future<HomeArguments> profile;
String proPicUrl = "empty";
ImageProvider<Object>? propicFile;
ImageProvider<Object>? isPictureAvailable(String url) {
if (url == "") {
return const AssetImage('images/i-dont-know-2.png');
} else if (url != "") {
return NetworkImage(url);
} else {
return null;
}
}
bool isUserNew(AppUser signedInUser) {
if (signedInUser.fname == "") {
return true;
} else {
return false;
}
}
bool isDevActive() {
if (AppEnviroment.getEnv() == "Dev") {
return true;
} else {
return false;
}
}
bool isBusinessUser(AppUser signedInUser) {
if (signedInUser.type == "personal") {
return false;
} else {
return true;
}
}
bool isBusinessUserNew(BusinessUser? businessUser) {
if (businessUser == null) {
return true;
} else {
return false;
}
}
// <a href="https://www.flaticon.com/free-icons/dont-know" title="dont know icons">Dont know icons created by Freepik - Flaticon</a>
@override
void dispose() {
// TODO: implement dispose
super.dispose();
}
@override
void initState() {
//profile = getProfile();
profile = MIHApiCalls().getProfile(amount);
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: profile,
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
return MIHHome(
signedInUser: snapshot.requireData.signedInUser,
businessUser: snapshot.data!.businessUser,
business: snapshot.data!.business,
notifications: snapshot.data!.notifi,
propicFile: isPictureAvailable(snapshot.data!.profilePicUrl),
isUserNew: isUserNew(snapshot.requireData.signedInUser),
isBusinessUser: isBusinessUser(snapshot.requireData.signedInUser),
isBusinessUserNew: isBusinessUserNew(snapshot.data!.businessUser),
isDevActive: isDevActive(),
personalSelected: widget.personalSelected,
);
} else {
return MIHLayoutBuilder(
actionButton: MIHAction(
icon: const Icon(Icons.refresh),
iconSize: 35,
onTap: () {
Navigator.of(context).popAndPushNamed("/");
},
),
header: const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"Mzanzi Innovation Hub",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
],
),
secondaryActionButton: null,
body: MIHBody(
borderOn: false,
bodyItems: [
Center(
child: Text(
'${snapshot.error} occurred',
style: const TextStyle(fontSize: 18),
),
),
],
),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
}
}
return const Mihloadingcircle();
},
);
}
}

View File

@@ -0,0 +1,419 @@
import 'dart:convert';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import 'package:http/http.dart' as http2;
import 'package:supertokens_flutter/supertokens.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_file_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_components/mih_profile_picture.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/arguments.dart';
class ProfileUserUpdate extends StatefulWidget {
final AppProfileUpdateArguments arguments;
// final AppUser signedInUser;
// final ImageProvider<Object>? propicFile;
const ProfileUserUpdate({
super.key,
required this.arguments,
});
@override
State<ProfileUserUpdate> createState() => _ProfileUserUpdateState();
}
class _ProfileUserUpdateState extends State<ProfileUserUpdate> {
final proPicController = TextEditingController();
final usernameController = TextEditingController();
final fnameController = TextEditingController();
final lnameController = TextEditingController();
PlatformFile? proPic;
late ImageProvider<Object>? propicPreview;
late bool businessUser;
final FocusNode _focusNode = FocusNode();
late String oldProPicName;
Future<void> 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: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{"file_path": filePath}),
);
//print("Here4");
//print(response.statusCode);
if (response.statusCode == 200) {
//SQL delete
} else {
internetConnectionPopUp();
}
}
Future<String> getFileUrlApiCall(String filePath) async {
if (widget.arguments.signedInUser.pro_pic_path == "") {
return "";
} else if (AppEnviroment.getEnv() == "Dev") {
return "${AppEnviroment.baseFileUrl}/mih/$filePath";
} else {
var url = "${AppEnviroment.baseApiUrl}/minio/pull/file/$filePath/prod";
var response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
String body = response.body;
var decodedData = jsonDecode(body);
return decodedData['minioURL'];
} else {
throw Exception(
"Error: GetUserData status code ${response.statusCode}");
}
}
}
Future<void> 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 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) {
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();
}
}
bool isFieldsFilled() {
if (fnameController.text.isEmpty ||
lnameController.text.isEmpty ||
usernameController.text.isEmpty) {
return false;
} else {
return true;
}
}
Future<void> updateUserApiCall() async {
var fname = proPicController.text.replaceAll(RegExp(r' '), '-');
var filePath =
"${widget.arguments.signedInUser.app_id}/profile_files/$fname";
var profileType;
if (businessUser) {
profileType = "business";
} else {
profileType = "personal";
}
if (isUsernameValid(usernameController.text) == false) {
usernamePopUp();
} else {
var response = await http.put(
Uri.parse("${AppEnviroment.baseApiUrl}/user/update/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"idusers": widget.arguments.signedInUser.idUser,
"username": usernameController.text,
"fnam": fnameController.text,
"lname": lnameController.text,
"type": profileType,
"pro_pic_path": filePath,
}),
);
//print("Here4");
//print(response.statusCode);
if (response.statusCode == 200) {
Navigator.of(context)
.popAndPushNamed('/', arguments: widget.arguments.signedInUser);
String message =
"${widget.arguments.signedInUser.email}'s information has been updated successfully!";
successPopUp(message);
} else {
internetConnectionPopUp();
}
}
}
bool isBusinessUser() {
if (widget.arguments.signedInUser.type == "personal") {
return false;
} else {
return true;
}
}
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,
);
},
);
}
void usernamePopUp() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Invalid Username");
},
);
}
bool isUsernameValid(String username) {
return RegExp(r'^(?=[a-zA-Z0-9._]{8,20}$)(?!.*[_.]{2})[^_.].*[^_.]$')
.hasMatch(username);
}
Future<void> submitForm() async {
if (isFieldsFilled()) {
if (oldProPicName != proPicController.text) {
await uploadSelectedFile(proPic);
}
await updateUserApiCall();
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
}
MIHAction getActionButton() {
return MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).popAndPushNamed(
'/',
arguments: true,
);
},
);
}
MIHHeader getHeader() {
return const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"Mzansi Profile",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: false,
bodyItems: [
//displayProPic(),
MIHProfilePicture(
profilePictureFile: widget.arguments.propicFile,
proPicController: proPicController,
proPic: proPic,
width: 155,
radius: 70,
drawerMode: false,
editable: true,
onChange: (newProPic) {
setState(() {
proPic = newProPic;
});
},
),
const SizedBox(height: 25.0),
Visibility(
visible: false,
child: MIHFileField(
controller: proPicController,
hintText: "Profile Picture",
editable: false,
required: false,
onPressed: () async {
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['jpg', 'png'],
);
if (result == null) return;
final selectedFile = result.files.first;
setState(() {
proPic = selectedFile;
propicPreview = MemoryImage(proPic!.bytes!);
});
setState(() {
proPicController.text = selectedFile.name;
});
},
),
),
const SizedBox(height: 10.0),
MIHTextField(
controller: usernameController,
hintText: "Username",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: fnameController,
hintText: "First Name",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: lnameController,
hintText: "Last Name",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text(
"Activate Business Account",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
const SizedBox(
width: 10,
),
Switch(
value: businessUser,
onChanged: (bool value) {
setState(() {
businessUser = value;
});
},
),
],
),
const SizedBox(height: 30.0),
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();
},
),
),
],
);
}
@override
void dispose() {
proPicController.dispose();
usernameController.dispose();
fnameController.dispose();
lnameController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
var proPicName = "";
if (widget.arguments.signedInUser.pro_pic_path.isNotEmpty) {
proPicName = widget.arguments.signedInUser.pro_pic_path.split("/").last;
}
setState(() {
propicPreview = widget.arguments.propicFile;
oldProPicName = proPicName;
proPicController.text = proPicName;
fnameController.text = widget.arguments.signedInUser.fname;
lnameController.text = widget.arguments.signedInUser.lname;
usernameController.text = widget.arguments.signedInUser.username;
businessUser = isBusinessUser();
});
super.initState();
}
@override
Widget build(BuildContext context) {
return MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(),
secondaryActionButton: null,
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
}
}

View File

@@ -0,0 +1,96 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/arguments.dart';
import '../../mih_objects/patients.dart';
import 'patient_add.dart';
import 'patient_view.dart';
class AddOrViewPatient extends StatefulWidget {
//final AppUser signedInUser;
final PatientViewArguments arguments;
const AddOrViewPatient({
super.key,
required this.arguments,
});
@override
State<AddOrViewPatient> createState() => _AddOrViewPatientState();
}
class _AddOrViewPatientState extends State<AddOrViewPatient> {
late double width;
late double height;
late Widget loading;
Future<Patient?> fetchPatient() async {
//print("Patien manager page: $endpoint");
final response = await http.get(Uri.parse(
"${AppEnviroment.baseApiUrl}/patients/${widget.arguments.signedInUser.app_id}"));
// print("Here");
// print("Body: ${response.body}");
// print("Code: ${response.statusCode}");
// var errorCode = response.statusCode.toString();
// var errorBody = response.body;
if (response.statusCode == 200) {
// print("Here1");
var decodedData = jsonDecode(response.body);
// print("Here2");
Patient patients = Patient.fromJson(decodedData as Map<String, dynamic>);
// print("Here3");
// print(patients);
return patients;
}
return null;
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
setState(() {
width = size.width;
height = size.height;
});
return FutureBuilder(
future: fetchPatient(),
builder: (ctx, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
// Extracting data from snapshot object
//final data = snapshot.data as String;
return PatientView(
arguments: PatientViewArguments(
widget.arguments.signedInUser,
snapshot.requireData,
null,
null,
widget.arguments.type,
));
} else if (snapshot.connectionState == ConnectionState.waiting) {
loading = Container(
width: width,
height: height,
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
child: const Mihloadingcircle(),
);
return loading;
} else {
return AddPatient(signedInUser: widget.arguments.signedInUser);
}
},
);
}
}

View File

@@ -0,0 +1,183 @@
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_core/theme.dart';
import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';
import "package:universal_html/html.dart" as html;
import 'package:http/http.dart' as http;
import '../../../main.dart';
import '../../../mih_objects/arguments.dart';
class BuildFileView extends StatefulWidget {
final String link;
final String path;
const BuildFileView({
super.key,
required this.link,
required this.path,
});
@override
State<BuildFileView> createState() => _BuildFileViewState();
}
class _BuildFileViewState extends State<BuildFileView> {
late PdfViewerController pdfViewerController = PdfViewerController();
//late TextEditingController currentPageController = TextEditingController();
double startZoomLevel = 1;
String getExtType(String path) {
//print(pdfLink.split(".")[1]);
return path.split(".").last;
}
String getFileName(String path) {
//print(pdfLink.split(".")[1]);
return path.split("/").last;
}
void printDocument() async {
print("Printing ${widget.path.split("/").last}");
http.Response response = await http.get(Uri.parse(widget.link));
var pdfData = response.bodyBytes;
Navigator.of(context).pushNamed(
'/file-veiwer/print-preview',
arguments: PrintPreviewArguments(
pdfData,
getFileName(
widget.path,
),
),
);
}
@override
void dispose() {
pdfViewerController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// double width = MediaQuery.sizeOf(context).width;
//double height = MediaQuery.sizeOf(context).height;
if (getExtType(widget.path).toLowerCase() == "pdf") {
//print(widget.pdfLink);
return Expanded(
child: Stack(
fit: StackFit.expand,
children: [
SfPdfViewerTheme(
data: SfPdfViewerThemeData(
backgroundColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
child: SfPdfViewer.network(
widget.link,
controller: pdfViewerController,
interactionMode: PdfInteractionMode.pan,
),
),
Positioned(
bottom: 10,
right: 10,
width: 50,
height: 50,
child: IconButton.filled(
iconSize: 35,
padding: const EdgeInsets.all(0),
onPressed: () {
printDocument();
},
icon: Icon(
Icons.print,
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
),
Positioned(
bottom: 10,
left: 10,
width: 50,
height: 50,
child: IconButton.filled(
iconSize: 35,
padding: const EdgeInsets.all(0),
onPressed: () {
html.window.open(
widget.link,
// '${AppEnviroment.baseFileUrl}/mih/$filePath',
'download');
},
icon: Icon(
Icons.download,
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
),
],
),
);
} else {
return Expanded(
// height: height,
// padding: const EdgeInsets.all(10.0),
child: Stack(
fit: StackFit.expand,
children: [
InteractiveViewer(
//constrained: true,
//clipBehavior: Clip.antiAlias,
maxScale: 5.0,
//minScale: 0.,
child: Image.network(widget.link),
),
Positioned(
bottom: 10,
right: 10,
width: 50,
height: 50,
child: IconButton.filled(
iconSize: 35,
padding: const EdgeInsets.all(0),
onPressed: () {
//expandImage(width, height);
Navigator.of(context).pushNamed(
'/file-veiwer',
arguments: FileViewArguments(
widget.link,
widget.path,
),
);
},
icon: Icon(
Icons.fullscreen,
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
),
Positioned(
bottom: 10,
left: 10,
width: 50,
height: 50,
child: IconButton.filled(
iconSize: 35,
padding: const EdgeInsets.all(0),
onPressed: () {
html.window.open(
widget.link,
// '${AppEnviroment.baseFileUrl}/mih/$filePath',
'download');
},
icon: Icon(
Icons.download,
color: MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
),
],
),
);
}
}
}

View File

@@ -0,0 +1,398 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../../main.dart';
import '../../../mih_components/mih_layout/mih_window.dart';
import '../../../mih_components/mih_pop_up_messages/mih_delete_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../../mih_env/env.dart';
import '../../../mih_objects/app_user.dart';
import '../../../mih_objects/arguments.dart';
import '../../../mih_objects/business.dart';
import '../../../mih_objects/business_user.dart';
import '../../../mih_objects/files.dart';
import '../../../mih_objects/patients.dart';
import 'build_file_view.dart';
class BuildFilesList extends StatefulWidget {
final AppUser signedInUser;
final List<PFile> files;
final Patient selectedPatient;
final Business? business;
final BusinessUser? businessUser;
final String type;
const BuildFilesList({
super.key,
required this.files,
required this.signedInUser,
required this.selectedPatient,
required this.business,
required this.businessUser,
required this.type,
});
@override
State<BuildFilesList> createState() => _BuildFilesListState();
}
class _BuildFilesListState extends State<BuildFilesList> {
int indexOn = 0;
final baseAPI = AppEnviroment.baseApiUrl;
final basefile = AppEnviroment.baseFileUrl;
String fileUrl = "";
Future<String> getFileUrlApiCall(String filePath) async {
var url = "$baseAPI/minio/pull/file/${AppEnviroment.getEnv()}/$filePath";
//print(url);
var response = await http.get(Uri.parse(url));
// print("here1");
//print(response.statusCode);
if (response.statusCode == 200) {
//print("here2");
String body = response.body;
//print(body);
//print("here3");
var decodedData = jsonDecode(body);
//print("Dedoced: ${decodedData['minioURL']}");
return decodedData['minioURL'];
//AppUser u = AppUser.fromJson(decodedData);
// print(u.email);
//return "AlometThere";
} else {
throw Exception("Error: GetUserData status code ${response.statusCode}");
}
//print(url);
// var response = await http.get(Uri.parse(url));
// // print("here1");
// //print(response.statusCode);
// if (response.statusCode == 200) {
// //print("here2");
// String body = response.body;
// //print(body);
// //print("here3");
// var decodedData = jsonDecode(body);
// //print("Dedoced: ${decodedData['minioURL']}");
// return decodedData['minioURL'];
// //AppUser u = AppUser.fromJson(decodedData);
// // print(u.email);
// //return "AlometThere";
// } else {
// throw Exception("Error: GetUserData status code ${response.statusCode}");
// }
}
Future<void> deleteFileApiCall(String filePath, int fileID) async {
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
// delete file from minio
var response = await http.delete(
Uri.parse("$baseAPI/minio/delete/file/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{"file_path": filePath}),
);
//print("Here4");
//print(response.statusCode);
if (response.statusCode == 200) {
//SQL delete
var response2 = await http.delete(
Uri.parse("$baseAPI/files/delete/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{"idpatient_files": fileID}),
);
if (response2.statusCode == 200) {
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pop();
//print(widget.business);
if (widget.business == null) {
Navigator.of(context).pushNamed('/patient-manager/patient',
arguments: PatientViewArguments(
widget.signedInUser,
widget.selectedPatient,
widget.businessUser,
widget.business,
"personal"));
} else {
Navigator.of(context).pushNamed('/patient-manager/patient',
arguments: PatientViewArguments(
widget.signedInUser,
widget.selectedPatient,
widget.businessUser,
widget.business,
"business"));
}
// Navigator.of(context)
// .pushNamed('/patient-profile', arguments: widget.signedInUser);
// setState(() {});
String message =
"The File has been deleted successfully. This means it will no longer be visible on your and cannot be used for future appointments.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
} 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,
);
},
);
}
void deleteFilePopUp(String filePath, int fileID) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHDeleteMessage(
deleteType: "File",
onTap: () async {
await deleteFileApiCall(filePath, fileID);
},
),
);
}
void viewFilePopUp(String fileName, String filePath, int fileID, String url) {
bool hasAccessToDelete = false;
if (widget.type == "business") {
hasAccessToDelete = true;
}
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: true,
windowTitle: fileName,
windowBody: [
BuildFileView(
link: url,
path: filePath,
//pdfLink: '${AppEnviroment.baseFileUrl}/mih/$filePath',
),
const SizedBox(
height: 10,
)
],
windowTools: [
Visibility(
visible: hasAccessToDelete,
child: IconButton(
onPressed: () {
deleteFilePopUp(filePath, fileID);
},
icon: Icon(
size: 35,
Icons.delete,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
),
],
onWindowTapClose: () {
Navigator.pop(context);
},
),
);
// showDialog(
// context: context,
// barrierDismissible: false,
// builder: (context) => Dialog(
// child: Stack(
// children: [
// Container(
// padding: const EdgeInsets.all(10.0),
// width: 800.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: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// const SizedBox(
// height: 25,
// ),
// Text(
// fileName,
// textAlign: TextAlign.center,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// fontSize: 35.0,
// fontWeight: FontWeight.bold,
// ),
// ),
// const SizedBox(height: 25.0),
// Expanded(
// child: BuildFileView(
// link: url,
// path: filePath,
// //pdfLink: '${AppEnviroment.baseFileUrl}/mih/$filePath',
// )),
// const SizedBox(height: 30.0),
// SizedBox(
// width: 300,
// height: 50,
// child: MIHButton(
// onTap: () {
// html.window.open(
// url,
// // '${AppEnviroment.baseFileUrl}/mih/$filePath',
// 'download');
// },
// buttonText: "Dowload",
// buttonColor: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// textColor:
// MzanziInnovationHub.of(context)!.theme.primaryColor(),
// ),
// )
// ],
// ),
// ),
// 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,
// ),
// ),
// ),
// Positioned(
// top: 5,
// left: 5,
// width: 50,
// height: 50,
// child: IconButton(
// onPressed: () {
// deleteFilePopUp(filePath, fileID);
// },
// icon: Icon(
// Icons.delete,
// color:
// MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// ),
// ),
// ),
// ],
// ),
// ),
// );
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
}
@override
Widget build(BuildContext context) {
if (widget.files.isNotEmpty) {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
);
},
itemCount: widget.files.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
widget.files[index].file_name,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
subtitle: Text(
widget.files[index].insert_date,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
// trailing: Icon(
// Icons.arrow_forward,
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// ),
onTap: () async {
await getFileUrlApiCall(widget.files[index].file_path)
.then((urlHere) {
//print(url);
setState(() {
fileUrl = urlHere;
});
});
viewFilePopUp(
widget.files[index].file_name,
widget.files[index].file_path,
widget.files[index].idpatient_files,
fileUrl);
},
);
},
);
} else {
return const Center(
child: Text(
"No Documents Available",
style: TextStyle(fontSize: 25, color: Colors.grey),
textAlign: TextAlign.center,
),
);
}
}
}

View File

@@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import '../../../main.dart';
import '../../../mih_objects/medicine.dart';
class BuildMedicinesList extends StatefulWidget {
final TextEditingController contoller;
final List<Medicine> medicines;
//final searchString;
const BuildMedicinesList({
super.key,
required this.contoller,
required this.medicines,
//required this.searchString,
});
@override
State<BuildMedicinesList> createState() => _BuildMedicinesListState();
}
class _BuildMedicinesListState extends State<BuildMedicinesList> {
int indexOn = 0;
@override
void dispose() {
// TODO: implement dispose
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.separated(
separatorBuilder: (BuildContext context, int index) {
return Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
);
},
itemCount: widget.medicines.length,
itemBuilder: (context, index) {
//final patient = widget.patients[index].id_no.contains(widget.searchString);
return ListTile(
title: Text(
widget.medicines[index].name,
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
subtitle: Text(
"${widget.medicines[index].unit} - ${widget.medicines[index].form}",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
onTap: () {
setState(() {
widget.contoller.text =
"${widget.medicines[index].name}%t${widget.medicines[index].unit}%t${widget.medicines[index].form}";
Navigator.of(context).pop();
});
},
trailing: Icon(
Icons.arrow_forward,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
);
},
);
}
}

View File

@@ -0,0 +1,397 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../../main.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_multiline_text_input.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../../mih_components/mih_layout/mih_window.dart';
import '../../../mih_components/mih_pop_up_messages/mih_delete_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../../mih_env/env.dart';
import '../../../mih_objects/app_user.dart';
import '../../../mih_objects/arguments.dart';
import '../../../mih_objects/business.dart';
import '../../../mih_objects/business_user.dart';
import '../../../mih_objects/notes.dart';
import '../../../mih_objects/patients.dart';
class BuildNotesList extends StatefulWidget {
final AppUser signedInUser;
final List<Note> notes;
final Patient selectedPatient;
final Business? business;
final BusinessUser? businessUser;
final String type;
const BuildNotesList({
super.key,
required this.notes,
required this.signedInUser,
required this.selectedPatient,
required this.business,
required this.businessUser,
required this.type,
});
@override
State<BuildNotesList> createState() => _BuildNotesListState();
}
class _BuildNotesListState extends State<BuildNotesList> {
final noteTitleController = TextEditingController();
final noteTextController = TextEditingController();
final businessNameController = TextEditingController();
final userNameController = TextEditingController();
final dateController = TextEditingController();
int indexOn = 0;
final baseAPI = AppEnviroment.baseApiUrl;
Future<void> deleteNoteApiCall(int NoteId) async {
var response = await http.delete(
Uri.parse("$baseAPI/notes/delete/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{"idpatient_notes": NoteId}),
);
//print("Here4");
//print(response.statusCode);
if (response.statusCode == 200) {
Navigator.of(context).pop();
Navigator.of(context).pop();
if (widget.business == null) {
Navigator.of(context).pushNamed('/patient-manager/patient',
arguments: PatientViewArguments(
widget.signedInUser,
widget.selectedPatient,
widget.businessUser,
widget.business,
"personal"));
} else {
Navigator.of(context).pushNamed('/patient-manager/patient',
arguments: PatientViewArguments(
widget.signedInUser,
widget.selectedPatient,
widget.businessUser,
widget.business,
"business"));
}
setState(() {});
String message =
"The note has been deleted successfully. This means it will no longer be visible on your and cannot be used for future appointments.";
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,
);
},
);
}
void deletePatientPopUp(int NoteId) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHDeleteMessage(
deleteType: "Note",
onTap: () {
deleteNoteApiCall(NoteId);
},
),
);
}
void viewNotePopUp(Note selectednote) {
setState(() {
noteTitleController.text = selectednote.note_name;
noteTextController.text = selectednote.note_text;
businessNameController.text = selectednote.doc_office;
userNameController.text = selectednote.doctor;
dateController.text = selectednote.insert_date;
});
bool hasAccessToDelete = false;
if (widget.type == "business" &&
selectednote.doc_office == widget.business!.Name) {
hasAccessToDelete = true;
}
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: true,
windowTitle: selectednote.note_name,
windowTools: [
Visibility(
visible: hasAccessToDelete,
child: IconButton(
onPressed: () {
deletePatientPopUp(selectednote.idpatient_notes);
},
icon: Icon(
Icons.delete,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
),
],
onWindowTapClose: () {
Navigator.pop(context);
},
windowBody: [
MIHTextField(
controller: businessNameController,
hintText: "Office",
editable: false,
required: false,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: userNameController,
hintText: "Created By",
editable: false,
required: false,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: dateController,
hintText: "Created Date",
editable: false,
required: false,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: noteTitleController,
hintText: "Note Title",
editable: false,
required: false,
),
const SizedBox(height: 10.0),
Expanded(
child: MIHMLTextField(
controller: noteTextController,
hintText: "Note Details",
editable: false,
required: false,
),
),
],
),
);
// showDialog(
// context: context,
// barrierDismissible: false,
// builder: (context) => Dialog(
// child: Stack(
// children: [
// Container(
// padding: const EdgeInsets.all(15.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: Column(
// mainAxisSize: MainAxisSize.max,
// children: [
// const SizedBox(
// height: 25,
// ),
// Text(
// selectednote.note_name,
// textAlign: TextAlign.center,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// fontSize: 35.0,
// fontWeight: FontWeight.bold,
// ),
// ),
// const SizedBox(height: 25.0),
// SizedBox(
// width: 700,
// child: MIHTextField(
// controller: businessNameController,
// hintText: "Office",
// editable: false,
// required: false,
// ),
// ),
// const SizedBox(height: 10.0),
// SizedBox(
// width: 700,
// child: MIHTextField(
// controller: userNameController,
// hintText: "Created By",
// editable: false,
// required: false,
// ),
// ),
// const SizedBox(height: 10.0),
// SizedBox(
// width: 700,
// child: MIHTextField(
// controller: dateController,
// hintText: "Created Date",
// editable: false,
// required: false,
// ),
// ),
// const SizedBox(height: 10.0),
// SizedBox(
// width: 700,
// child: MIHTextField(
// controller: noteTitleController,
// hintText: "Note Title",
// editable: false,
// required: false,
// ),
// ),
// const SizedBox(height: 10.0),
// Expanded(
// child: MIHMLTextField(
// controller: noteTextController,
// hintText: "Note Details",
// editable: false,
// required: false,
// ),
// ),
// //const SizedBox(height: 25.0),
// // SizedBox(
// // width: 300,
// // height: 100,
// // child: MIHButton(
// // onTap: () {
// // Navigator.pop(context);
// // },
// // buttonText: "Close",
// // buttonColor: Colors.blueAccent,
// // textColor: Colors.white,
// // ),
// // )
// ],
// ),
// ),
// 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,
// ),
// ),
// ),
// Positioned(
// top: 5,
// left: 5,
// width: 50,
// height: 50,
// child: IconButton(
// onPressed: () {
// deletePatientPopUp(selectednote.idpatient_notes);
// },
// icon: Icon(
// Icons.delete,
// color:
// MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// ),
// ),
// ),
// ],
// ),
// ),
// );
}
@override
void dispose() {
noteTextController.dispose();
businessNameController.dispose();
userNameController.dispose();
dateController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (widget.notes.isNotEmpty) {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
);
},
itemCount: widget.notes.length,
itemBuilder: (context, index) {
String notePreview = widget.notes[index].note_text;
if (notePreview.length > 30) {
notePreview = "${notePreview.substring(0, 30)} ...";
}
return ListTile(
title: Text(
"${widget.notes[index].note_name}\n${widget.notes[index].doc_office} - ${widget.notes[index].doctor}",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
subtitle: Text(
"${widget.notes[index].insert_date}:\n$notePreview",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
), //Text(widget.notes[index].note_text),
trailing: Icon(
Icons.arrow_forward,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
onTap: () {
viewNotePopUp(widget.notes[index]);
},
);
},
);
} else {
return const Center(
child: Text(
"No Notes Available",
style: TextStyle(fontSize: 25, color: Colors.grey),
textAlign: TextAlign.center,
),
);
}
}
}

View File

@@ -0,0 +1,452 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../../main.dart';
import '../../../mih_apis/mih_api_calls.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_date_input.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_time_input.dart';
import '../../../mih_components/mih_layout/mih_window.dart';
import '../../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_warning_message.dart';
import '../../../mih_env/env.dart';
import '../../../mih_objects/app_user.dart';
import '../../../mih_objects/arguments.dart';
import '../../../mih_objects/business.dart';
import '../../../mih_objects/patient_access.dart';
import '../../../mih_objects/patients.dart';
class BuildPatientAccessList extends StatefulWidget {
final List<PatientAccess> patientAccesses;
final AppUser signedInUser;
final Business? business;
final BusinessArguments arguments;
const BuildPatientAccessList({
super.key,
required this.patientAccesses,
required this.signedInUser,
required this.business,
required this.arguments,
});
@override
State<BuildPatientAccessList> createState() => _BuildPatientsListState();
}
class _BuildPatientsListState extends State<BuildPatientAccessList> {
TextEditingController dateController = TextEditingController();
TextEditingController timeController = TextEditingController();
TextEditingController idController = TextEditingController();
TextEditingController fnameController = TextEditingController();
TextEditingController lnameController = TextEditingController();
final baseAPI = AppEnviroment.baseApiUrl;
Future<void> addPatientAppointmentAPICall(int index) async {
var response = await http.post(
Uri.parse("$baseAPI/queue/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": widget.business!.business_id,
"app_id": widget.patientAccesses[index].app_id,
"date": dateController.text,
"time": timeController.text,
"access": "pending",
}),
);
if (response.statusCode == 201) {
// Navigator.pushNamed(context, '/patient-manager/patient',
// arguments: widget.signedInUser);
String message =
"The appointment has been successfully booked!\n\nAn approval request as been sent to the patient.Once the access request has been approved, you will be able to access the patientAccesses profile. ou can check the status of your request in patient queue under the appointment.";
// "${fnameController.text} ${lnameController.text} patient profiole has been successfully added!\n";
Navigator.pop(context);
Navigator.pop(context);
setState(() {
dateController.text = "";
timeController.text = "";
});
Navigator.of(context).pushNamed(
'/patient-manager',
arguments: BusinessArguments(
widget.arguments.signedInUser,
widget.arguments.businessUser,
widget.arguments.business,
),
);
successPopUp(message);
addAccessReviewNotificationAPICall(index);
} else {
internetConnectionPopUp();
}
}
Future<void> addAccessReviewNotificationAPICall(int index) async {
var response = await http.post(
Uri.parse("$baseAPI/notifications/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"app_id": widget.patientAccesses[index].app_id,
"notification_type": "New Appointment Booked",
"notification_message":
"A new Appointment has been booked by ${widget.arguments.business!.Name} for the ${dateController.text} ${timeController.text}. Please approve the Access Review request.",
"action_path": "/access-review",
}),
);
if (response.statusCode == 201) {
// Navigator.pushNamed(context, '/patient-manager/patient',
// arguments: widget.signedInUser);
} 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,
);
},
);
}
void submitApointment(int index) {
MIHApiCalls.addAppointmentAPICall(
widget.arguments.business!.business_id,
widget.patientAccesses[index].app_id,
dateController.text,
timeController.text,
widget.arguments,
context,
);
//addPatientAppointmentAPICall(index);
}
bool isAppointmentFieldsFilled() {
if (dateController.text.isEmpty || timeController.text.isEmpty) {
return false;
} else {
return true;
}
}
void appointmentPopUp(int index) {
var firstLetterFName = widget.patientAccesses[index].fname;
var firstLetterLName = widget.patientAccesses[index].lname;
setState(() {
idController.text = widget.patientAccesses[index].id_no;
fnameController.text = firstLetterFName;
lnameController.text = firstLetterLName;
});
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Patient Appointment",
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
},
windowBody: [
MIHTextField(
controller: idController,
hintText: "ID No.",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: fnameController,
hintText: "First Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: lnameController,
hintText: "Surname",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHDateField(
controller: dateController,
lableText: "Date",
required: true,
),
const SizedBox(height: 10.0),
MIHTimeField(
controller: timeController,
lableText: "Time",
required: true,
),
const SizedBox(height: 30.0),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
buttonText: "Book",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
//print("here1");
bool filled = isAppointmentFieldsFilled();
//print("fields filled: $filled");
if (filled) {
//print("here2");
submitApointment(index);
//print("here3");
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
},
),
)
],
),
);
}
void noAccessWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "No Access");
},
);
}
bool hasAccessToProfile(int index) {
var hasAccess = false;
if (widget.patientAccesses[index].status == "approved") {
hasAccess = true;
} else {
hasAccess = false;
}
return hasAccess;
}
void patientProfileChoicePopUp(int index, Patient? patientProfile) async {
var firstLetterFName = widget.patientAccesses[index].fname;
var firstLetterLName = widget.patientAccesses[index].lname;
setState(() {
idController.text = widget.patientAccesses[index].id_no;
fnameController.text = firstLetterFName;
lnameController.text = firstLetterLName;
});
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Patient Profile",
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
},
windowBody: [
MIHTextField(
controller: idController,
hintText: "ID No.",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: fnameController,
hintText: "First Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: lnameController,
hintText: "Surname",
editable: false,
required: true,
),
const SizedBox(height: 30.0),
Wrap(runSpacing: 10, spacing: 10, children: [
SizedBox(
width: 300,
height: 50,
child: MIHButton(
buttonText: "Book Appointment",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
print("Book an Appointment!!!");
appointmentPopUp(index);
// //print("here1");
// bool filled = isAppointmentFieldsFilled();
// //print("fields filled: $filled");
// if (filled) {
// //print("here2");
// submitApointment(index);
// //print("here3");
// } else {
// showDialog(
// context: context,
// builder: (context) {
// return const MIHErrorMessage(errorType: "Input Error");
// },
// );
// }
},
),
),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
buttonText: "View Patient Profile",
buttonColor:
MzanziInnovationHub.of(context)!.theme.successColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed('/patient-manager/patient',
arguments: PatientViewArguments(
widget.signedInUser,
patientProfile,
widget.arguments.businessUser,
widget.business,
"business",
));
},
),
),
])
],
),
);
}
Widget displayAccessTile(int index) {
var firstName = widget.patientAccesses[index].fname;
var lastName = widget.patientAccesses[index].lname;
String access = widget.patientAccesses[index].status.toUpperCase();
TextSpan accessWithColour;
var hasAccess = false;
hasAccess = hasAccessToProfile(index);
//print(hasAccess);
if (access == "APPROVED") {
accessWithColour = TextSpan(
text: "$access\n",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.successColor()));
} else if (access == "PENDING") {
accessWithColour = TextSpan(
text: "$access\n",
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.messageTextColor()));
} else {
accessWithColour = TextSpan(
text: "$access\n",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.errorColor()));
}
return ListTile(
title: Text(
"$firstName $lastName",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
subtitle: RichText(
text: TextSpan(
text: "ID No.: ${widget.patientAccesses[index].id_no}\n",
style: DefaultTextStyle.of(context).style,
children: <TextSpan>[
const TextSpan(text: "Access: "),
accessWithColour,
]),
),
onTap: () async {
Patient? p;
if (hasAccess) {
await MIHApiCalls.fetchPatientByAppId(
widget.patientAccesses[index].app_id)
.then((result) {
setState(() {
p = result;
});
});
patientProfileChoicePopUp(index, p);
} else {
noAccessWarning();
}
},
trailing: Icon(
Icons.arrow_forward,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
);
}
@override
void dispose() {
dateController.dispose();
timeController.dispose();
idController.dispose();
fnameController.dispose();
lnameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, index) {
return Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
);
},
itemCount: widget.patientAccesses.length,
itemBuilder: (context, index) {
//final patient = widget.patientAccesses[index].id_no.contains(widget.searchString);
//print(index);
return displayAccessTile(index);
},
);
}
}

View File

@@ -0,0 +1,670 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../../main.dart';
import '../../../mih_apis/mih_api_calls.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_date_input.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_time_input.dart';
import '../../../mih_components/mih_layout/mih_window.dart';
import '../../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_warning_message.dart';
import '../../../mih_env/env.dart';
import '../../../mih_objects/app_user.dart';
import '../../../mih_objects/arguments.dart';
import '../../../mih_objects/business.dart';
import '../../../mih_objects/patients.dart';
class BuildPatientsList extends StatefulWidget {
final List<Patient> patients;
final AppUser signedInUser;
final Business? business;
final BusinessArguments arguments;
const BuildPatientsList({
super.key,
required this.patients,
required this.signedInUser,
required this.business,
required this.arguments,
});
@override
State<BuildPatientsList> createState() => _BuildPatientsListState();
}
class _BuildPatientsListState extends State<BuildPatientsList> {
TextEditingController dateController = TextEditingController();
TextEditingController timeController = TextEditingController();
TextEditingController idController = TextEditingController();
TextEditingController fnameController = TextEditingController();
TextEditingController lnameController = TextEditingController();
TextEditingController accessStatusController = TextEditingController();
final baseAPI = AppEnviroment.baseApiUrl;
Future<void> addPatientAppointmentAPICall(int index) async {
var response = await http.post(
Uri.parse("$baseAPI/queue/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": widget.business!.business_id,
"app_id": widget.patients[index].app_id,
"date": dateController.text,
"time": timeController.text,
"access": "pending",
}),
);
if (response.statusCode == 201) {
// Navigator.pushNamed(context, '/patient-manager/patient',
// arguments: widget.signedInUser);
String message =
"The appointment has been successfully booked!\n\nAn approval request as been sent to the patient.Once the access request has been approved, you will be able to access the patients profile. ou can check the status of your request in patient queue under the appointment.";
// "${fnameController.text} ${lnameController.text} patient profiole has been successfully added!\n";
Navigator.pop(context);
Navigator.pop(context);
setState(() {
dateController.text = "";
timeController.text = "";
});
Navigator.of(context).pushNamed(
'/patient-manager',
arguments: BusinessArguments(
widget.arguments.signedInUser,
widget.arguments.businessUser,
widget.arguments.business,
),
);
successPopUp(message);
addAccessReviewNotificationAPICall(index);
} else {
internetConnectionPopUp();
}
}
Future<void> addPatientAccessAPICall(int index) async {
var response = await http.post(
Uri.parse("$baseAPI/access-requests/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": widget.business!.business_id,
"app_id": widget.patients[index].app_id,
"date": dateController.text,
"time": timeController.text,
"access": "pending",
}),
);
if (response.statusCode == 201) {
// Navigator.pushNamed(context, '/patient-manager/patient',
// arguments: widget.signedInUser);
String message =
"The appointment has been successfully booked!\n\nAn approval request as been sent to the patient.Once the access request has been approved, you will be able to access the patients profile. ou can check the status of your request in patient queue under the appointment.";
// "${fnameController.text} ${lnameController.text} patient profiole has been successfully added!\n";
Navigator.pop(context);
Navigator.pop(context);
setState(() {
dateController.text = "";
timeController.text = "";
});
Navigator.of(context).pushNamed(
'/patient-manager',
arguments: BusinessArguments(
widget.arguments.signedInUser,
widget.arguments.businessUser,
widget.arguments.business,
),
);
successPopUp(message);
addAccessReviewNotificationAPICall(index);
} else {
internetConnectionPopUp();
}
}
Future<void> addAccessReviewNotificationAPICall(int index) async {
var response = await http.post(
Uri.parse("$baseAPI/notifications/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"app_id": widget.patients[index].app_id,
"notification_type": "New Appointment Booked",
"notification_message":
"A new Appointment has been booked by ${widget.arguments.business!.Name} for the ${dateController.text} ${timeController.text}. Please approve the Access Review request.",
"action_path": "/access-review",
}),
);
if (response.statusCode == 201) {
// Navigator.pushNamed(context, '/patient-manager/patient',
// arguments: widget.signedInUser);
} 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,
);
},
);
}
void submitApointment(int index) {
MIHApiCalls.addAppointmentAPICall(
widget.arguments.business!.business_id,
widget.patients[index].app_id,
dateController.text,
timeController.text,
widget.arguments,
context,
);
//addPatientAppointmentAPICall(index);
}
bool isAppointmentFieldsFilled() {
if (dateController.text.isEmpty || timeController.text.isEmpty) {
return false;
} else {
return true;
}
}
void appointmentPopUp(int index) {
var firstLetterFName = widget.patients[index].first_name[0];
var firstLetterLName = widget.patients[index].last_name[0];
var fnameStar = '*' * 8;
var lnameStar = '*' * 8;
setState(() {
idController.text = widget.patients[index].id_no;
fnameController.text = firstLetterFName + fnameStar;
lnameController.text = firstLetterLName + lnameStar;
});
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Patient Appointment",
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
},
windowBody: [
MIHTextField(
controller: idController,
hintText: "ID No.",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: fnameController,
hintText: "First Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: lnameController,
hintText: "Surname",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHDateField(
controller: dateController,
lableText: "Date",
required: true,
),
const SizedBox(height: 10.0),
MIHTimeField(
controller: timeController,
lableText: "Time",
required: true,
),
const SizedBox(height: 30.0),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
buttonText: "Book",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
//print("here1");
bool filled = isAppointmentFieldsFilled();
//print("fields filled: $filled");
if (filled) {
//print("here2");
submitApointment(index);
//print("here3");
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
},
),
)
],
),
);
}
void noAccessWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "No Access");
},
);
}
Future<bool> hasAccessToProfile(int index) async {
var hasAccess = false;
await MIHApiCalls.checkBusinessAccessToPatient(
widget.business!.business_id, widget.patients[index].app_id)
.then((results) {
if (results.isEmpty) {
setState(() {
hasAccess = false;
});
} else if (results[0].status == "approved") {
setState(() {
hasAccess = true;
});
} else {
setState(() {
hasAccess = false;
});
}
});
return hasAccess;
}
Future<String> getAccessStatusOfProfile(int index) async {
var accessStatus = "";
await MIHApiCalls.checkBusinessAccessToPatient(
widget.business!.business_id, widget.patients[index].app_id)
.then((results) {
if (results.isEmpty) {
setState(() {
accessStatus = "";
});
} else {
setState(() {
accessStatus = results[0].status;
});
}
});
return accessStatus;
}
void patientProfileChoicePopUp(int index) async {
var hasAccess = false;
String accessStatus = "";
await hasAccessToProfile(index).then((result) {
setState(() {
hasAccess = result;
});
});
await getAccessStatusOfProfile(index).then((result) {
setState(() {
accessStatus = result;
});
});
// print(accessStatus);
// print(hasAccess);
var firstLetterFName = widget.patients[index].first_name[0];
var firstLetterLName = widget.patients[index].last_name[0];
var fnameStar = '*' * 8;
var lnameStar = '*' * 8;
if (accessStatus == "") {
accessStatus = "No Access";
}
setState(() {
idController.text = widget.patients[index].id_no;
fnameController.text = firstLetterFName + fnameStar;
lnameController.text = firstLetterLName + lnameStar;
accessStatusController.text = accessStatus.toUpperCase();
});
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Patient Profile",
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
},
windowBody: [
MIHTextField(
controller: idController,
hintText: "ID No.",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: fnameController,
hintText: "First Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: lnameController,
hintText: "Surname",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: accessStatusController,
hintText: "Access Status",
editable: false,
required: true,
),
const SizedBox(height: 20.0),
Visibility(
visible: !hasAccess,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Important Notice: Requesting Patient Profile Access",
style: TextStyle(
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
Text(
"You are about to request access to a patient's profile. Please be aware of the following important points:",
style: TextStyle(
fontWeight: FontWeight.normal,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
SizedBox(
width: 600,
child: Text(
"1. Permanent Access: Once the patient accepts your access request, it will become permanent.",
style: TextStyle(
fontWeight: FontWeight.normal,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
),
SizedBox(
width: 600,
child: Text(
"2. Shared Information: Any updates you make to the patient's profile will be visible to others who have access to the profile.",
style: TextStyle(
fontWeight: FontWeight.normal,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
),
SizedBox(
width: 600,
child: Text(
"3. Irreversible Access: Once granted, you cannot revoke your access to the patient's profile.",
style: TextStyle(
fontWeight: FontWeight.normal,
color:
MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
),
Text(
"By pressing the \"Request Access\" button you accept the above terms.",
style: TextStyle(
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!.theme.errorColor(),
),
),
],
),
),
const SizedBox(height: 20.0),
Wrap(runSpacing: 10, spacing: 10, children: [
Visibility(
visible: hasAccess,
child: SizedBox(
width: 300,
height: 50,
child: MIHButton(
buttonText: "Book Appointment",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
if (hasAccess) {
appointmentPopUp(index);
} else {
noAccessWarning();
}
},
),
),
),
Visibility(
visible: hasAccess,
child: SizedBox(
width: 300,
height: 50,
child: MIHButton(
buttonText: "View Patient Profile",
buttonColor:
MzanziInnovationHub.of(context)!.theme.successColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
if (hasAccess) {
Navigator.of(context)
.pushNamed('/patient-manager/patient',
arguments: PatientViewArguments(
widget.signedInUser,
widget.patients[index],
widget.arguments.businessUser,
widget.business,
"business",
));
} else {
noAccessWarning();
}
},
),
),
),
Visibility(
visible: !hasAccess && accessStatus != "pending",
child: SizedBox(
width: 300,
height: 50,
child: MIHButton(
buttonText: "Request Access",
buttonColor:
MzanziInnovationHub.of(context)!.theme.successColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
print("Send access Request...");
MIHApiCalls.addPatientAccessAPICall(
widget.business!.business_id,
widget.patients[index].app_id,
"patient",
widget.business!.Name,
widget.arguments,
context,
);
},
),
),
),
Visibility(
visible: !hasAccess && accessStatus == "pending",
child: const SizedBox(
width: 500,
//height: 50,
child: Text(
"Patient has not approved access to their profile. Once access has been approved you can book and appointment or view their profile."),
),
),
])
],
),
);
}
Widget isMainMember(int index) {
//var matchRE = RegExp(r'^[a-z]+$');
var firstLetterFName = widget.patients[index].first_name[0];
var firstLetterLName = widget.patients[index].last_name[0];
var fnameStar = '*' * 8;
var lnameStar = '*' * 8;
if (widget.patients[index].medical_aid_main_member == "Yes") {
return Row(
mainAxisSize: MainAxisSize.max,
children: [
Text(
"$firstLetterFName$fnameStar $firstLetterLName$lnameStar",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
const SizedBox(
width: 10,
),
Icon(
Icons.star_border_rounded,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
],
);
} else {
return Text(
"$firstLetterFName$fnameStar $firstLetterLName$lnameStar",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
);
}
}
Widget hasMedicalAid(int index) {
var medAidNoStar = '*' * 8;
if (widget.patients[index].medical_aid == "Yes") {
return ListTile(
title: isMainMember(index),
subtitle: Text(
"ID No.: ${widget.patients[index].id_no}\nMedical Aid No.: $medAidNoStar",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
onTap: () {
patientProfileChoicePopUp(index);
// setState(() {
// appointmentPopUp(index);
// // Add popup to add patienmt to queue
// // Navigator.of(context).pushNamed('/patient-manager/patient',
// // arguments: PatientViewArguments(
// // widget.signedInUser, widget.patients[index], "business"));
// });
},
trailing: Icon(
Icons.arrow_forward,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
);
} else {
return ListTile(
title: isMainMember(index),
subtitle: Text(
"ID No.: ${widget.patients[index].id_no}\nMedical Aid No.: $medAidNoStar",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
onTap: () {
patientProfileChoicePopUp(index);
// setState(() {
// appointmentPopUp(index);
// // Navigator.of(context).pushNamed('/patient-manager/patient',
// // arguments: PatientViewArguments(
// // widget.signedInUser, widget.patients[index], "business"));
// });
},
trailing: Icon(
Icons.add,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
);
}
}
@override
void dispose() {
dateController.dispose();
timeController.dispose();
idController.dispose();
fnameController.dispose();
lnameController.dispose();
accessStatusController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, index) {
return Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
);
},
itemCount: widget.patients.length,
itemBuilder: (context, index) {
//final patient = widget.patients[index].id_no.contains(widget.searchString);
//print(index);
return hasMedicalAid(index);
},
);
}
}

View File

@@ -0,0 +1,843 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../../main.dart';
import '../../../mih_apis/mih_api_calls.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_date_input.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../../mih_components/mih_inputs_and_buttons/mih_time_input.dart';
import '../../../mih_components/mih_layout/mih_window.dart';
import '../../../mih_components/mih_pop_up_messages/mih_delete_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../../mih_components/mih_pop_up_messages/mih_warning_message.dart';
import '../../../mih_env/env.dart';
import '../../../mih_objects/app_user.dart';
import '../../../mih_objects/arguments.dart';
import '../../../mih_objects/business.dart';
import '../../../mih_objects/business_user.dart';
import '../../../mih_objects/patient_queue.dart';
import '../../../mih_objects/patients.dart';
class BuildPatientQueueList extends StatefulWidget {
final List<PatientQueue> patientQueue;
final AppUser signedInUser;
final Business? business;
final BusinessUser? businessUser;
const BuildPatientQueueList({
super.key,
required this.patientQueue,
required this.signedInUser,
required this.business,
required this.businessUser,
});
@override
State<BuildPatientQueueList> createState() => _BuildPatientsListState();
}
class _BuildPatientsListState extends State<BuildPatientQueueList> {
String baseAPI = AppEnviroment.baseApiUrl;
TextEditingController dateController = TextEditingController();
TextEditingController timeController = TextEditingController();
TextEditingController idController = TextEditingController();
TextEditingController fnameController = TextEditingController();
TextEditingController lnameController = TextEditingController();
TextEditingController daysExtensionController = TextEditingController();
int counter = 0;
Future<void> updateAccessAPICall(int index, String accessType) async {
var response = await http.put(
Uri.parse("$baseAPI/access-requests/update/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"business_id": widget.business!.business_id,
"app_id": widget.patientQueue[index].app_id,
"date_time": widget.patientQueue[index].date_time,
"access": accessType,
}),
);
if (response.statusCode == 200) {
//Navigator.of(context).pushNamed('/home');
addCancelledAppointmentNotificationAPICall(index);
// Navigator.of(context).pop();
// Navigator.of(context).pop();
// Navigator.of(context).pushNamed(
// '/patient-manager',
// arguments: BusinessArguments(
// widget.signedInUser,
// widget.businessUser,
// widget.business,
// ),
// );
// String message =
// "The appointment for ${widget.patientQueue[index].first_name} ${widget.patientQueue[index].last_name} at ${widget.patientQueue[index].date_time} has been successfully canceled.";
// successPopUp(message);
} else {
internetConnectionPopUp();
}
}
// Future<void> extendAccessAPICall(int index, String revokeDate) async {
// var response = await http.put(
// Uri.parse("$baseAPI/access-requests/extension/"),
// headers: <String, String>{
// "Content-Type": "application/json; charset=UTF-8"
// },
// body: jsonEncode(<String, dynamic>{
// "business_id": widget.business!.business_id,
// "app_id": widget.patientQueue[index].app_id,
// "date_time": widget.patientQueue[index].date_time,
// "revoke_date": revokeDate,
// }),
// );
// if (response.statusCode == 200) {
// addAccessExtensionNotificationAPICall(index, revokeDate);
// //addCancelledAppointmentNotificationAPICall(index);
// } else {
// internetConnectionPopUp();
// }
// }
Future<void> updateApointmentAPICall(int index) async {
var response = await http.put(
Uri.parse("$baseAPI/queue/appoointment/update/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"idpatient_queue": widget.patientQueue[index].idpatient_queue,
"date": dateController.text,
"time": timeController.text,
}),
);
if (response.statusCode == 200) {
//Navigator.of(context).pushNamed('/home');
addRescheduledAppointmentNotificationAPICall(index);
// Navigator.of(context).pop();
// Navigator.of(context).pop();
// Navigator.of(context).pop();
// Navigator.of(context).pushNamed(
// '/patient-manager',
// arguments: BusinessArguments(
// widget.signedInUser,
// widget.businessUser,
// widget.business,
// ),
// );
// String message =
// "The appointment has been rescheduled for ${dateController.text} ${timeController.text}.\n\nJust a heads up: We've reset your access to the patient's profile and it will have to be approved again.";
// successPopUp(message);
} else {
internetConnectionPopUp();
}
}
Future<Patient> fetchPatients(String app_id) async {
//print("pat man drawer: " + endpointUserData + widget.userEmail);
var response = await http.get(Uri.parse("$baseAPI/patients/$app_id"));
// print(response.statusCode);
// print(response.body);
if (response.statusCode == 200) {
// print("here");
String body = response.body;
var decodedData = jsonDecode(body);
Patient u = Patient.fromJson(decodedData);
// print(u.email);
//setState(() {
//_widgetOptions = setLayout(u);
//});
return u;
} else {
throw Exception("Error: GetUserData status code ${response.statusCode}");
}
}
void internetConnectionPopUp() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Internet Connection");
},
);
}
void deleteFilePopUp(int index) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHDeleteMessage(
deleteType: "appointment",
onTap: () async {
await updateAccessAPICall(index, "cancelled");
},
),
);
}
void successPopUp(String message) {
showDialog(
context: context,
builder: (context) {
return MIHSuccessMessage(
successType: "Success",
successMessage: message,
);
},
);
}
void viewConfirmationPopUp(int index) {
String subtitle =
"Appointment: ${widget.patientQueue[index].date_time.substring(0, 16).replaceAll("T", " ")}\n";
subtitle += "ID No.: ${widget.patientQueue[index].id_no}\n";
subtitle +=
"Patient: ${widget.patientQueue[index].first_name} ${widget.patientQueue[index].last_name}\n";
// subtitle +=
// "Patient: ${widget.patientQueue[index].first_name} ${widget.patientQueue[index].last_name}\n\n";
//subtitle += "Business Type: ${widget.patientQueue[index].last_name}\n\n";
subtitle +=
"Would you like to view the patient's profile before the appointment or cancel the appointment now?\n";
//"You are about to approve an access request to your patient profile.\nPlease be aware that once approved, ${widget.accessRequests[index].Name} will have access to your profile information until ${widget.accessRequests[index].revoke_date.substring(0, 16).replaceAll("T", " ")}.\nIf you are unsure about an upcoming appointment with ${widget.accessRequests[index].Name}, please contact ${widget.accessRequests[index].contact_no} for clarification before approving this request.\n";
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Appointment Confirmation",
windowBody: [
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Text(
subtitle,
textAlign: TextAlign.left,
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
fontSize: 15,
//fontWeight: FontWeight.bold,
),
),
),
Wrap(
runSpacing: 10,
spacing: 10,
children: [
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: () {
//updateAccessAPICall(index, "cancelled");
var appointmentDateTime = widget
.patientQueue[index].date_time
.substring(0, 16)
.split("T");
var firstName = "";
var lastName = "";
firstName = widget.patientQueue[index].first_name;
lastName = widget.patientQueue[index].last_name;
setState(() {
idController.text = widget.patientQueue[index].id_no;
fnameController.text = firstName;
lnameController.text = lastName;
dateController.text = appointmentDateTime[0];
timeController.text = appointmentDateTime[1];
});
manageAppointmentPopUp(index);
},
buttonText: "Manage Appointment",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: () {
//updateAccessAPICall(index, "approved");
Patient selectedPatient;
fetchPatients(widget.patientQueue[index].app_id).then(
(result) {
setState(() {
selectedPatient = result;
Navigator.of(context).pop();
Navigator.of(context)
.pushNamed('/patient-manager/patient',
arguments: PatientViewArguments(
widget.signedInUser,
selectedPatient,
widget.businessUser,
widget.business,
"business",
));
});
},
);
},
buttonText: "View Patient Profile",
buttonColor:
MzanziInnovationHub.of(context)!.theme.successColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
],
),
const SizedBox(
height: 10,
),
],
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
}),
);
}
Future<void> addCancelledAppointmentNotificationAPICall(int index) async {
var response = await http.post(
Uri.parse("$baseAPI/notifications/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"app_id": widget.patientQueue[index].app_id,
"notification_type": "Appointment Cancelled",
"notification_message":
"Your appointment with ${widget.business!.Name} for the ${widget.patientQueue[index].date_time.replaceAll("T", " ")} has been cancelled.",
"action_path": "/access-review",
}),
);
if (response.statusCode == 201) {
// Navigator.pushNamed(context, '/patient-manager/patient',
// arguments: widget.signedInUser);
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed(
'/patient-manager',
arguments: BusinessArguments(
widget.signedInUser,
widget.businessUser,
widget.business,
),
);
String message =
"The appointment for ${widget.patientQueue[index].first_name} ${widget.patientQueue[index].last_name} at ${widget.patientQueue[index].date_time} has been successfully canceled.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
}
Future<void> addRescheduledAppointmentNotificationAPICall(int index) async {
var response = await http.post(
Uri.parse("$baseAPI/notifications/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"app_id": widget.patientQueue[index].app_id,
"notification_type": "Rescheduled Appointment",
"notification_message":
"Your appointment with ${widget.business!.Name} for the ${widget.patientQueue[index].date_time.replaceAll("T", " ").substring(0, widget.patientQueue[index].date_time.length - 3)} has been rescheduled to the ${dateController.text} ${timeController.text}.",
"action_path": "/access-review",
}),
);
if (response.statusCode == 201) {
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed(
'/patient-manager',
arguments: BusinessArguments(
widget.signedInUser,
widget.businessUser,
widget.business,
),
);
String message =
"The appointment for ${widget.patientQueue[index].first_name} ${widget.patientQueue[index].last_name} at ${widget.patientQueue[index].date_time} has been successfully canceled.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
}
// Future<void> addAccessExtensionNotificationAPICall(
// int index, String revokeDate) async {
// var response = await http.post(
// Uri.parse("$baseAPI/notifications/insert/"),
// headers: <String, String>{
// "Content-Type": "application/json; charset=UTF-8"
// },
// body: jsonEncode(<String, dynamic>{
// "app_id": widget.patientQueue[index].app_id,
// "notification_type": "Access Extension Request",
// "notification_message":
// "${widget.business!.Name} - access expiry date extension for appointment: ${widget.patientQueue[index].date_time.split("T")[0]}. Expiry Date: from ${widget.patientQueue[index].revoke_date.split("T")[0]} to ${revokeDate.split(" ")[0]}.",
// "action_path": "/access-review",
// }),
// );
// if (response.statusCode == 201) {
// Navigator.of(context).pop();
// Navigator.of(context).pop();
// Navigator.of(context).pushNamed(
// '/patient-manager',
// arguments: BusinessArguments(
// widget.signedInUser,
// widget.businessUser,
// widget.business,
// ),
// );
// String message =
// "you have successfully requested an extension on the expirey date. The request has been sent tp ${widget.patientQueue[index].first_name} ${widget.patientQueue[index].last_name} to review and approve the request";
// successPopUp(message);
// } else {
// internetConnectionPopUp();
// }
// }
void manageAppointmentPopUp(int index) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Manage Appointment",
windowBody: [
MIHTextField(
controller: idController,
hintText: "ID No.",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: fnameController,
hintText: "First Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: lnameController,
hintText: "Surname",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
const SizedBox(height: 10.0),
MIHDateField(
controller: dateController,
lableText: "Date",
required: true,
),
const SizedBox(height: 10.0),
MIHTimeField(
controller: timeController,
lableText: "Time",
required: true,
),
const SizedBox(height: 30.0),
Wrap(
runSpacing: 10,
spacing: 10,
children: [
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: () {
//add mihapicall for deleting appointment
MIHApiCalls.deleteApointmentAPICall(
widget.patientQueue[index].idpatient_queue,
widget.patientQueue[index].app_id,
widget.patientQueue[index].date_time,
BusinessArguments(
widget.signedInUser,
widget.businessUser,
widget.business,
),
context,
);
//updateAccessAPICall(index, "cancelled");
},
buttonText: "Cancel Appointment",
buttonColor:
MzanziInnovationHub.of(context)!.theme.errorColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: () {
MIHApiCalls.updateApointmentAPICall(
widget.patientQueue[index].idpatient_queue,
widget.patientQueue[index].app_id,
widget.business!.Name,
widget.patientQueue[index].date_time,
dateController.text,
timeController.text,
BusinessArguments(
widget.signedInUser,
widget.businessUser,
widget.business,
),
context,
);
},
buttonText: "Update Appointment",
buttonColor:
MzanziInnovationHub.of(context)!.theme.successColor(),
textColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
],
),
const SizedBox(
height: 10,
),
],
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
}),
);
}
Widget displayQueue(int index) {
String title =
widget.patientQueue[index].date_time.split('T')[1].substring(0, 5);
String line234 = "";
// var nowDate = DateTime.now();
// var expireyDate = DateTime.parse(widget.patientQueue[index].revoke_date);
line234 +=
"Name: ${widget.patientQueue[index].first_name} ${widget.patientQueue[index].last_name}\nID No.: ${widget.patientQueue[index].id_no}\nMedical Aid No: ";
if (widget.patientQueue[index].medical_aid_no == "") {
line234 += "No Medical Aid";
} else {
// subtitle +=
// "\nMedical Aid No: ";
line234 += widget.patientQueue[index].medical_aid_no;
}
// String line5 = "\nAccess Request: ";
// String access = "";
// if (expireyDate.isBefore(nowDate) &&
// widget.patientQueue[index].access != "cacelled") {
// access += "EXPIRED";
// } else {
// access += widget.patientQueue[index].access.toUpperCase();
// }
// TextSpan accessWithColour;
// if (access == "APPROVED") {
// accessWithColour = TextSpan(
// text: access,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!.theme.successColor()));
// } else if (access == "PENDING") {
// accessWithColour = TextSpan(
// text: access,
// style: TextStyle(
// color:
// MzanziInnovationHub.of(context)!.theme.messageTextColor()));
// } else {
// accessWithColour = TextSpan(
// text: access,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!.theme.errorColor()));
// }
// String line6 = "";
// line6 +=
// "\nAccess Expiration date: ${widget.patientQueue[index].revoke_date.substring(0, 16).replaceAll("T", " ")}";
return ListTile(
title: Text(
"Appointment: $title",
style: TextStyle(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
subtitle: RichText(
text: TextSpan(
text: line234,
style: DefaultTextStyle.of(context).style,
// children: [
// TextSpan(text: line5),
// accessWithColour,
// TextSpan(text: line6),
// ]
),
),
// Text(
// subtitle,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// ),
// ),
onTap: () {
// var todayDate = DateTime.now();
// var revokeDate = DateTime.parse(widget.patientQueue[index].revoke_date);
// // print(
// // "Todays: $todayDate\nRevoke Date: $revokeDate\nHas revoke date passed: ${revokeDate.isBefore(todayDate)}");
// if (revokeDate.isBefore(todayDate)) {
// expiredAccessWarning();
// } else if (widget.patientQueue[index].access == "approved") {
// viewConfirmationPopUp(index);
// // Patient selectedPatient;
// // fetchPatients(widget.patientQueue[index].app_id).then(
// // (result) {
// // setState(() {
// // selectedPatient = result;
// // Navigator.of(context).pushNamed('/patient-manager/patient',
// // arguments: PatientViewArguments(
// // widget.signedInUser,
// // selectedPatient,
// // widget.businessUser,
// // widget.business,
// // "business",
// // ));
// // });
// // },
// // );
// } else if (widget.patientQueue[index].access == "declined") {
// accessDeclinedWarning();
// } else if (widget.patientQueue[index].access == "cancelled") {
// appointmentCancelledWarning();
// } else {
viewConfirmationPopUp(index);
//noAccessWarning();
// }
},
//leading: getExtendAccessButton(access),
//trailing: getExtendAccessButton(access, index),
// Icon(
// Icons.arrow_forward,
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// ),
);
}
bool isAccessExpired(String accessType) {
if (accessType == "EXPIRED") {
return true;
} else {
return false;
}
}
// Widget getExtendAccessButton(String accessType, int index) {
// if (isAccessExpired(accessType)) {
// return IconButton(
// icon: const Icon(Icons.cached),
// onPressed: () {
// setState(() {
// daysExtensionController.text = counter.toString();
// });
// reapplyForAccess(index);
// },
// );
// } else {
// return Icon(
// Icons.arrow_forward,
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// );
// }
// }
// void reapplyForAccess(int index) {
// showDialog(
// context: context,
// barrierDismissible: false,
// builder: (context) => MIHWindow(
// fullscreen: false,
// windowTitle: "Extend Access",
// windowTools: const [],
// onWindowTapClose: () {
// Navigator.pop(context);
// },
// windowBody: [
// Text(
// "Current Expiration Date : ${widget.patientQueue[index].revoke_date.replaceAll("T", " ")}",
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// fontSize: 15,
// fontWeight: FontWeight.normal,
// ),
// ),
// const SizedBox(height: 5),
// Text(
// "Select the number of days you would like to extend the access by.",
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// fontSize: 15,
// fontWeight: FontWeight.normal,
// ),
// ),
// const SizedBox(height: 5),
// Text(
// "Once you click \"Apply\", an access review request will be triggered to the patient.",
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// fontSize: 15,
// fontWeight: FontWeight.normal,
// ),
// ),
// const SizedBox(height: 30),
// Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// IconButton.filled(
// onPressed: () {
// if (counter > 0) {
// counter--;
// setState(() {
// daysExtensionController.text = counter.toString();
// });
// }
// },
// icon: const Icon(Icons.remove),
// ),
// const SizedBox(width: 15),
// SizedBox(
// width: 100,
// child: MIHTextField(
// controller: daysExtensionController,
// hintText: "Days",
// editable: false,
// required: true,
// ),
// ),
// const SizedBox(width: 15),
// IconButton.filled(
// onPressed: () {
// counter++;
// setState(() {
// daysExtensionController.text = counter.toString();
// });
// },
// icon: const Icon(Icons.add),
// ),
// ],
// ),
// const SizedBox(height: 30),
// SizedBox(
// width: 300,
// height: 50,
// child: MIHButton(
// onTap: () {
// print(
// "Revoke Date (String): ${widget.patientQueue[index].revoke_date}");
// var revokeDate = DateTime.parse(widget
// .patientQueue[index].revoke_date
// .replaceAll("T", " "));
// var newRevokeDate = revokeDate.add(
// Duration(days: int.parse(daysExtensionController.text)));
// print("Revoke Date (DateTime): $revokeDate");
// print("New Revoke Date (DateTime): $newRevokeDate");
// print(
// "${widget.business!.Name} would like to extend the access expirey date for your appointment on the ${widget.patientQueue[index].date_time}.\nNew Expirey Date: $revokeDate",
// );
// extendAccessAPICall(index, "$newRevokeDate");
// },
// buttonText: "Apply",
// buttonColor:
// MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
// ),
// ),
// ],
// ),
// );
// }
void noAccessWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "No Access");
},
);
}
void accessDeclinedWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "Access Declined");
},
);
}
void appointmentCancelledWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "Appointment Canelled");
},
);
}
void expiredAccessWarning() {
showDialog(
context: context,
builder: (context) {
return const MIHWarningMessage(warningType: "Expired Access");
},
);
}
@override
void dispose() {
daysExtensionController.dispose();
dateController.dispose();
timeController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.separated(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
separatorBuilder: (BuildContext context, index) {
return Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
);
},
itemCount: widget.patientQueue.length,
itemBuilder: (context, index) {
//final patient = widget.patients[index].id_no.contains(widget.searchString);
//print(index);
return displayQueue(index);
},
);
}
}

View File

@@ -0,0 +1,304 @@
import 'package:flutter/material.dart';
import '../../main.dart';
import 'package:syncfusion_flutter_core/theme.dart';
import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';
import "package:universal_html/html.dart" as html;
import 'package:http/http.dart' as http;
import 'package:printing/printing.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_objects/arguments.dart';
class FullScreenFileViewer extends StatefulWidget {
final FileViewArguments arguments;
const FullScreenFileViewer({
super.key,
required this.arguments,
});
@override
State<FullScreenFileViewer> createState() => _FullScreenFileViewerState();
}
class _FullScreenFileViewerState extends State<FullScreenFileViewer> {
late PdfViewerController pdfViewerController = PdfViewerController();
int currentPageCount = 0;
int currentPage = 0;
double startZoomLevel = 1.0;
double zoomOut = 0;
String getExtType(String path) {
//print(pdfLink.split(".")[1]);
return path.split(".").last;
}
String getFileName(String path) {
//print(pdfLink.split(".")[1]);
return path.split("/").last;
}
void onPageSelect() {
setState(() {
currentPage = pdfViewerController.pageNumber;
});
}
void printDocument() async {
print("Printing ${widget.arguments.path.split("/").last}");
http.Response response = await http.get(Uri.parse(widget.arguments.link));
var pdfData = response.bodyBytes;
Navigator.of(context).pushNamed(
'/file-veiwer/print-preview',
arguments: pdfData,
);
// Navigator.of(context).push(
// MaterialPageRoute(
// builder: (context) => PdfPreview(
// initialPageFormat: PdfPageFormat.a4,
// build: (format) => pdfData,
// ),
// ),
// );
// try {
// await Printing.layoutPdf(
// onLayout: (PdfPageFormat format) async => pdfData);
// } on Exception catch (_) {
// print("Print Error");
// }
}
void shareDocument() async {
http.Response response = await http.get(Uri.parse(widget.arguments.link));
var pdfData = response.bodyBytes;
await Printing.sharePdf(
bytes: pdfData, filename: widget.arguments.path.split("/").last);
}
MIHAction getActionButton() {
return MIHAction(
icon: const Icon(
Icons.fullscreen_exit,
),
iconSize: 35,
onTap: () {
Navigator.pop(context);
},
);
}
MIHHeader getHeader(double width) {
bool isPDF;
if (getExtType(widget.arguments.path).toLowerCase() == "pdf") {
isPDF = true;
} else {
isPDF = false;
}
return MIHHeader(
headerAlignment: MainAxisAlignment.end,
headerItems: [
Visibility(
visible: isPDF,
child: IconButton(
iconSize: 30,
padding: const EdgeInsets.all(0),
onPressed: () {
pdfViewerController.previousPage();
//print(pdfViewerController.);
//if (pdfViewerController.pageNumber > 1) {
setState(() {
currentPage = pdfViewerController.pageNumber;
});
// }
},
icon: Icon(
Icons.arrow_back,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
),
Visibility(
visible: isPDF,
child: Text(
"$currentPage / $currentPageCount",
style: const TextStyle(fontSize: 20),
),
),
Visibility(
visible: isPDF,
child: IconButton(
iconSize: 30,
padding: const EdgeInsets.all(0),
onPressed: () {
pdfViewerController.nextPage();
//print(pdfViewerController.pageNumber);
//if (pdfViewerController.pageNumber < currentPageCount) {
setState(() {
currentPage = pdfViewerController.pageNumber;
});
//}
},
icon: Icon(
Icons.arrow_forward,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
),
Visibility(
visible: isPDF,
child: IconButton(
iconSize: 30,
padding: const EdgeInsets.all(0),
onPressed: () {
if (zoomOut > 0) {
setState(() {
zoomOut = zoomOut - 100;
});
} else {
setState(() {
pdfViewerController.zoomLevel = startZoomLevel + 0.25;
startZoomLevel = pdfViewerController.zoomLevel;
});
}
},
icon: Icon(
Icons.zoom_in,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
),
Visibility(
visible: isPDF,
child: IconButton(
iconSize: 30,
padding: const EdgeInsets.all(0),
onPressed: () {
if (pdfViewerController.zoomLevel > 1) {
setState(() {
pdfViewerController.zoomLevel = startZoomLevel - 0.25;
startZoomLevel = pdfViewerController.zoomLevel;
});
} else {
if (zoomOut < (width - 100)) {
setState(() {
zoomOut = zoomOut + 100;
});
}
}
},
icon: Icon(
Icons.zoom_out,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
),
// IconButton(
// iconSize: 30,
// padding: const EdgeInsets.all(0),
// onPressed: () {
// printDocument();
// },
// icon: Icon(
// Icons.print,
// color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// ),
// ),
IconButton(
iconSize: 30,
padding: const EdgeInsets.all(0),
onPressed: () {
html.window.open(
widget.arguments.link,
// '${AppEnviroment.baseFileUrl}/mih/$filePath',
'download');
},
icon: Icon(
Icons.download,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
],
);
}
MIHBody getBody(double width, double height) {
Widget fileViewer;
if (getExtType(widget.arguments.path).toLowerCase() == "pdf") {
fileViewer = SizedBox(
width: width - zoomOut,
height: height - 70,
child: SfPdfViewerTheme(
data: SfPdfViewerThemeData(
backgroundColor:
MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
child: SfPdfViewer.network(
widget.arguments.link,
controller: pdfViewerController,
initialZoomLevel: startZoomLevel,
pageSpacing: 2,
maxZoomLevel: 5,
interactionMode: PdfInteractionMode.pan,
onDocumentLoaded: (details) {
setState(() {
currentPage = pdfViewerController.pageNumber;
currentPageCount = pdfViewerController.pageCount;
});
},
),
),
);
} else {
fileViewer = SizedBox(
width: width,
height: height - 70,
child: InteractiveViewer(
maxScale: 5.0,
//minScale: 0.,
child: Image.network(widget.arguments.link),
),
);
}
return MIHBody(
borderOn: false,
bodyItems: [
fileViewer,
],
);
}
@override
void dispose() {
pdfViewerController.dispose();
super.dispose();
}
@override
void initState() {
//pdfViewerController = widget.arguments.pdfViewerController!;
pdfViewerController.addListener(onPageSelect);
super.initState();
}
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.sizeOf(context);
double width = size.width;
double height = size.height;
return MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(width),
secondaryActionButton: null,
body: getBody(width, height),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
}
}

View File

@@ -0,0 +1,443 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/arguments.dart';
class AddPatient extends StatefulWidget {
final AppUser signedInUser;
const AddPatient({
super.key,
required this.signedInUser,
});
@override
State<AddPatient> createState() => _AddPatientState();
}
class _AddPatientState extends State<AddPatient> {
final idController = TextEditingController();
final fnameController = TextEditingController();
final lnameController = TextEditingController();
final cellController = TextEditingController();
final emailController = TextEditingController();
final medNoController = TextEditingController();
final medNameController = TextEditingController();
final medSchemeController = TextEditingController();
final addressController = TextEditingController();
final medAidController = TextEditingController();
final medMainMemController = TextEditingController();
final medAidCodeController = TextEditingController();
final baseAPI = AppEnviroment.baseApiUrl;
late int futureDocOfficeId;
late bool medRequired;
final FocusNode _focusNode = FocusNode();
bool isFieldsFilled() {
if (medRequired) {
if (idController.text.isEmpty ||
fnameController.text.isEmpty ||
lnameController.text.isEmpty ||
cellController.text.isEmpty ||
emailController.text.isEmpty ||
medNoController.text.isEmpty ||
medNameController.text.isEmpty ||
medSchemeController.text.isEmpty ||
addressController.text.isEmpty ||
medAidController.text.isEmpty ||
medMainMemController.text.isEmpty ||
medAidCodeController.text.isEmpty) {
return false;
} else {
return true;
}
} else {
if (idController.text.isEmpty ||
fnameController.text.isEmpty ||
lnameController.text.isEmpty ||
cellController.text.isEmpty ||
emailController.text.isEmpty ||
addressController.text.isEmpty ||
medAidController.text.isEmpty) {
return false;
} else {
return true;
}
}
}
Future<void> addPatientAPICall() async {
var response = await http.post(
Uri.parse("$baseAPI/patients/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"id_no": idController.text,
"first_name": fnameController.text,
"last_name": lnameController.text,
"email": emailController.text,
"cell_no": cellController.text,
"medical_aid": medAidController.text,
"medical_aid_main_member": medMainMemController.text,
"medical_aid_no": medNoController.text,
"medical_aid_code": medAidCodeController.text,
"medical_aid_name": medNameController.text,
"medical_aid_scheme": medSchemeController.text,
"address": addressController.text,
"app_id": widget.signedInUser.app_id,
}),
);
if (response.statusCode == 201) {
Navigator.of(context).popAndPushNamed('/patient-profile',
arguments: PatientViewArguments(
widget.signedInUser, null, null, null, "personal"));
String message =
"${fnameController.text} ${lnameController.text} patient profiole has been successfully added!\n";
successPopUp(message);
} else {
internetConnectionPopUp();
}
}
void internetConnectionPopUp() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Internet Connection");
},
);
}
void messagePopUp(error) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(error),
);
},
);
}
void successPopUp(String message) {
showDialog(
context: context,
builder: (context) {
return MIHSuccessMessage(
successType: "Success",
successMessage: message,
);
},
);
}
void isRequired() {
//print("listerner triggered");
if (medAidController.text == "Yes") {
setState(() {
medRequired = true;
});
} else {
setState(() {
medRequired = false;
});
}
}
Widget displayForm() {
return SingleChildScrollView(
child: Column(
children: [
Text(
"Personal Details",
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 22.0,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
const SizedBox(height: 10.0),
MIHTextField(
controller: idController,
hintText: "13 digit ID Number or Passport",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: fnameController,
hintText: "First Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: lnameController,
hintText: "Last Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: cellController,
hintText: "Cell Number",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: emailController,
hintText: "Email",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: addressController,
hintText: "Address",
editable: true,
required: true,
),
const SizedBox(height: 15.0),
Text(
"Medical Aid Details",
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 22.0,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
const SizedBox(height: 10.0),
MIHDropdownField(
controller: medAidController,
hintText: "Medical Aid",
editable: true,
onSelect: (_) {
isRequired();
},
required: true,
dropdownOptions: const ["Yes", "No"],
),
Visibility(
visible: medRequired,
child: Column(
children: [
const SizedBox(height: 10.0),
MIHDropdownField(
controller: medMainMemController,
hintText: "Main Member",
editable: medRequired,
required: medRequired,
dropdownOptions: const ["Yes", "No"],
),
const SizedBox(height: 10.0),
MIHTextField(
controller: medNoController,
hintText: "Medical Aid No.",
editable: medRequired,
required: medRequired,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: medAidCodeController,
hintText: "Medical Aid Code",
editable: medRequired,
required: medRequired,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: medNameController,
hintText: "Medical Aid Name",
editable: medRequired,
required: medRequired,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: medSchemeController,
hintText: "Medical Aid Scheme",
editable: medRequired,
required: medRequired,
),
],
),
),
const SizedBox(height: 30.0),
SizedBox(
width: 450.0,
height: 50.0,
child: MIHButton(
onTap: () {
submitForm();
},
buttonText: "Add",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
],
),
);
}
void submitForm() {
if (isFieldsFilled()) {
addPatientAPICall();
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
}
MIHAction getActionButton() {
return MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pop();
},
);
}
MIHHeader getHeader() {
return const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"Add Patient Details",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: true,
bodyItems: [
KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
submitForm();
}
},
child: displayForm(),
),
],
);
}
@override
void dispose() {
idController.dispose();
fnameController.dispose();
lnameController.dispose();
cellController.dispose();
emailController.dispose();
medNoController.dispose();
medNameController.dispose();
medSchemeController.dispose();
addressController.dispose();
medAidController.dispose();
medMainMemController.dispose();
medAidCodeController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
medAidController.addListener(isRequired);
setState(() {
fnameController.text = widget.signedInUser.fname;
lnameController.text = widget.signedInUser.lname;
emailController.text = widget.signedInUser.email;
medAidController.text = "No";
});
super.initState();
}
@override
Widget build(BuildContext context) {
return MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(),
secondaryActionButton: null,
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
// return Scaffold(
// // appBar: const MIHAppBar(
// // barTitle: "Add Patient",
// // propicFile: null,
// // ),
// //drawer: MIHAppDrawer(signedInUser: widget.signedInUser),
// body: SafeArea(
// child: Stack(
// children: [
// KeyboardListener(
// focusNode: _focusNode,
// autofocus: true,
// onKeyEvent: (event) async {
// if (event is KeyDownEvent &&
// event.logicalKey == LogicalKeyboardKey.enter) {
// submitForm();
// }
// },
// child: displayForm(),
// ),
// Positioned(
// top: 10,
// left: 5,
// width: 50,
// height: 50,
// child: IconButton(
// onPressed: () {
// Navigator.of(context).pop();
// },
// icon: const Icon(Icons.arrow_back),
// ),
// )
// ],
// ),
// ),
// );
}
}

View File

@@ -0,0 +1,298 @@
import 'package:flutter/material.dart';
import 'package:patient_manager/mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../main.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/arguments.dart';
import '../../mih_objects/patients.dart';
class PatientDetails extends StatefulWidget {
final AppUser signedInUser;
final Patient selectedPatient;
final String type;
const PatientDetails({
super.key,
required this.signedInUser,
required this.selectedPatient,
required this.type,
});
@override
State<PatientDetails> createState() => _PatientDetailsState();
}
class _PatientDetailsState extends State<PatientDetails> {
final idController = TextEditingController();
final fnameController = TextEditingController();
final lnameController = TextEditingController();
final cellController = TextEditingController();
final emailController = TextEditingController();
final medNoController = TextEditingController();
final medNameController = TextEditingController();
final medSchemeController = TextEditingController();
final addressController = TextEditingController();
final medAidController = TextEditingController();
final medMainMemController = TextEditingController();
final medAidCodeController = TextEditingController();
double? headingFontSize = 35.0;
double? bodyFonstSize = 20.0;
double textFieldWidth = 400.0;
late String medAid;
Widget getPatientDetailsField() {
return Wrap(
spacing: 15,
runSpacing: 10,
children: [
SizedBox(
width: textFieldWidth,
child: MIHTextField(
controller: idController,
hintText: "ID No.",
editable: false,
required: false),
),
SizedBox(
width: textFieldWidth,
child: MIHTextField(
controller: fnameController,
hintText: "Name",
editable: false,
required: false),
),
SizedBox(
width: textFieldWidth,
child: MIHTextField(
controller: lnameController,
hintText: "Surname",
editable: false,
required: false),
),
SizedBox(
width: textFieldWidth,
child: MIHTextField(
controller: cellController,
hintText: "Cell No.",
editable: false,
required: false),
),
SizedBox(
width: textFieldWidth,
child: MIHTextField(
controller: emailController,
hintText: "Email",
editable: false,
required: false),
),
SizedBox(
width: textFieldWidth,
child: MIHTextField(
controller: addressController,
hintText: "Address",
editable: false,
required: false),
),
],
);
}
Widget getMedAidDetailsFields() {
List<Widget> medAidDet = [];
medAidDet.add(SizedBox(
width: textFieldWidth,
child: MIHTextField(
controller: medAidController,
hintText: "Medical Aid",
editable: false,
required: false),
));
bool req;
if (medAid == "Yes") {
req = true;
} else {
req = false;
}
medAidDet.addAll([
Visibility(
visible: req,
child: SizedBox(
width: textFieldWidth,
child: MIHTextField(
controller: medMainMemController,
hintText: "Main Member",
editable: false,
required: false),
),
),
//const SizedBox(height: 10.0),
Visibility(
visible: req,
child: SizedBox(
width: textFieldWidth,
child: MIHTextField(
controller: medNoController,
hintText: "No.",
editable: false,
required: false),
),
),
//const SizedBox(height: 10.0),
Visibility(
visible: req,
child: SizedBox(
width: textFieldWidth,
child: MIHTextField(
controller: medAidCodeController,
hintText: "Code",
editable: false,
required: false),
),
),
//const SizedBox(height: 10.0),
Visibility(
visible: req,
child: SizedBox(
width: textFieldWidth,
child: MIHTextField(
controller: medNameController,
hintText: "Name",
editable: false,
required: false),
),
),
//const SizedBox(height: 10.0),
Visibility(
visible: req,
child: SizedBox(
width: textFieldWidth,
child: MIHTextField(
controller: medSchemeController,
hintText: "Scheme",
editable: false,
required: false),
),
),
//),
]);
return Wrap(
spacing: 10,
runSpacing: 10,
children: medAidDet,
);
}
List<Widget> setIcons() {
if (widget.type == "personal") {
return [
Text(
"Personal Details",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
IconButton(
icon: const Icon(Icons.edit),
alignment: Alignment.topRight,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
onPressed: () {
Navigator.of(context).pushNamed('/patient-profile/edit',
arguments: PatientEditArguments(
widget.signedInUser, widget.selectedPatient));
},
)
];
} else {
return [
Text(
"Patient Details",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
];
}
}
@override
void dispose() {
idController.dispose();
fnameController.dispose();
lnameController.dispose();
cellController.dispose();
emailController.dispose();
medNameController.dispose();
medNoController.dispose();
medSchemeController.dispose();
addressController.dispose();
medAidController.dispose();
medMainMemController.dispose();
medAidCodeController.dispose();
super.dispose();
}
@override
void initState() {
setState(() {
idController.value = TextEditingValue(text: widget.selectedPatient.id_no);
fnameController.value =
TextEditingValue(text: widget.selectedPatient.first_name);
lnameController.value =
TextEditingValue(text: widget.selectedPatient.last_name);
cellController.value =
TextEditingValue(text: widget.selectedPatient.cell_no);
emailController.value =
TextEditingValue(text: widget.selectedPatient.email);
medNameController.value =
TextEditingValue(text: widget.selectedPatient.medical_aid_name);
medNoController.value =
TextEditingValue(text: widget.selectedPatient.medical_aid_no);
medSchemeController.value =
TextEditingValue(text: widget.selectedPatient.medical_aid_scheme);
addressController.value =
TextEditingValue(text: widget.selectedPatient.address);
medAidController.value =
TextEditingValue(text: widget.selectedPatient.medical_aid);
medMainMemController.value = TextEditingValue(
text: widget.selectedPatient.medical_aid_main_member);
medAidCodeController.value =
TextEditingValue(text: widget.selectedPatient.medical_aid_code);
medAid = widget.selectedPatient.medical_aid;
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
//crossAxisAlignment: ,
children: setIcons(),
),
Divider(color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
const SizedBox(height: 10),
getPatientDetailsField(),
const SizedBox(height: 10),
Text(
"Medical Aid Details",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
Divider(color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
const SizedBox(height: 10),
getMedAidDetailsFields(),
],
);
}
}

View File

@@ -0,0 +1,721 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../main.dart';
import 'package:supertokens_flutter/supertokens.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/arguments.dart';
import '../../mih_objects/patients.dart';
class EditPatient extends StatefulWidget {
final Patient selectedPatient;
final AppUser signedInUser;
const EditPatient({
super.key,
required this.selectedPatient,
required this.signedInUser,
});
@override
State<EditPatient> createState() => _EditPatientState();
}
class _EditPatientState extends State<EditPatient> {
var idController = TextEditingController();
final fnameController = TextEditingController();
final lnameController = TextEditingController();
final cellController = TextEditingController();
final emailController = TextEditingController();
final medNoController = TextEditingController();
final medNameController = TextEditingController();
final medSchemeController = TextEditingController();
final addressController = TextEditingController();
final medAidController = TextEditingController();
final medMainMemController = TextEditingController();
final medAidCodeController = TextEditingController();
final baseAPI = AppEnviroment.baseApiUrl;
final docOfficeIdApiUrl = "${AppEnviroment.baseApiUrl}/users/profile/";
final apiUrlEdit = "${AppEnviroment.baseApiUrl}/patients/update/";
final apiUrlDelete = "${AppEnviroment.baseApiUrl}/patients/delete/";
late int futureDocOfficeId;
late String userEmail;
late bool medRequired;
late double width;
late double height;
final FocusNode _focusNode = FocusNode();
// Future getOfficeIdByUser(String endpoint) async {
// final response = await http.get(Uri.parse(endpoint));
// if (response.statusCode == 200) {
// String body = response.body;
// var decodedData = jsonDecode(body);
// AppUser u = AppUser.fromJson(decodedData as Map<String, dynamic>);
// setState(() {
// //futureDocOfficeId = u.docOffice_id;
// //print(futureDocOfficeId);
// });
// } else {
// internetConnectionPopUp();
// throw Exception('failed to load patients');
// }
// }
Future<void> updatePatientApiCall() async {
//print("Here1");
//userEmail = getLoginUserEmail() as String;
//print(userEmail);
//print("Here2");
//await getOfficeIdByUser(docOfficeIdApiUrl + userEmail);
//print(futureDocOfficeId.toString());
//print("Here3");
var response = await http.put(
Uri.parse(apiUrlEdit),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"id_no": idController.text,
"first_name": fnameController.text,
"last_name": lnameController.text,
"email": emailController.text,
"cell_no": cellController.text,
"medical_aid": medAidController.text,
"medical_aid_main_member": medMainMemController.text,
"medical_aid_no": medNoController.text,
"medical_aid_code": medAidCodeController.text,
"medical_aid_name": medNameController.text,
"medical_aid_scheme": medSchemeController.text,
"address": addressController.text,
"app_id": widget.selectedPatient.app_id,
}),
);
// print("Here4");
// print(response.statusCode);
if (response.statusCode == 200) {
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed('/patient-profile',
arguments: PatientViewArguments(
widget.signedInUser, null, null, null, "personal"));
//Navigator.of(context).pushNamed('/');
String message =
"${fnameController.text} ${lnameController.text}'s information has been updated successfully! Their medical records and details are now current.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
}
Future<void> deletePatientApiCall() async {
//print("Here1");
//userEmail = getLoginUserEmail() as String;
//print(userEmail);
//print("Here2");
//await getOfficeIdByUser(docOfficeIdApiUrl + userEmail);
//print("Office ID: ${futureDocOfficeId.toString()}");
//print("OPatient ID No: ${idController.text}");
//print("Here3");
var response = await http.delete(
Uri.parse(apiUrlDelete),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(
<String, dynamic>{"app_id": widget.selectedPatient.app_id}),
);
//print("Here4");
//print(response.statusCode);
if (response.statusCode == 200) {
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).popAndPushNamed('/patient-profile',
arguments: PatientViewArguments(
widget.signedInUser, null, null, null, "personal"));
String message =
"${fnameController.text} ${lnameController.text}'s record has been deleted successfully. This means it will no longer be visible in patient manager and cannot be used for future appointments.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
}
Future<void> getLoginUserEmail() async {
var uid = await SuperTokens.getUserId();
var response = await http.get(Uri.parse("$baseAPI/user/$uid"));
if (response.statusCode == 200) {
var user = jsonDecode(response.body);
userEmail = user["email"];
}
}
void messagePopUp(error) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(error),
);
},
);
}
void internetConnectionPopUp() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Internet Connection");
},
);
}
void deletePatientPopUp() {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => Dialog(
child: Stack(
children: [
Container(
padding: const EdgeInsets.all(10.0),
width: 700.0,
height: (height / 3) * 2,
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: SingleChildScrollView(
child: Column(
//mainAxisSize: MainAxisSize.max,
children: [
Icon(
Icons.warning_amber_rounded,
size: 100,
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
),
const SizedBox(height: 15),
Text(
"Are you sure you want to delete this?",
textAlign: TextAlign.center,
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: 25.0,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Text(
"This action is permanent! Deleting ${fnameController.text} ${lnameController.text} will remove him\\her from your account. You won't be able to recover it once it's gone.",
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Text(
"Here's what you'll be deleting:",
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: SizedBox(
width: 450,
child: Text(
"1) Patient Profile Information.\n2) Patient Notes\n3) Patient Files.",
textAlign: TextAlign.left,
style: TextStyle(
color: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: deletePatientApiCall,
buttonText: "Delete",
buttonColor: MzanziInnovationHub.of(context)!
.theme
.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!
.theme
.primaryColor(),
))
],
),
),
),
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,
),
),
),
],
),
),
);
}
void successPopUp(String message) {
showDialog(
context: context,
builder: (context) {
return MIHSuccessMessage(
successType: "Success",
successMessage: message,
);
},
);
}
bool isFieldsFilled() {
if (medRequired) {
if (idController.text.isEmpty ||
fnameController.text.isEmpty ||
lnameController.text.isEmpty ||
cellController.text.isEmpty ||
emailController.text.isEmpty ||
medNoController.text.isEmpty ||
medNameController.text.isEmpty ||
medSchemeController.text.isEmpty ||
addressController.text.isEmpty ||
medAidController.text.isEmpty ||
medMainMemController.text.isEmpty ||
medAidCodeController.text.isEmpty) {
return false;
} else {
return true;
}
} else {
if (idController.text.isEmpty ||
fnameController.text.isEmpty ||
lnameController.text.isEmpty ||
cellController.text.isEmpty ||
emailController.text.isEmpty ||
addressController.text.isEmpty ||
medAidController.text.isEmpty) {
return false;
} else {
return true;
}
}
}
void isRequired() {
//print("listerner triggered");
if (medAidController.text == "Yes") {
setState(() {
medRequired = true;
});
} else {
setState(() {
medRequired = false;
});
}
}
Widget displayForm() {
return SingleChildScrollView(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Personal Details",
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 22.0,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
IconButton(
icon: const Icon(Icons.delete),
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
//alignment: Alignment.topRight,
onPressed: () {
deletePatientPopUp();
},
),
],
),
Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
const SizedBox(height: 10.0),
MIHTextField(
controller: idController,
hintText: "13 digit ID Number or Passport",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: fnameController,
hintText: "First Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: lnameController,
hintText: "Last Name",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: cellController,
hintText: "Cell Number",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: emailController,
hintText: "Email",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: addressController,
hintText: "Address",
editable: true,
required: true,
),
const SizedBox(height: 15.0),
Text(
"Medical Aid Details",
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 22.0,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
const SizedBox(height: 10.0),
MIHDropdownField(
controller: medAidController,
hintText: "Medical Aid",
onSelect: (selected) {
if (selected == "Yes") {
setState(() {
medRequired = true;
});
} else {
setState(() {
medRequired = false;
});
}
},
editable: true,
required: true,
dropdownOptions: const ["Yes", "No"],
),
Visibility(
visible: medRequired,
child: Column(
children: [
const SizedBox(height: 10.0),
MIHDropdownField(
controller: medMainMemController,
hintText: "Main Member.",
editable: medRequired,
required: medRequired,
dropdownOptions: const ["Yes", "No"],
),
const SizedBox(height: 10.0),
MIHTextField(
controller: medNoController,
hintText: "Medical Aid No.",
editable: medRequired,
required: medRequired,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: medAidCodeController,
hintText: "Medical Aid Code",
editable: medRequired,
required: medRequired,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: medNameController,
hintText: "Medical Aid Name",
editable: medRequired,
required: medRequired,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: medSchemeController,
hintText: "Medical Aid Scheme",
editable: medRequired,
required: medRequired,
),
],
),
),
const SizedBox(height: 30.0),
SizedBox(
width: 500.0,
height: 50.0,
child: MIHButton(
onTap: () {
submitForm();
},
buttonText: "Update",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
),
],
),
);
}
void submitForm() {
if (isFieldsFilled()) {
if (!medRequired) {
setState(() {
medMainMemController.text = "";
medNoController.text = "";
medAidCodeController.text = "";
medNameController.text = "";
medSchemeController.text = "";
});
}
updatePatientApiCall();
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
}
MIHAction getActionButton() {
return MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pop();
},
);
}
MIHHeader getHeader() {
return const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"Edit Patient Details",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: true,
bodyItems: [
KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
submitForm();
}
},
child: displayForm(),
),
],
);
}
@override
void dispose() {
idController.dispose();
fnameController.dispose();
lnameController.dispose();
cellController.dispose();
emailController.dispose();
medNoController.dispose();
medNameController.dispose();
medSchemeController.dispose();
addressController.dispose();
medAidController.dispose();
medMainMemController.dispose();
medAidCodeController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
getLoginUserEmail();
medAidController.addListener(isRequired);
setState(() {
idController.value = TextEditingValue(text: widget.selectedPatient.id_no);
fnameController.value =
TextEditingValue(text: widget.selectedPatient.first_name);
lnameController.value =
TextEditingValue(text: widget.selectedPatient.last_name);
cellController.value =
TextEditingValue(text: widget.selectedPatient.cell_no);
emailController.value =
TextEditingValue(text: widget.selectedPatient.email);
medNameController.value =
TextEditingValue(text: widget.selectedPatient.medical_aid_name);
medNoController.value =
TextEditingValue(text: widget.selectedPatient.medical_aid_no);
medSchemeController.value =
TextEditingValue(text: widget.selectedPatient.medical_aid_scheme);
addressController.value =
TextEditingValue(text: widget.selectedPatient.address);
medAidController.value =
TextEditingValue(text: widget.selectedPatient.medical_aid);
medMainMemController.value = TextEditingValue(
text: widget.selectedPatient.medical_aid_main_member);
medAidCodeController.value =
TextEditingValue(text: widget.selectedPatient.medical_aid_code);
});
super.initState();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
setState(() {
width = size.width;
height = size.height;
});
return MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(),
secondaryActionButton: null,
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
// return Scaffold(
// // appBar: const MIHAppBar(
// // barTitle: "Edit Patient",
// // propicFile: null,
// // ),
// body: SafeArea(
// child: Stack(
// children: [
// KeyboardListener(
// focusNode: _focusNode,
// autofocus: true,
// onKeyEvent: (event) async {
// if (event is KeyDownEvent &&
// event.logicalKey == LogicalKeyboardKey.enter) {
// submitForm();
// }
// },
// child: displayForm(),
// ),
// Positioned(
// top: 10,
// left: 5,
// width: 50,
// height: 50,
// child: IconButton(
// onPressed: () {
// Navigator.of(context).pop();
// },
// icon: const Icon(Icons.arrow_back),
// ),
// ),
// Positioned(
// top: 10,
// right: 5,
// width: 50,
// height: 50,
// child: IconButton(
// icon: const Icon(Icons.delete),
// color:
// MzanziInnovationHub.of(context)!.theme.secondaryColor(),
// //alignment: Alignment.topRight,
// onPressed: () {
// deletePatientPopUp();
// },
// ))
// ],
// ),
// ),
// );
}
}

View File

@@ -0,0 +1,543 @@
import 'dart:async';
import 'dart:convert';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import 'package:http/http.dart' as http2;
import 'package:supertokens_flutter/supertokens.dart';
import '../../mih_components/med_cert_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_file_input.dart';
import '../../mih_components/mih_layout/mih_window.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/business.dart';
import '../../mih_objects/business_user.dart';
import '../../mih_objects/files.dart';
import '../../mih_objects/patients.dart';
import 'builder/build_files_list.dart';
import 'prescip_input.dart';
class PatientFiles extends StatefulWidget {
final int patientIndex;
final Patient selectedPatient;
final AppUser signedInUser;
final Business? business;
final BusinessUser? businessUser;
final String type;
const PatientFiles({
super.key,
required this.patientIndex,
required this.selectedPatient,
required this.signedInUser,
required this.business,
required this.businessUser,
required this.type,
});
@override
State<PatientFiles> createState() => _PatientFilesState();
}
class _PatientFilesState extends State<PatientFiles> {
String endpointFiles = "${AppEnviroment.baseApiUrl}/files/patients/";
String endpointUser = "${AppEnviroment.baseApiUrl}/users/profile/";
String endpointGenFiles =
"${AppEnviroment.baseApiUrl}/files/generate/med-cert/";
String endpointFileUpload = "${AppEnviroment.baseApiUrl}/files/upload/file/";
String endpointInsertFiles = "${AppEnviroment.baseApiUrl}/files/insert/";
final startDateController = TextEditingController();
final endDateTextController = TextEditingController();
final retDateTextController = TextEditingController();
final selectedFileController = TextEditingController();
final medicineController = TextEditingController();
final quantityController = TextEditingController();
final dosageController = TextEditingController();
final timesDailyController = TextEditingController();
final noDaysController = TextEditingController();
final noRepeatsController = TextEditingController();
final outputController = TextEditingController();
late Future<List<PFile>> futueFiles;
late String userEmail = "";
late PlatformFile selected;
final baseAPI = AppEnviroment.baseApiUrl;
Future<void> generateMedCert() async {
//start loading circle
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
var response1 = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/minio/generate/med-cert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"app_id": widget.selectedPatient.app_id,
"fullName":
"${widget.selectedPatient.first_name} ${widget.selectedPatient.last_name}",
"id_no": widget.selectedPatient.id_no,
"docfname":
"DR. ${widget.signedInUser.fname} ${widget.signedInUser.lname}",
"startDate": startDateController.text,
"busName": widget.business!.Name,
"busAddr": "*TO BE ADDED IN THE FUTURE*",
"busNo": widget.business!.contact_no,
"busEmail": widget.business!.bus_email,
"endDate": endDateTextController.text,
"returnDate": retDateTextController.text,
"logo_path": widget.business!.logo_path,
"sig_path": widget.businessUser!.sig_path,
}),
);
//print(response1.statusCode);
DateTime now = new DateTime.now();
DateTime date = new DateTime(now.year, now.month, now.day);
String fileName =
"Med-Cert-${widget.selectedPatient.first_name} ${widget.selectedPatient.last_name}-${date.toString().substring(0, 10)}.pdf";
if (response1.statusCode == 200) {
var response2 = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/files/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"file_path":
"${widget.selectedPatient.app_id}/patient_files/$fileName",
"file_name": fileName,
"app_id": widget.selectedPatient.app_id
}),
);
//print(response2.statusCode);
if (response2.statusCode == 201) {
setState(() {
startDateController.clear();
endDateTextController.clear();
retDateTextController.clear();
futueFiles = fetchFiles();
});
// end loading circle
Navigator.of(context).pop();
Navigator.of(context).pop();
String message =
"The medical certificate $fileName has been successfully generated and added to ${widget.selectedPatient.first_name} ${widget.selectedPatient.last_name}'s record. You can now access and download it for their use.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
} else {
internetConnectionPopUp();
}
}
Future<void> uploadSelectedFile(PlatformFile file) async {
//var strem = new http.ByteStream.fromBytes(file.bytes.)
//start loading circle
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
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.selectedPatient.app_id;
request.fields['folder'] = "patient_files";
request.files.add(await http2.MultipartFile.fromBytes('file', file.bytes!,
filename: file.name.replaceAll(RegExp(r' '), '-')));
//print("here2");
var response1 = await request.send();
//print("here3");
//print(response1.statusCode);
//print(response1.toString());
if (response1.statusCode == 200) {
//print("here3");
var fname = file.name.replaceAll(RegExp(r' '), '-');
var filePath = "${widget.selectedPatient.app_id}/patient_files/$fname";
var response2 = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/files/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"file_path": filePath,
"file_name": fname,
"app_id": widget.selectedPatient.app_id
}),
);
//print("here5");
//print(response2.statusCode);
if (response2.statusCode == 201) {
setState(() {
selectedFileController.clear();
futueFiles = fetchFiles();
});
// end loading circle
Navigator.of(context).pop();
String message =
"The file ${file.name.replaceAll(RegExp(r' '), '-')} has been successfully generated and added to ${widget.selectedPatient.first_name} ${widget.selectedPatient.last_name}'s record. You can now access and download it for their use.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
} else {
internetConnectionPopUp();
}
}
Future<List<PFile>> fetchFiles() async {
final response = await http.get(Uri.parse(
"${AppEnviroment.baseApiUrl}/files/patients/${widget.selectedPatient.app_id}"));
//print(response.statusCode);
//print(response.body);
if (response.statusCode == 200) {
Iterable l = jsonDecode(response.body);
List<PFile> files =
List<PFile>.from(l.map((model) => PFile.fromJson(model)));
return files;
} else {
internetConnectionPopUp();
throw Exception('failed to load patients');
}
}
void successPopUp(String message) {
showDialog(
context: context,
builder: (context) {
return MIHSuccessMessage(
successType: "Success",
successMessage: message,
);
},
);
}
void messagePopUp(error) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(error),
);
},
);
}
void medCertPopUp() {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Create Medical Certificate",
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
},
windowBody: [
Medcertinput(
startDateController: startDateController,
endDateTextController: endDateTextController,
retDateTextController: retDateTextController,
),
const SizedBox(height: 15.0),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
buttonText: "Generate",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () async {
if (isMedCertFieldsFilled()) {
await generateMedCert();
//Navigator.pop(context);
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
},
),
)
],
),
);
}
void prescritionPopUp() {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Create Prescription",
windowTools: const [],
onWindowTapClose: () {
medicineController.clear();
quantityController.clear();
dosageController.clear();
timesDailyController.clear();
noDaysController.clear();
noRepeatsController.clear();
Navigator.pop(context);
},
windowBody: [
PrescripInput(
medicineController: medicineController,
quantityController: quantityController,
dosageController: dosageController,
timesDailyController: timesDailyController,
noDaysController: noDaysController,
noRepeatsController: noRepeatsController,
outputController: outputController,
selectedPatient: widget.selectedPatient,
signedInUser: widget.signedInUser,
business: widget.business,
businessUser: widget.businessUser,
),
],
),
);
}
void uploudFilePopUp() {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Upload File",
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
},
windowBody: [
MIHFileField(
controller: selectedFileController,
hintText: "Select File",
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(() {
selected = selectedFile;
});
setState(() {
selectedFileController.text = selectedFile.name;
});
},
),
const SizedBox(height: 15),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
buttonText: "Add File",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
if (isFileFieldsFilled()) {
uploadSelectedFile(selected);
Navigator.pop(context);
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
},
),
)
],
),
);
}
void internetConnectionPopUp() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Internet Connection");
},
);
}
bool isMedCertFieldsFilled() {
if (startDateController.text.isEmpty ||
endDateTextController.text.isEmpty ||
retDateTextController.text.isEmpty) {
return false;
} else {
return true;
}
}
bool isFileFieldsFilled() {
if (selectedFileController.text.isEmpty) {
return false;
} else {
return true;
}
}
List<Widget> setIcons() {
if (widget.type == "personal") {
return [
Text(
"Documents",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
IconButton(
onPressed: () {
uploudFilePopUp();
},
icon: Icon(
Icons.add,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
)
];
} else {
return [
Text(
"Documents",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
IconButton(
onPressed: () {
medCertPopUp();
},
icon: Icon(
Icons.sick_outlined,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
IconButton(
onPressed: () {
prescritionPopUp();
},
icon: Icon(
Icons.medication_outlined,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
IconButton(
onPressed: () {
uploudFilePopUp();
},
icon: Icon(
Icons.add,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
)
];
}
}
@override
void dispose() {
startDateController.dispose();
endDateTextController.dispose();
retDateTextController.dispose();
selectedFileController.dispose();
medicineController.dispose();
quantityController.dispose();
dosageController.dispose();
timesDailyController.dispose();
noDaysController.dispose();
noRepeatsController.dispose();
outputController.dispose();
super.dispose();
}
@override
void initState() {
futueFiles = fetchFiles();
//patientDetails = getPatientDetails() as Patient;
//getUserDetails();
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: futueFiles,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: Mihloadingcircle(),
);
} else if (snapshot.hasData) {
final filesList = snapshot.data!;
return Column(children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: setIcons(),
),
Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
const SizedBox(height: 10),
BuildFilesList(
files: filesList,
signedInUser: widget.signedInUser,
selectedPatient: widget.selectedPatient,
business: widget.business,
businessUser: widget.businessUser,
type: widget.type,
),
]);
} else {
return const Center(
child: Text("Error Loading Notes"),
);
}
},
);
}
}

View File

@@ -0,0 +1,679 @@
import 'dart:async';
import 'package:flutter_swipe_detector/flutter_swipe_detector.dart';
import 'package:intl/intl.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../main.dart';
import '../../mih_apis/mih_api_calls.dart';
import '../../mih_components/mih_calendar.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_search_input.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/arguments.dart';
import '../../mih_objects/patient_access.dart';
import '../../mih_objects/patient_queue.dart';
import '../../mih_objects/patients.dart';
import 'builder/build_patient_access_list.dart';
import 'builder/build_patient_list.dart';
import 'builder/build_patient_queue_list.dart';
class PatientManager extends StatefulWidget {
//final AppUser signedInUser;
final BusinessArguments arguments;
const PatientManager({
super.key,
required this.arguments,
});
@override
State<PatientManager> createState() => _PatientManagerState();
}
//
class _PatientManagerState extends State<PatientManager> {
TextEditingController searchController = TextEditingController();
TextEditingController accessSearchController = TextEditingController();
TextEditingController queueDateController = TextEditingController();
String baseUrl = AppEnviroment.baseApiUrl;
final FocusNode _focusNode = FocusNode();
String searchString = "";
String accessSearchString = "";
var now = DateTime.now();
var formatter = DateFormat('yyyy-MM-dd');
late String formattedDate;
bool start = true;
int _selectedIndex = 0;
late Future<List<Patient>> patientSearchResults;
late Future<List<PatientAccess>> patientAccessResults;
late Future<List<PatientQueue>> patientQueueResults;
//Waiting Room Widgets/ Functions
List<PatientQueue> filterQueueResults(
List<PatientQueue> queueList, String query) {
List<PatientQueue> templist = [];
//print(query);
for (var item in queueList) {
if (item.date_time.contains(query)) {
//print(item.medical_aid_no);
templist.add(item);
}
}
//print(templist);
return templist;
}
Widget displayQueueList(List<PatientQueue> patientQueueList) {
if (patientQueueList.isNotEmpty) {
return BuildPatientQueueList(
patientQueue: patientQueueList,
signedInUser: widget.arguments.signedInUser,
business: widget.arguments.business,
businessUser: widget.arguments.businessUser,
);
}
return Padding(
padding: const EdgeInsets.only(top: 35.0),
child: Center(
child: Text(
"No Appointments for $formattedDate",
style: TextStyle(
fontSize: 25,
color: MzanziInnovationHub.of(context)!.theme.messageTextColor()),
textAlign: TextAlign.center,
),
),
);
}
Widget patientQueue() {
return Column(mainAxisSize: MainAxisSize.max, children: [
//const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"Waiting Room",
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
),
IconButton(
iconSize: 20,
icon: const Icon(
Icons.refresh,
),
onPressed: () {
refreshQueue();
},
),
],
),
Divider(color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
MIHCalendar(
calendarWidth: 500,
rowHeight: 35,
setDate: (value) {
setState(() {
queueDateController.text = value;
});
}),
//spacer
FutureBuilder(
future: patientQueueResults,
builder: (context, snapshot) {
//print("patient Queue List ${snapshot.hasData}");
if (snapshot.connectionState == ConnectionState.waiting) {
return const Mihloadingcircle();
} else if (snapshot.connectionState == ConnectionState.done) {
List<PatientQueue> patientQueueList;
// if (searchString == "") {
// patientQueueList = [];
// } else {
patientQueueList = filterQueueResults(
snapshot.requireData, queueDateController.text);
// print(patientQueueList);
// }
return displayQueueList(patientQueueList);
} else {
return Center(
child: Text(
"Error pulling Patients Data\n$baseUrl/patients/search/$searchString",
style: TextStyle(
fontSize: 25,
color: MzanziInnovationHub.of(context)!.theme.errorColor()),
textAlign: TextAlign.center,
),
);
}
},
),
]);
}
void refreshQueue() {
setState(() {
start = true;
});
checkforchange();
}
void checkforchange() {
if (start == true) {
setState(() {
patientQueueResults = MIHApiCalls.fetchBusinessAppointmentsAPICall(
queueDateController.text, widget.arguments.business!.business_id);
start = false;
});
}
if (formattedDate != queueDateController.text) {
setState(() {
patientQueueResults = MIHApiCalls.fetchBusinessAppointmentsAPICall(
queueDateController.text, widget.arguments.business!.business_id);
formattedDate = queueDateController.text;
});
}
}
//Patient Lookup Widgets/ Functions
List<Patient> filterSearchResults(List<Patient> patList, String query) {
List<Patient> templist = [];
//print(query);
for (var item in patList) {
if (item.id_no.contains(searchString) ||
item.medical_aid_no.contains(searchString)) {
//print(item.medical_aid_no);
templist.add(item);
}
}
return templist;
}
Widget displayPatientList(List<Patient> patientsList, String searchString) {
if (patientsList.isNotEmpty) {
return BuildPatientsList(
patients: patientsList,
signedInUser: widget.arguments.signedInUser,
business: widget.arguments.business,
arguments: widget.arguments,
);
} else if (patientsList.isEmpty && searchString != "") {
return Padding(
padding: const EdgeInsets.only(top: 35.0),
child: Center(
child: Text(
"No ID or Medical Aid No. matches a Patient",
style: TextStyle(
fontSize: 25,
color:
MzanziInnovationHub.of(context)!.theme.messageTextColor()),
textAlign: TextAlign.center,
),
),
);
} else {
return Padding(
padding: const EdgeInsets.only(top: 35.0),
child: Center(
child: Text(
"Enter ID or Medical Aid No. of Patient",
style: TextStyle(
fontSize: 25,
color:
MzanziInnovationHub.of(context)!.theme.messageTextColor()),
textAlign: TextAlign.center,
),
),
);
}
}
Widget patientSearch() {
return KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
submitPatientForm();
}
},
child: Column(mainAxisSize: MainAxisSize.max, children: [
const Text(
"Patient Lookup",
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
),
Divider(color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
//spacer
const SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
flex: 1,
child: MIHSearchField(
controller: searchController,
hintText: "ID or Medical Aid No. Search",
required: false,
editable: true,
onTap: () {
submitPatientForm();
},
),
),
IconButton(
onPressed: () {
setState(() {
searchController.clear();
searchString = "";
});
submitPatientForm();
},
icon: const Icon(
Icons.filter_alt_off,
size: 25,
))
],
),
//spacer
const SizedBox(height: 10),
FutureBuilder(
future: patientSearchResults,
builder: (context, snapshot) {
//print("patient Liust ${snapshot.data}");
if (snapshot.connectionState == ConnectionState.waiting) {
return const Mihloadingcircle();
} else if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
List<Patient> patientsList;
if (searchString == "") {
patientsList = [];
} else {
patientsList =
filterSearchResults(snapshot.data!, searchString);
//print(patientsList);
}
return displayPatientList(patientsList, searchString);
} else {
return Center(
child: Text(
"Error pulling Patients Data\n$baseUrl/patients/search/$searchString",
style: TextStyle(
fontSize: 25,
color:
MzanziInnovationHub.of(context)!.theme.errorColor()),
textAlign: TextAlign.center,
),
);
}
},
),
]),
);
}
void submitPatientForm() {
if (searchController.text != "") {
setState(() {
searchString = searchController.text;
patientSearchResults = MIHApiCalls.fetchPatients(searchString);
});
}
// else {
// showDialog(
// context: context,
// builder: (context) {
// return const MIHErrorMessage(errorType: "Input Error");
// },
// );
// }
}
//Patient Access Widgets/ Functions
List<PatientAccess> filterAccessResults(
List<PatientAccess> patAccList, String query) {
List<PatientAccess> templist = [];
//print(query);
for (var item in patAccList) {
if (item.id_no.contains(query)) {
//print(item.medical_aid_no);
templist.add(item);
}
}
return templist;
}
Widget displayPatientAccessList(List<PatientAccess> patientsAccessList) {
if (patientsAccessList.isNotEmpty) {
return BuildPatientAccessList(
patientAccesses: patientsAccessList,
signedInUser: widget.arguments.signedInUser,
business: widget.arguments.business,
arguments: widget.arguments,
);
}
return Padding(
padding: const EdgeInsets.only(top: 35.0),
child: Center(
child: Text(
"No Patients matching search",
style: TextStyle(
fontSize: 25,
color: MzanziInnovationHub.of(context)!.theme.messageTextColor()),
textAlign: TextAlign.center,
),
),
);
}
Widget patientAccessSearch() {
return KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
submitPatientAccessForm();
}
},
child: Column(mainAxisSize: MainAxisSize.max, children: [
const Text(
"My Patient List",
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
),
Divider(color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
//spacer
const SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
flex: 1,
child: MIHSearchField(
controller: accessSearchController,
hintText: "ID Search",
required: false,
editable: true,
onTap: () {
submitPatientAccessForm();
},
),
),
IconButton(
onPressed: () {
setState(() {
accessSearchController.clear();
accessSearchString = "";
});
submitPatientAccessForm();
},
icon: const Icon(
Icons.filter_alt_off,
size: 25,
))
],
),
//spacer
const SizedBox(height: 10),
FutureBuilder(
future: patientAccessResults,
builder: (context, snapshot) {
//print("patient Liust ${snapshot.data}");
if (snapshot.connectionState == ConnectionState.waiting) {
return const Mihloadingcircle();
} else if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
List<PatientAccess> patientsAccessList;
if (accessSearchString == "") {
patientsAccessList = snapshot.data!;
} else {
patientsAccessList =
filterAccessResults(snapshot.data!, accessSearchString);
//print(patientsList);
}
return displayPatientAccessList(patientsAccessList);
} else {
return Center(
child: Text(
"Error pulling Patient Access Data\n$baseUrl/access-requests/business/patient/${widget.arguments.business!.business_id}",
style: TextStyle(
fontSize: 25,
color:
MzanziInnovationHub.of(context)!.theme.errorColor()),
textAlign: TextAlign.center,
),
);
}
},
),
]),
);
}
void submitPatientAccessForm() {
// if (searchController.text != "") {
setState(() {
accessSearchString = accessSearchController.text;
patientAccessResults = MIHApiCalls.getPatientAccessListOfBusiness(
widget.arguments.business!.business_id);
});
// } else {
// showDialog(
// context: context,
// builder: (context) {
// return const MIHErrorMessage(errorType: "Input Error");
// },
// );
// }
}
//Layout Widgets/ Functions
Widget showSelection(int index) {
if (index == 0) {
return SizedBox(
//width: 660,
child: patientQueue(),
);
} else if (index == 1) {
return SizedBox(
//width: 660,
child: patientAccessSearch(),
);
} else {
return SizedBox(
//width: 660,
child: patientSearch(),
);
}
}
MIHAction getActionButton() {
return MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).popAndPushNamed(
'/',
arguments: false,
);
},
);
}
MIHHeader getHeader() {
return MIHHeader(
headerAlignment: MainAxisAlignment.end,
headerItems: [
//============ Waiting Room ================
Visibility(
visible: _selectedIndex != 0,
child: IconButton(
onPressed: () {
setState(() {
_selectedIndex = 0;
});
},
icon: const Icon(
Icons.people,
size: 35,
),
),
),
Visibility(
visible: _selectedIndex == 0,
child: IconButton.filled(
onPressed: () {
setState(() {
_selectedIndex = 0;
});
},
icon: const Icon(
Icons.people,
size: 35,
),
),
),
//============ My Patient List ================
Visibility(
visible: _selectedIndex != 1,
child: IconButton(
onPressed: () {
setState(() {
_selectedIndex = 1;
});
},
icon: const Icon(
Icons.check_box_outlined,
size: 35,
),
),
),
Visibility(
visible: _selectedIndex == 1,
child: IconButton.filled(
onPressed: () {
setState(() {
_selectedIndex = 1;
});
},
icon: const Icon(
Icons.check_box_outlined,
size: 35,
),
),
),
//============ Patient Lookup ================
Visibility(
visible: _selectedIndex != 2,
child: IconButton(
onPressed: () {
setState(() {
_selectedIndex = 2;
});
},
icon: const Icon(
Icons.search,
size: 35,
),
),
),
Visibility(
visible: _selectedIndex == 2,
child: IconButton.filled(
onPressed: () {
setState(() {
_selectedIndex = 2;
});
},
icon: const Icon(
Icons.search,
size: 35,
),
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: true,
bodyItems: [
showSelection(_selectedIndex),
],
);
}
@override
void dispose() {
searchController.dispose();
accessSearchController.dispose();
queueDateController.dispose();
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
patientSearchResults = MIHApiCalls.fetchPatients("abc");
patientAccessResults = MIHApiCalls.getPatientAccessListOfBusiness(
widget.arguments.business!.business_id);
queueDateController.addListener(checkforchange);
setState(() {
formattedDate = formatter.format(now);
queueDateController.text = formattedDate;
});
super.initState();
}
@override
Widget build(BuildContext context) {
return SwipeDetector(
onSwipeLeft: (offset) {
if (_selectedIndex < 2) {
setState(() {
_selectedIndex += 1;
});
}
//print("swipe left");
},
onSwipeRight: (offset) {
if (_selectedIndex > 0) {
setState(() {
_selectedIndex -= 1;
});
}
//print("swipe right");
},
child: MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(),
secondaryActionButton: null,
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
),
);
}
}

View File

@@ -0,0 +1,455 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_multiline_text_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_text_input.dart';
import '../../mih_components/mih_layout/mih_window.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/business.dart';
import '../../mih_objects/business_user.dart';
import '../../mih_objects/notes.dart';
import '../../mih_objects/patients.dart';
import 'builder/build_notes_list.dart';
class PatientNotes extends StatefulWidget {
final String patientAppId;
final Patient selectedPatient;
final AppUser signedInUser;
final Business? business;
final BusinessUser? businessUser;
final String type;
const PatientNotes({
super.key,
required this.patientAppId,
required this.selectedPatient,
required this.signedInUser,
required this.business,
required this.businessUser,
required this.type,
});
@override
State<PatientNotes> createState() => _PatientNotesState();
}
class _PatientNotesState extends State<PatientNotes> {
String endpoint = "${AppEnviroment.baseApiUrl}/notes/patients/";
String apiUrlAddNote = "${AppEnviroment.baseApiUrl}/notes/insert/";
final titleController = TextEditingController();
final noteTextController = TextEditingController();
final officeController = TextEditingController();
final dateController = TextEditingController();
final doctorController = TextEditingController();
late Future<List<Note>> futueNotes;
Future<List<Note>> fetchNotes(String endpoint) async {
final response = await http.get(Uri.parse(
"${AppEnviroment.baseApiUrl}/notes/patients/${widget.selectedPatient.app_id}"));
if (response.statusCode == 200) {
Iterable l = jsonDecode(response.body);
List<Note> notes =
List<Note>.from(l.map((model) => Note.fromJson(model)));
//print("Here notes");
return notes;
} else {
internetConnectionPopUp();
throw Exception('failed to load patients');
}
}
Future<void> addPatientNoteAPICall() async {
// String title = "";
// if (widget.businessUser!.title == "Doctor") {
// title = "Dr.";
// }
var response = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/notes/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"note_name": titleController.text,
"note_text": noteTextController.text,
"doc_office": officeController.text,
"doctor": doctorController.text,
"app_id": widget.selectedPatient.app_id,
}),
);
if (response.statusCode == 201) {
setState(() {
futueNotes = fetchNotes(endpoint + widget.patientAppId.toString());
});
// Navigator.of(context)
// .pushNamed('/patient-manager', arguments: widget.userEmail);
String message =
"Your note has been successfully added to the patients medical record. You can now view it alongside their other important information.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
}
void successPopUp(String message) {
showDialog(
context: context,
builder: (context) {
return MIHSuccessMessage(
successType: "Success",
successMessage: message,
);
},
);
}
void internetConnectionPopUp() {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Internet Connection");
},
);
}
void messagePopUp(error) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(error),
);
},
);
}
void addNotePopUp() {
DateTime now = new DateTime.now();
DateTime date = new DateTime(now.year, now.month, now.day);
var title = "";
if (widget.businessUser!.title == "Doctor") {
title = "Dr.";
}
setState(() {
officeController.text = widget.business!.Name;
doctorController.text =
"$title ${widget.signedInUser.fname} ${widget.signedInUser.lname}";
dateController.text = date.toString().substring(0, 10);
});
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => MIHWindow(
fullscreen: false,
windowTitle: "Add Note",
windowTools: const [],
onWindowTapClose: () {
Navigator.pop(context);
titleController.clear();
noteTextController.clear();
},
windowBody: [
MIHTextField(
controller: officeController,
hintText: "Office",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: doctorController,
hintText: "Created By",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: dateController,
hintText: "Created Date",
editable: false,
required: true,
),
const SizedBox(height: 10.0),
MIHTextField(
controller: titleController,
hintText: "Note Title",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
SizedBox(
//width: 700,
height: 250,
child: MIHMLTextField(
controller: noteTextController,
hintText: "Note Details",
editable: true,
required: true,
),
),
const SizedBox(height: 15.0),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: () {
if (isFieldsFilled()) {
addPatientNoteAPICall();
Navigator.pop(context);
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
},
buttonText: "Add Note",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
)
],
),
);
// showDialog(
// context: context,
// barrierDismissible: false,
// builder: (context) => Dialog(
// child: Stack(
// children: [
// Container(
// padding: const EdgeInsets.all(10.0),
// width: 700.0,
// //height: 500.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: SingleChildScrollView(
// padding: const EdgeInsets.symmetric(horizontal: 10),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: [
// Text(
// "Add Note",
// textAlign: TextAlign.center,
// style: TextStyle(
// color: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// fontSize: 35.0,
// fontWeight: FontWeight.bold,
// ),
// ),
// const SizedBox(height: 25.0),
// SizedBox(
// width: 700,
// child: MIHTextField(
// controller: officeController,
// hintText: "Office",
// editable: false,
// required: true,
// ),
// ),
// const SizedBox(height: 10.0),
// SizedBox(
// width: 700,
// child: MIHTextField(
// controller: doctorController,
// hintText: "Created By",
// editable: false,
// required: true,
// ),
// ),
// const SizedBox(height: 10.0),
// SizedBox(
// width: 700,
// child: MIHTextField(
// controller: dateController,
// hintText: "Created Date",
// editable: false,
// required: true,
// ),
// ),
// const SizedBox(height: 10.0),
// SizedBox(
// width: 700,
// child: MIHTextField(
// controller: titleController,
// hintText: "Note Title",
// editable: true,
// required: true,
// ),
// ),
// const SizedBox(height: 10.0),
// SizedBox(
// width: 700,
// height: 250,
// child: MIHMLTextField(
// controller: noteTextController,
// hintText: "Note Details",
// editable: true,
// required: true,
// ),
// ),
// const SizedBox(height: 30.0),
// SizedBox(
// width: 300,
// height: 50,
// child: MIHButton(
// onTap: () {
// if (isFieldsFilled()) {
// addPatientNoteAPICall();
// Navigator.pop(context);
// } else {
// showDialog(
// context: context,
// builder: (context) {
// return const MIHErrorMessage(
// errorType: "Input Error");
// },
// );
// }
// },
// buttonText: "Add Note",
// buttonColor: MzanziInnovationHub.of(context)!
// .theme
// .secondaryColor(),
// textColor: MzanziInnovationHub.of(context)!
// .theme
// .primaryColor(),
// ),
// )
// ],
// ),
// ),
// ),
// Positioned(
// top: 5,
// right: 5,
// width: 50,
// height: 50,
// child: IconButton(
// onPressed: () {
// Navigator.pop(context);
// titleController.clear();
// noteTextController.clear();
// },
// icon: Icon(
// Icons.close,
// color: MzanziInnovationHub.of(context)!.theme.errorColor(),
// size: 35,
// ),
// ),
// ),
// ],
// ),
// ),
// );
}
bool isFieldsFilled() {
if (titleController.text.isEmpty || noteTextController.text.isEmpty) {
return false;
} else {
return true;
}
}
List<Widget> setIcons() {
if (widget.type == "personal") {
return [
Text(
"Notes",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
),
];
} else {
return [
Text(
"Notes",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
),
IconButton(
onPressed: () {
addNotePopUp();
},
icon: Icon(Icons.add,
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
)
];
}
}
@override
void dispose() {
titleController.dispose();
noteTextController.dispose();
super.dispose();
}
@override
void initState() {
futueNotes = fetchNotes(endpoint + widget.patientAppId);
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: futueNotes,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: Mihloadingcircle(),
);
} else if (snapshot.hasData) {
final notesList = snapshot.data!;
return Column(children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: setIcons(),
),
Divider(
color: MzanziInnovationHub.of(context)!.theme.secondaryColor()),
const SizedBox(height: 10),
BuildNotesList(
notes: notesList,
signedInUser: widget.signedInUser,
selectedPatient: widget.selectedPatient,
business: widget.business,
businessUser: widget.businessUser,
type: widget.type,
),
]);
} else {
return const Center(
child: Text("Error Loading Notes"),
);
}
},
);
}
}

View File

@@ -0,0 +1,355 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_swipe_detector/flutter_swipe_detector.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/arguments.dart';
import '../../mih_objects/patients.dart';
import 'patient_details.dart';
import 'patient_files.dart';
import 'patient_notes.dart';
class PatientView extends StatefulWidget {
//final AppUser signedInUser;
final PatientViewArguments arguments;
const PatientView({
super.key,
required this.arguments,
});
@override
State<PatientView> createState() => _PatientViewState();
}
class _PatientViewState extends State<PatientView> {
int _selectedIndex = 0;
late double popUpWidth;
late double? popUpheight;
late double popUpTitleSize;
late double popUpSubtitleSize;
late double popUpBodySize;
late double popUpIconSize;
late double popUpPaddingSize;
late double width;
late double height;
Future<Patient?> fetchPatient() async {
//print("Patien manager page: $endpoint");
var patientAppId = widget.arguments.selectedPatient!.app_id;
final response = await http
.get(Uri.parse("${AppEnviroment.baseApiUrl}/patients/$patientAppId"));
// print("Here");
// print("Body: ${response.body}");
// print("Code: ${response.statusCode}");
// var errorCode = response.statusCode.toString();
// var errorBody = response.body;
if (response.statusCode == 200) {
//print("Here1");
var decodedData = jsonDecode(response.body);
// print("Here2");
Patient patients = Patient.fromJson(decodedData as Map<String, dynamic>);
// print("Here3");
// print(patients);
return patients;
}
return null;
}
void checkScreenSize() {
if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") {
setState(() {
popUpWidth = (width / 4) * 2;
popUpheight = null;
});
} else {
setState(() {
popUpWidth = width - (width * 0.1);
popUpheight = null;
});
}
}
Widget showSelection(int index) {
if (index == 0) {
return PatientDetails(
signedInUser: widget.arguments.signedInUser,
selectedPatient: widget.arguments.selectedPatient!,
type: widget.arguments.type,
);
} else if (index == 1) {
return PatientNotes(
patientAppId: widget.arguments.selectedPatient!.app_id,
selectedPatient: widget.arguments.selectedPatient!,
signedInUser: widget.arguments.signedInUser,
business: widget.arguments.business,
businessUser: widget.arguments.businessUser,
type: widget.arguments.type,
);
} else {
return PatientFiles(
patientIndex: widget.arguments.selectedPatient!.idpatients,
selectedPatient: widget.arguments.selectedPatient!,
signedInUser: widget.arguments.signedInUser,
business: widget.arguments.business,
businessUser: widget.arguments.businessUser,
type: widget.arguments.type,
);
}
}
MIHAction getActionButton() {
if (widget.arguments.type == "Personal") {
return MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).popAndPushNamed(
'/',
arguments: true,
);
},
);
} else {
return MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pop();
},
);
}
}
MIHHeader getHeader() {
return MIHHeader(
headerAlignment: MainAxisAlignment.end,
headerItems: [
//============ Patient Details ================
Visibility(
visible: _selectedIndex != 0,
child: IconButton(
onPressed: () {
setState(() {
_selectedIndex = 0;
});
},
icon: const Icon(
Icons.perm_identity,
size: 35,
),
),
),
Visibility(
visible: _selectedIndex == 0,
child: IconButton.filled(
iconSize: 35,
onPressed: () {
setState(() {
_selectedIndex = 0;
});
},
icon: const Icon(
Icons.perm_identity,
),
),
),
//============ Patient Notes ================
Visibility(
visible: _selectedIndex != 1,
child: IconButton(
onPressed: () {
setState(() {
_selectedIndex = 1;
});
},
icon: const Icon(
Icons.article_outlined,
size: 35,
),
),
),
Visibility(
visible: _selectedIndex == 1,
child: IconButton.filled(
onPressed: () {
setState(() {
_selectedIndex = 1;
});
},
icon: const Icon(
Icons.article_outlined,
size: 35,
),
),
),
//============ Patient Files ================
Visibility(
visible: _selectedIndex != 2,
child: IconButton(
onPressed: () {
setState(() {
_selectedIndex = 2;
});
},
icon: const Icon(
Icons.file_present,
size: 35,
),
),
),
Visibility(
visible: _selectedIndex == 2,
child: IconButton.filled(
onPressed: () {
setState(() {
_selectedIndex = 2;
});
},
icon: const Icon(
Icons.file_present,
size: 35,
),
),
),
],
);
}
MIHBody getBody() {
return MIHBody(
borderOn: true,
bodyItems: [showSelection(_selectedIndex)],
);
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
setState(() {
width = size.width;
height = size.height;
});
checkScreenSize();
return SwipeDetector(
onSwipeLeft: (offset) {
if (_selectedIndex < 2) {
setState(() {
_selectedIndex += 1;
});
}
//print("swipe left");
},
onSwipeRight: (offset) {
if (_selectedIndex > 0) {
setState(() {
_selectedIndex -= 1;
});
}
//print("swipe right");
},
child: MIHLayoutBuilder(
actionButton: getActionButton(),
header: getHeader(),
secondaryActionButton: null,
body: getBody(),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
),
);
// return Scaffold(
// body: SafeArea(
// child: SingleChildScrollView(
// child: Stack(
// children: [
// Container(
// width: width,
// height: height,
// padding: const EdgeInsets.symmetric(
// vertical: 10.0, horizontal: 15.0),
// child: Column(
// mainAxisSize: MainAxisSize.max,
// children: [
// Row(
// crossAxisAlignment: CrossAxisAlignment.end,
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// IconButton(
// onPressed: () {
// setState(() {
// _selectedIndex = 0;
// });
// },
// icon: const Icon(
// Icons.perm_identity,
// size: 35,
// ),
// ),
// IconButton(
// onPressed: () {
// setState(() {
// _selectedIndex = 1;
// });
// },
// icon: const Icon(
// Icons.article_outlined,
// size: 35,
// ),
// ),
// IconButton(
// onPressed: () {
// setState(() {
// _selectedIndex = 2;
// });
// },
// icon: const Icon(
// Icons.file_present,
// size: 35,
// ),
// ),
// ],
// ),
// const SizedBox(
// height: 10.0,
// ),
// showSelection(_selectedIndex),
// ],
// ),
// ),
// Positioned(
// top: 10,
// left: 5,
// width: 50,
// height: 50,
// child: IconButton(
// onPressed: () {
// Navigator.of(context).pop();
// },
// icon: const Icon(Icons.arrow_back),
// ),
// )
// ],
// ),
// ),
// ),
// );
}
}

View File

@@ -0,0 +1,570 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../main.dart';
import 'package:supertokens_flutter/http.dart' as http;
import '../../mih_components/medicine_search.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_button.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_dropdown_input.dart';
import '../../mih_components/mih_inputs_and_buttons/mih_search_input.dart';
import '../../mih_components/mih_pop_up_messages/mih_error_message.dart';
import '../../mih_components/mih_pop_up_messages/mih_loading_circle.dart';
import '../../mih_components/mih_pop_up_messages/mih_success_message.dart';
import '../../mih_env/env.dart';
import '../../mih_objects/app_user.dart';
import '../../mih_objects/arguments.dart';
import '../../mih_objects/business.dart';
import '../../mih_objects/business_user.dart';
import '../../mih_objects/patients.dart';
import '../../mih_objects/perscription.dart';
class PrescripInput extends StatefulWidget {
final TextEditingController medicineController;
final TextEditingController quantityController;
final TextEditingController dosageController;
final TextEditingController timesDailyController;
final TextEditingController noDaysController;
final TextEditingController noRepeatsController;
final TextEditingController outputController;
final Patient selectedPatient;
final AppUser signedInUser;
final Business? business;
final BusinessUser? businessUser;
const PrescripInput({
super.key,
required this.medicineController,
required this.quantityController,
required this.dosageController,
required this.timesDailyController,
required this.noDaysController,
required this.noRepeatsController,
required this.outputController,
required this.selectedPatient,
required this.signedInUser,
required this.business,
required this.businessUser,
});
@override
State<PrescripInput> createState() => _PrescripInputState();
}
class _PrescripInputState extends State<PrescripInput> {
final FocusNode _focusNode = FocusNode();
List<Perscription> perscriptionObjOutput = [];
late double width;
late double height;
final numberOptions = [
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12",
"13",
"14",
"15",
"16",
"17",
"18",
"19",
"20",
"21",
"22",
"23",
"24",
"25",
"26",
"27",
"28",
"29",
"30"
];
Future<void> generatePerscription() async {
//start loading circle
showDialog(
context: context,
builder: (context) {
return const Mihloadingcircle();
},
);
var response1 = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/minio/generate/perscription/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"app_id": widget.selectedPatient.app_id,
"fullName":
"${widget.selectedPatient.first_name} ${widget.selectedPatient.last_name}",
"id_no": widget.selectedPatient.id_no,
"docfname":
"DR. ${widget.signedInUser.fname} ${widget.signedInUser.lname}",
"busName": widget.business!.Name,
"busAddr": "*TO BE ADDED IN THE FUTURE*",
"busNo": widget.business!.contact_no,
"busEmail": widget.business!.bus_email,
"logo_path": widget.business!.logo_path,
"sig_path": widget.businessUser!.sig_path,
"data": perscriptionObjOutput,
}),
);
//print(response1.statusCode);
DateTime now = new DateTime.now();
DateTime date = new DateTime(now.year, now.month, now.day);
String fileName =
"Perscription-${widget.selectedPatient.first_name} ${widget.selectedPatient.last_name}-${date.toString().substring(0, 10)}.pdf";
if (response1.statusCode == 200) {
var response2 = await http.post(
Uri.parse("${AppEnviroment.baseApiUrl}/files/insert/"),
headers: <String, String>{
"Content-Type": "application/json; charset=UTF-8"
},
body: jsonEncode(<String, dynamic>{
"file_path":
"${widget.selectedPatient.app_id}/patient_files/$fileName",
"file_name": fileName,
"app_id": widget.selectedPatient.app_id
}),
);
//print(response2.statusCode);
if (response2.statusCode == 201) {
setState(() {
//To do
widget.medicineController.clear();
widget.dosageController.clear();
widget.timesDailyController.clear();
widget.noDaysController.clear();
widget.timesDailyController.clear();
widget.noRepeatsController.clear();
widget.quantityController.clear();
widget.outputController.clear();
// futueFiles = fetchFiles();
});
// end loading circle
Navigator.of(context).pop();
Navigator.of(context).pop();
Navigator.of(context).pushNamed('/patient-manager/patient',
arguments: PatientViewArguments(
widget.signedInUser,
widget.selectedPatient,
widget.businessUser,
widget.business,
"business",
));
String message =
"The perscription $fileName has been successfully generated and added to ${widget.selectedPatient.first_name} ${widget.selectedPatient.last_name}'s record. You can now access and download it for their use.";
successPopUp(message);
} else {
internetConnectionPopUp();
}
} 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,
);
},
);
}
void getMedsPopUp(TextEditingController medSearch) {
showDialog(
context: context,
builder: (context) {
return MedicineSearch(
searchVlaue: medSearch,
);
},
);
}
bool isFieldsFilled() {
if (widget.medicineController.text.isEmpty ||
// widget.quantityController.text.isEmpty ||
widget.dosageController.text.isEmpty ||
widget.timesDailyController.text.isEmpty ||
widget.noDaysController.text.isEmpty ||
widget.noRepeatsController.text.isEmpty) {
return false;
} else {
return true;
}
}
void updatePerscriptionList() {
String name;
String unit;
String form;
List<String> medNameList = widget.medicineController.text.split("%t");
if (medNameList.length == 1) {
name = medNameList[0];
unit = "";
form = "";
} else {
name = medNameList[0];
unit = medNameList[1];
form = medNameList[2];
}
int quantityCalc = calcQuantity(
widget.dosageController.text,
widget.timesDailyController.text,
widget.noDaysController.text,
medNameList[2].toLowerCase());
Perscription tempObj = Perscription(
name: name,
unit: unit,
form: form,
fullForm: getFullDoagesForm(form),
quantity: "$quantityCalc",
dosage: widget.dosageController.text,
times: widget.timesDailyController.text,
days: widget.noDaysController.text,
repeats: widget.noRepeatsController.text,
);
perscriptionObjOutput.add(tempObj);
}
String getPerscTitle(int index) {
return "${perscriptionObjOutput[index].name} - ${perscriptionObjOutput[index].form}";
}
String getPerscSubtitle(int index) {
if (perscriptionObjOutput[index].form.toLowerCase() == "syr") {
String unit = perscriptionObjOutput[index].unit.toLowerCase();
if (perscriptionObjOutput[index].unit.toLowerCase().contains("ml")) {
unit = "ml";
}
return "${perscriptionObjOutput[index].dosage} $unit, ${perscriptionObjOutput[index].times} time(s) daily, for ${perscriptionObjOutput[index].days} day(s)\nQuantity: ${perscriptionObjOutput[index].quantity}\nNo. of repeats: ${perscriptionObjOutput[index].repeats}";
} else {
return "${perscriptionObjOutput[index].dosage} ${perscriptionObjOutput[index].fullForm}(s), ${perscriptionObjOutput[index].times} time(s) daily, for ${perscriptionObjOutput[index].days} day(s)\nQuantity: ${perscriptionObjOutput[index].quantity}\nNo. of repeats: ${perscriptionObjOutput[index].repeats}";
}
}
String getFullDoagesForm(String abr) {
var dosageFormList = {
"liq": "liquid",
"tab": "tablet",
"cap": "capsule",
"cps": "capsule",
"oin": "ointment",
"lit": "lotion",
"lot": "lotion",
"inj": "injection",
"syr": "syrup",
"dsp": "effervescent tablet",
"eft": "effervescent tablet",
"ear": "drops",
"drp": "drops",
"opd": "drops",
"udv": "vial",
"sus": "suspension",
"susp": "suspension",
"cal": "calasthetic",
"sol": "solution",
"sln": "solution",
"neb": "nebuliser",
"inh": "inhaler",
"spo": "inhaler",
"inf": "infusion",
"chg": "chewing Gum",
"vac": "vacutainer",
"vag": "vaginal gel",
"jel": "gel",
"eyo": "eye ointment",
"vat": "vaginal cream",
"poi": "injection",
"ped": "powder",
"pow": "powder",
"por": "powder",
"sac": "sachet",
"sup": "suppository",
"cre": "cream",
"ptd": "patch",
"ect": "tablet",
"nas": "spray",
};
String form;
if (dosageFormList[abr.toLowerCase()] == null) {
form = abr;
} else {
form = dosageFormList[abr.toLowerCase()]!;
}
return form;
}
int calcQuantity(String dosage, String times, String days, String form) {
var dosageFormList = [
"tab",
"cap",
"cps",
"dsp",
"eft",
"udv",
"chg",
"sac",
"sup",
"ptd",
"ect",
];
if (dosageFormList.contains(form)) {
return int.parse(dosage) * int.parse(times) * int.parse(days);
} else {
return 1;
}
}
Widget displayMedInput() {
return Column(
children: [
//const SizedBox(height: 25.0),
KeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKeyEvent: (event) async {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.enter) {
getMedsPopUp(widget.medicineController);
}
},
child: MIHSearchField(
controller: widget.medicineController,
hintText: "Medicine",
required: true,
editable: true,
onTap: () {
getMedsPopUp(widget.medicineController);
},
),
),
const SizedBox(height: 10.0),
// SizedBox(
// width: 300,
// child: MIHDropdownField(
// controller: widget.quantityController,
// hintText: "Quantity",
// dropdownOptions: numberOptions,
// required: true,
// editable: true,
// ),
// ),
// const SizedBox(height: 10.0),
MIHDropdownField(
controller: widget.dosageController,
hintText: "Dosage",
dropdownOptions: numberOptions,
required: true,
editable: true,
),
const SizedBox(height: 10.0),
MIHDropdownField(
controller: widget.timesDailyController,
hintText: "Times Daily",
dropdownOptions: numberOptions,
required: true,
editable: true,
),
const SizedBox(height: 10.0),
MIHDropdownField(
controller: widget.noDaysController,
hintText: "No. Days",
dropdownOptions: numberOptions,
required: true,
editable: true,
),
const SizedBox(height: 10.0),
MIHDropdownField(
controller: widget.noRepeatsController,
hintText: "No. Repeats",
dropdownOptions: numberOptions,
required: true,
editable: true,
),
const SizedBox(height: 15.0),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
buttonText: "Add",
buttonColor:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
onTap: () {
if (isFieldsFilled()) {
// int quantity;
// int.parse(widget.dosageController.text) *
// int.parse(widget.timesDailyController.text) *
// int.parse(widget.noDaysController.text);
setState(() {
//widget.quantityController.text = "$quantity";
updatePerscriptionList();
widget.medicineController.clear();
widget.quantityController.clear();
widget.dosageController.clear();
widget.timesDailyController.clear();
widget.noDaysController.clear();
widget.noRepeatsController.clear();
});
//addPatientAPICall();
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
},
),
)
],
);
}
Widget displayPerscList() {
return Column(
children: [
Container(
width: 550,
height: 325,
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: ListView.separated(
separatorBuilder: (BuildContext context, int index) {
return const Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Divider(),
);
},
itemCount: perscriptionObjOutput.length,
itemBuilder: (context, index) {
//final patient = widget.patients[index].id_no.contains(widget.searchString);
return ListTile(
title: Text(
getPerscTitle(index),
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
subtitle: Text(
getPerscSubtitle(index),
style: TextStyle(
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
),
//onTap: () {},
trailing: IconButton(
icon: Icon(
Icons.delete_forever_outlined,
color:
MzanziInnovationHub.of(context)!.theme.secondaryColor(),
),
onPressed: () {
setState(() {
perscriptionObjOutput.removeAt(index);
});
},
),
);
},
),
),
const SizedBox(height: 15.0),
SizedBox(
width: 300,
height: 50,
child: MIHButton(
onTap: () async {
if (perscriptionObjOutput.isNotEmpty) {
//print(jsonEncode(perscriptionObjOutput));
await generatePerscription();
Navigator.pop(context);
} else {
showDialog(
context: context,
builder: (context) {
return const MIHErrorMessage(errorType: "Input Error");
},
);
}
},
buttonText: "Generate",
buttonColor: MzanziInnovationHub.of(context)!.theme.successColor(),
textColor: MzanziInnovationHub.of(context)!.theme.primaryColor(),
),
)
],
);
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
@override
void initState() {
//futueMeds = getMedList(endpointMeds);
super.initState();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
setState(() {
width = size.width;
height = size.height;
});
return Wrap(
direction: Axis.horizontal,
alignment: WrapAlignment.center,
spacing: 10,
runSpacing: 10,
// mainAxisAlignment: MainAxisAlignment.center,
// mainAxisSize: MainAxisSize.max,
// crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(width: 500, child: displayMedInput()),
displayPerscList(),
],
);
}
}

View File

@@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
import '../../mih_components/mih_layout/mih_action.dart';
import '../../mih_components/mih_layout/mih_body.dart';
import '../../mih_components/mih_layout/mih_header.dart';
import '../../mih_components/mih_layout/mih_layout_builder.dart';
class MIHTest extends StatefulWidget {
const MIHTest({super.key});
@override
State<MIHTest> createState() => _MIHTestState();
}
class _MIHTestState extends State<MIHTest> {
late YoutubePlayerController videoController;
String videoLink = "https://www.youtube.com/watch?v=P2bM9eosJ_A";
@override
void initState() {
videoController = YoutubePlayerController(
initialVideoId: "P2bM9eosJ_A",
);
super.initState();
}
@override
Widget build(BuildContext context) {
return MIHLayoutBuilder(
actionButton: MIHAction(
icon: const Icon(Icons.arrow_back),
iconSize: 35,
onTap: () {
Navigator.of(context).pushNamedAndRemoveUntil(
'/',
arguments: true,
(route) => false,
);
},
),
header: const MIHHeader(
headerAlignment: MainAxisAlignment.center,
headerItems: [
Text(
"Test",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
],
),
secondaryActionButton: null,
body: MIHBody(
borderOn: false,
bodyItems: [
YoutubePlayer(
controller: videoController,
),
],
),
actionDrawer: null,
secondaryActionDrawer: null,
bottomNavBar: null,
pullDownToRefresh: false,
onPullDown: () async {},
);
// return MIHWindow(
// fullscreen: false,
// windowTitle: "Test",
// windowTools: const [],
// onWindowTapClose: () {
// Navigator.pop(context);
// },
// windowBody: [
// YoutubePlayer(
// controller: videoController,
// ),
// ],
// );
}
}

View File

@@ -0,0 +1,255 @@
import 'package:flutter/material.dart';
import '../mih_components/mih_layout/mih_print_prevew.dart';
import '../mih_components/mih_pop_up_messages/mih_notification_message.dart';
import '../mih_packages/appointment/appointments.dart';
import '../mih_packages/authentication/auth_check.dart';
import '../mih_packages/patient_profile/add_or_view_patient.dart';
import '../mih_packages/patient_profile/patient_add.dart';
import '../mih_packages/patient_profile/patient_edit.dart';
import '../mih_packages/patient_profile/patient_manager.dart';
import '../mih_objects/app_user.dart';
import '../mih_objects/arguments.dart';
import '../mih_packages/about_mih/mih_about.dart';
import '../mih_packages/authentication/forgot_password.dart';
import '../mih_packages/authentication/reset_password.dart';
import '../mih_packages/patient_profile/full_screen_file.dart';
import '../mih_packages/manage_business/manage_business_profile.dart';
import '../mih_packages/access_review/patient_access_review.dart';
import '../mih_packages/patient_profile/patient_view.dart';
import '../mih_packages/manage_business/profile_business_add.dart';
import '../mih_packages/manage_business/business_details.dart';
import '../mih_packages/mzansi_profile/profile_user_update.dart';
class RouteGenerator {
static Route<dynamic> generateRoute(RouteSettings settings) {
final args = settings.arguments;
var extPath = Uri.base.path;
// print(extPath);
// print(settings.name);
// External Links Navigation
switch (extPath) {
case '/auth/reset-password':
return MaterialPageRoute(
settings: settings,
builder: (_) => ResetPassword(
token: Uri.base.queryParameters['token'],
));
default:
// Internal Navigation
switch (settings.name) {
// Authgentication
case '/':
if (args is bool) {
return MaterialPageRoute(
settings: settings,
builder: (_) => AuthCheck(
personalSelected: args,
),
);
} else {
return MaterialPageRoute(
settings: settings,
builder: (_) => const AuthCheck(
personalSelected: true,
),
);
}
case '/notifications':
if (args is NotificationArguments) {
return MaterialPageRoute(
settings: settings,
builder: (_) => MIHNotificationMessage(
arguments: args,
),
);
}
return _errorRoute();
case '/forgot-password':
return MaterialPageRoute(
settings: settings, builder: (_) => const ForgotPassword());
//http://mzansi-innovation-hub.co.za/auth/reset-password
//===============================================================
//About MIH
case '/about':
return MaterialPageRoute(
settings: settings, builder: (_) => const MIHAbout());
//===============================================================
//User Profile
case '/user-profile':
if (args is AppProfileUpdateArguments) {
return MaterialPageRoute(
settings: settings,
builder: (_) => ProfileUserUpdate(
arguments: args,
),
);
}
return _errorRoute();
//===============================================================
//Business Profile Pages
case '/business-profile':
if (args is BusinessArguments) {
return MaterialPageRoute(
settings: settings,
builder: (_) => BusinessDetails(
arguments: args,
),
);
}
return _errorRoute();
case '/business-profile/set-up':
if (args is AppUser) {
return MaterialPageRoute(
settings: settings,
builder: (_) => ProfileBusinessAdd(
signedInUser: args,
),
);
}
return _errorRoute();
case '/business-profile/manage':
if (args is BusinessArguments) {
return MaterialPageRoute(
settings: settings,
builder: (_) => ManageBusinessProfile(
arguments: args,
),
);
}
return _errorRoute();
//===============================================================
// Patient Profile Pages
case '/patient-profile':
if (args is PatientViewArguments) {
//print("route generator: $args");
return MaterialPageRoute(
settings: settings,
builder: (_) => AddOrViewPatient(
arguments: args,
),
);
}
return _errorRoute();
case '/patient-profile/set-up':
if (args is AppUser) {
return MaterialPageRoute(
settings: settings,
builder: (_) => AddPatient(
signedInUser: args,
),
);
}
return _errorRoute();
case '/patient-profile/edit':
if (args is PatientEditArguments) {
return MaterialPageRoute(
settings: settings,
builder: (_) => EditPatient(
signedInUser: args.signedInUser,
selectedPatient: args.selectedPatient,
),
);
}
return _errorRoute();
//===============================================================
// Access Review Page
case '/access-review':
if (args is AppUser) {
//print("route generator: $args");
return MaterialPageRoute(
settings: settings,
builder: (_) => PatientAccessRequest(
signedInUser: args,
),
);
}
return _errorRoute();
//===============================================================
// Appointment Page
case '/appointments':
if (args is AppUser) {
//print("route generator: $args");
return MaterialPageRoute(
settings: settings,
builder: (_) => Appointments(
signedInUser: args,
),
);
}
return _errorRoute();
//===============================================================
//Patient Manager Pages
case '/patient-manager':
if (args is BusinessArguments) {
//print("route generator: $args");
return MaterialPageRoute(
settings: settings,
builder: (_) => PatientManager(
arguments: args,
),
);
}
return _errorRoute();
case '/patient-manager/patient':
if (args is PatientViewArguments) {
return MaterialPageRoute(
settings: settings,
builder: (_) => PatientView(
arguments: args,
),
);
}
return _errorRoute();
//===============================================================
//Full Screen File Viewer
case '/file-veiwer':
if (args is FileViewArguments) {
return MaterialPageRoute(
settings: settings,
builder: (_) => FullScreenFileViewer(
arguments: args,
),
);
}
return _errorRoute();
case '/file-veiwer/print-preview':
if (args is PrintPreviewArguments) {
return MaterialPageRoute(
settings: settings,
builder: (_) => MIHPrintPreview(
arguments: args,
),
);
}
return _errorRoute();
default:
return _errorRoute();
}
}
}
}
Route<dynamic> _errorRoute() {
print("Invalid Route");
return MaterialPageRoute(
settings: const RouteSettings(name: '/'),
builder: (_) => const AuthCheck(
personalSelected: true,
));
// return MaterialPageRoute(builder: (_) {
// return const Scaffold(
// appBar: MIHAppBar(barTitle: "Error"),
// body: Center(
// child: Text("Error"),
// ),
// );
// });
}

View File

@@ -0,0 +1,254 @@
import 'package:flutter/material.dart';
import "package:universal_html/html.dart" as html;
class MyTheme {
late int _mainColor;
late int _secondColor;
//late int _errColor;
//late int _succColor;
late int _mesColor;
late String mode;
late String screenType;
late AssetImage loading;
late TargetPlatform platform;
bool kIsWeb = const bool.fromEnvironment('dart.library.js_util');
// Options:-
// f3f9d2 = Cream
// f0f0c9 = cream2
// caffd0 = light green
// B0F2B4 = light grean 2 *
// 85bda6 = light green 3
// 70f8ba = green
// F7F3EA = white
// a63446 = red
//747474
MyTheme() {
mode = "Dark";
//_errColor = 0xffD87E8B;
//_succColor = 0xffB0F2B4;
//_mesColor = 0xffc8c8c8d9;
}
ThemeData getData() {
return ThemeData(
fontFamily: 'Segoe UI',
scaffoldBackgroundColor: primaryColor(),
// pageTransitionsTheme: PageTransitionsTheme(
// builders: Map<TargetPlatform, PageTransitionsBuilder>.fromIterable(
// TargetPlatform.values,
// value: (dynamic _) => const FadeUpwardsPageTransitionsBuilder(),
// ),
// ),
colorScheme: ColorScheme(
brightness: getBritness(),
primary: secondaryColor(),
onPrimary: primaryColor(),
secondary: primaryColor(),
onSecondary: secondaryColor(),
error: errorColor(),
onError: primaryColor(),
surface: primaryColor(),
onSurface: secondaryColor(),
),
datePickerTheme: DatePickerThemeData(
backgroundColor: primaryColor(),
headerBackgroundColor: secondaryColor(),
headerForegroundColor: primaryColor(),
),
appBarTheme: AppBarTheme(
color: secondaryColor(),
foregroundColor: primaryColor(),
titleTextStyle: TextStyle(
color: primaryColor(),
fontSize: 25,
fontWeight: FontWeight.bold,
),
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: secondaryColor(),
foregroundColor: primaryColor(),
extendedTextStyle: TextStyle(color: primaryColor()),
),
drawerTheme: DrawerThemeData(
backgroundColor: primaryColor(),
));
}
String getPlatform() {
if (isPwa()) {
if (platform == TargetPlatform.android) {
return "Android";
} else if (platform == TargetPlatform.iOS) {
return "iOS";
}
} else if (kIsWeb) {
return "Web";
} else if (!kIsWeb) {
if (platform == TargetPlatform.android) {
return "Android";
} else if (platform == TargetPlatform.iOS) {
return "iOS";
}
}
return "Other";
}
bool isPwa() {
return html.window.matchMedia('(display-mode: standalone)').matches;
}
void setMode(String m) {
mode;
}
ThemeData darkMode() {
return getData();
}
ThemeData lightMode() {
return getData();
}
Brightness getBritness() {
if (mode == "Dark") {
return Brightness.dark;
} else {
return Brightness.light;
}
}
Color messageTextColor() {
if (mode == "Dark") {
_mesColor = 0XFFc8c8c8;
} else {
_mesColor = 0XFF747474;
}
return Color(_mesColor);
}
Color errorColor() {
if (mode == "Dark") {
return const Color(0xffD87E8B);
} else {
return const Color(0xffbb3d4f);
}
//return Color(_errColor);
}
Color highlightColor() {
if (mode == "Dark") {
return const Color(0XFF9bc7fa);
} else {
return const Color(0XFF354866);
}
}
Color successColor() {
if (mode == "Dark") {
return const Color(0xffB0F2B4);
} else {
return const Color(0xff56a95b);
}
}
AssetImage logoFrame() {
if (mode == "Dark") {
return const AssetImage(
'images/frame_dark.png',
);
} else {
return const AssetImage(
'images/frame_light.png',
);
}
}
AssetImage altLogoFrame() {
if (mode == "Light") {
return const AssetImage(
'images/frame_dark.png',
);
} else {
return const AssetImage(
'images/frame_light.png',
);
}
}
AssetImage logoImage() {
if (mode == "Dark") {
return const AssetImage(
'images/logo_dark.png',
);
} else {
return const AssetImage(
'images/logo_light.png',
);
}
}
AssetImage altLogoImage() {
if (mode == "Light") {
return const AssetImage(
'images/logo_dark.png',
);
} else {
return const AssetImage(
'images/logo_light.png',
);
}
}
AssetImage loadingImage() {
if (mode == "Dark") {
loading = const AssetImage(
'images/loading_light.gif',
);
} else {
loading = const AssetImage(
'images/loading_dark.gif',
);
}
return loading;
}
AssetImage altLoadingImage() {
if (mode == "Dark") {
loading = const AssetImage(
'images/loading_dark.gif',
);
} else {
loading = const AssetImage(
'images/loading_light.gif',
);
}
return loading;
}
void setScreenType(double width) {
if (width <= 800) {
screenType = "mobile";
} else {
screenType = "desktop";
}
}
Color primaryColor() {
if (mode == "Dark") {
_mainColor = 0XFF3A4454;
} else {
_mainColor = 0XFFbedcfe;
}
return Color(_mainColor);
}
Color secondaryColor() {
if (mode == "Dark") {
_secondColor = 0XFFbedcfe;
} else {
_secondColor = 0XFF3A4454;
}
return Color(_secondColor);
}
}

View File

@@ -0,0 +1 @@