3 July 2024

This commit is contained in:
2024-07-03 15:13:02 +02:00
parent 6f10fd8572
commit 8f0134a98f
62 changed files with 1101 additions and 343 deletions

32
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,32 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "MIH Dev",
"cwd": "Frontend/patient_manager",
"request": "launch",
"type": "dart",
"flutterMode": "debug",
"program": "lib/main_dev.dart",
"toolArgs": [
//"--dart-define-from-file=config/config-dev.json",
"--dart-define",
"baseURL=url"
],
},
{
"name": "MIH Prod",
"cwd": "Frontend\\patient_manager",
"request": "launch",
"type": "dart",
"flutterMode": "debug",
"program": "lib/main_prod.dart",
"args": [
" — dart-define-from-file=config/config-prod.json"
],
},
]
}

View File

@@ -41,3 +41,6 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
# Flutter config file
/config/

View File

@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:patient_manager/main.dart';
class HomeAppDrawer extends StatefulWidget {
final String userEmail;
const HomeAppDrawer({super.key, required this.userEmail});
@override
State<HomeAppDrawer> createState() => _HomeAppDrawerState();
}
class _HomeAppDrawerState extends State<HomeAppDrawer> {
@override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: const BoxDecoration(
color: Colors.blueAccent,
),
child: Text(widget.userEmail),
),
ListTile(
title: const Row(
children: [
Icon(Icons.logout),
SizedBox(width: 25.0),
Text("Sign Out"),
],
),
onTap: () {
client.auth.signOut();
Navigator.of(context).pushNamed('/');
},
)
],
),
);
}
}

View File

@@ -23,24 +23,28 @@ class HomeTile extends StatelessWidget {
child: Column(
//mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.abc),
title: Text(
tileName,
style: const TextStyle(
fontWeight: FontWeight.bold,
Expanded(
child: ListTile(
leading: const Icon(Icons.abc),
title: Text(
tileName,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
subtitle: Text(tileDescription),
),
subtitle: Text(tileDescription),
),
const Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Icon(Icons.arrow_forward),
),
],
const Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Icon(Icons.arrow_forward),
),
],
),
)
],
),

View File

@@ -19,6 +19,7 @@ class HomeTileGrid extends StatelessWidget {
children: [
HomeTile(
onTap: () {
//print("Home Tiles: $userEmail");
Navigator.of(context)
.pushNamed('/patient-manager', arguments: userEmail);
},

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:patient_manager/main.dart';
class MyAppBar extends StatelessWidget implements PreferredSizeWidget {
@override
@@ -15,17 +14,17 @@ class MyAppBar extends StatelessWidget implements PreferredSizeWidget {
backgroundColor: Colors.blueAccent,
elevation: 8,
shadowColor: Colors.black,
actions: [
IconButton(
onPressed: () {
client.auth.signOut();
Navigator.of(context).pushNamed('/');
},
icon: const Icon(Icons.logout),
iconSize: 35,
color: Colors.black,
),
],
// actions: [
// IconButton(
// onPressed: () {
// client.auth.signOut();
// Navigator.of(context).pushNamed('/');
// },
// icon: const Icon(Icons.logout),
// iconSize: 35,
// color: Colors.black,
// ),
// ],
title: Text(
barTitle,
style: const TextStyle(

View File

@@ -1,35 +0,0 @@
import 'package:flutter/material.dart';
class MyAppDrawer extends StatefulWidget {
final String drawerTitle;
const MyAppDrawer({super.key, required this.drawerTitle});
@override
State<MyAppDrawer> createState() => _MyAppDrawerState();
}
class _MyAppDrawerState extends State<MyAppDrawer> {
@override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: const BoxDecoration(
color: Colors.blueAccent,
),
child: Text(widget.drawerTitle),
),
ListTile(
title: const Text("Home"),
onTap: () {
Navigator.of(context).pushNamed('/home');
},
)
],
),
);
}
}

View File

@@ -191,6 +191,96 @@ class _MyErrorMessageState extends State<MyErrorMessage> {
);
}
void setInternetError() {
messageTypes["Internet Connection"] = Stack(
children: [
Container(
padding: const EdgeInsets.all(10.0),
width: 500.0,
height: 450.0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(25.0),
border: Border.all(color: Colors.red, width: 5.0),
),
child: const Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.warning_amber_rounded,
size: 100,
color: Colors.red,
),
SizedBox(height: 15),
Text(
"Internet Connection Lost!",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.red,
fontSize: 25.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 10),
Padding(
padding: EdgeInsets.symmetric(horizontal: 25.0),
child: 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: Colors.black,
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
),
SizedBox(height: 15),
Padding(
padding: EdgeInsets.symmetric(horizontal: 25.0),
child: Text(
"Here are a few things you can try:",
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
SizedBox(height: 10),
Padding(
padding: EdgeInsets.symmetric(horizontal: 25.0),
child: 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: Colors.black,
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
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,
),
),
),
],
);
}
Widget? getErrorMessage(String type) {
return messageTypes[type];
}
@@ -199,6 +289,7 @@ class _MyErrorMessageState extends State<MyErrorMessage> {
void initState() {
setInputError();
setinvalidCredError();
setInternetError();
super.initState();
}

View File

@@ -0,0 +1,193 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:patient_manager/main.dart';
import 'package:http/http.dart' as http;
import 'package:patient_manager/objects/appUser.dart';
class PatManAppDrawer extends StatefulWidget {
final String userEmail;
const PatManAppDrawer({super.key, required this.userEmail});
@override
State<PatManAppDrawer> createState() => _PatManAppDrawerState();
}
class _PatManAppDrawerState extends State<PatManAppDrawer> {
String endpointUserData = "http://localhost:80/users/profile/";
late AppUser signedInUser;
Future<AppUser> getUserDetails() async {
//print("pat man drawer: " + endpointUserData + widget.userEmail);
var response =
await http.get(Uri.parse(endpointUserData + widget.userEmail));
//print(response.statusCode);
//print(response.body);
if (response.statusCode == 200) {
return AppUser.fromJson(
jsonDecode(response.body) as Map<String, dynamic>);
} else {
throw Exception("Error: GetUserData status code ${response.statusCode}");
}
}
@override
void initState() {
//signedInUser = getUserDetails();
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getUserDetails(),
builder: (BuildContext context, AsyncSnapshot<AppUser> snapshot) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: const BoxDecoration(
color: Colors.blueAccent,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Signed Is As:",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.0,
),
),
const SizedBox(
height: 50.0,
),
Row(
children: [
const Text(
"Name: ",
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(width: 15),
Text("${snapshot.data?.fname} ${snapshot.data?.lname}"),
],
),
Row(
children: [
const Text(
"Email: ",
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(width: 16),
Text("${snapshot.data?.email}"),
],
),
],
),
),
ListTile(
title: const Row(
children: [
Icon(Icons.home_outlined),
SizedBox(width: 25.0),
Text("Home"),
],
),
onTap: () {
Navigator.of(context).pushNamed('/home');
},
),
ListTile(
title: const Row(
children: [
Icon(Icons.perm_identity),
SizedBox(width: 25.0),
Text("Profile"),
],
),
onTap: () {
//signedInUser = snapshot.data!;
//print("PatManAppDrawer: ${signedInUser.runtimeType}");
Navigator.of(context).pushNamed('/patient-manager/profile',
arguments: snapshot.data);
},
),
ListTile(
title: const Row(
children: [
Icon(Icons.logout),
SizedBox(width: 25.0),
Text("Sign Out"),
],
),
onTap: () {
client.auth.signOut();
Navigator.of(context).pushNamed('/');
},
)
],
),
);
},
);
}
// Drawer(
// child: ListView(
// padding: EdgeInsets.zero,
// children: [
// DrawerHeader(
// decoration: const BoxDecoration(
// color: Colors.blueAccent,
// ),
// child: Column(
// children: [
// const Text("Signed Is As:"),
// Text("Name: ${signedInUser.fname} ${signedInUser.lname}"),
// Text("Email: ${signedInUser.email}"),
// ],
// ),
// ),
// ListTile(
// title: const Row(
// children: [
// Icon(Icons.home_outlined),
// SizedBox(width: 25.0),
// Text("Home"),
// ],
// ),
// onTap: () {
// Navigator.of(context).pushNamed('/home');
// },
// ),
// ListTile(
// title: const Row(
// children: [
// Icon(Icons.perm_identity),
// SizedBox(width: 25.0),
// Text("Profile"),
// ],
// ),
// onTap: () {
// //Navigator.of(context).pushNamed('/home');
// },
// ),
// ListTile(
// title: const Row(
// children: [
// Icon(Icons.logout),
// SizedBox(width: 25.0),
// Text("Sign Out"),
// ],
// ),
// onTap: () {
// client.auth.signOut();
// Navigator.of(context).pushNamed('/');
// },
// )
// ],
// ),
// );
}

View File

@@ -30,7 +30,7 @@ class PatientFiles extends StatefulWidget {
class _PatientFilesState extends State<PatientFiles> {
String endpointFiles = "http://localhost:80/files/patients/";
String endpointUser = "http://localhost:80/docOffices/user/";
String endpointUser = "http://localhost:80/users/profile/";
String endpointGenFiles = "http://localhost:80/files/generate/med-cert/";
String endpointFileUpload = "http://localhost:80/files/upload/file/";
String endpointInsertFiles = "http://localhost:80/files/insert/";
@@ -278,6 +278,7 @@ class _PatientFilesState extends State<PatientFiles> {
),
TextButton(
onPressed: () {
selectedFileController.clear();
Navigator.pop(context);
},
child: const Text("Cancel"),

View File

@@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
import 'package:patient_manager/objects/appUser.dart';
class ProfileOfficeUpdate extends StatefulWidget {
final AppUser signedInUser;
//final String userEmail;
const ProfileOfficeUpdate({
super.key,
required this.signedInUser,
});
@override
State<ProfileOfficeUpdate> createState() => _ProfileOfficeUpdateState();
}
class _ProfileOfficeUpdateState extends State<ProfileOfficeUpdate> {
@override
Widget build(BuildContext context) {
return Center(child: Text("Office profile: ${widget.signedInUser.email}"));
}
}

View File

@@ -0,0 +1,100 @@
import 'package:flutter/material.dart';
import 'package:patient_manager/components/myDropdownInput.dart';
import 'package:patient_manager/components/myTextInput.dart';
import 'package:patient_manager/components/mybutton.dart';
import 'package:patient_manager/objects/appUser.dart';
class ProfileUserUpdate extends StatefulWidget {
final AppUser signedInUser;
//final String userEmail;
const ProfileUserUpdate({
super.key,
required this.signedInUser,
});
@override
State<ProfileUserUpdate> createState() => _ProfileUserUpdateState();
}
class _ProfileUserUpdateState extends State<ProfileUserUpdate> {
final fnameController = TextEditingController();
final lnameController = TextEditingController();
final titleController = TextEditingController();
@override
void initState() {
fnameController.text = widget.signedInUser.fname;
lnameController.text = widget.signedInUser.lname;
titleController.text = widget.signedInUser.title;
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(height: 15.0),
const Text(
"Update User profile:",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
const SizedBox(height: 15.0),
MyTextField(
controller: fnameController,
hintText: "First Name",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MyTextField(
controller: lnameController,
hintText: "Last Name",
editable: true,
required: true,
),
const SizedBox(height: 10.0),
MyDropdownField(
controller: titleController,
signedInUser: widget.signedInUser,
dropdownOptions: const <DropdownMenuEntry<String>>[
DropdownMenuEntry(value: "Dr.", label: "Doctor"),
DropdownMenuEntry(value: "Assistant", label: "Assistant"),
],
),
const SizedBox(height: 10.0),
SizedBox(
width: 500.0,
height: 100.0,
child: MyButton(
onTap: () {
if (fnameController.text == "") {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Incomplete Field\\s'),
content: const Text(
'Please conplete all fields',
),
actions: <Widget>[
TextButton(
child: const Text('Disable'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
},
buttonText: "Update"),
),
],
);
}
}

View File

@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
import 'package:patient_manager/main.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Supabase.initialize(
url: "https://stzluvsyhbwtfbztarmu.supabase.co",
anonKey:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InN0emx1dnN5aGJ3dGZienRhcm11Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTIwNzUyMTMsImV4cCI6MjAyNzY1MTIxM30.a7VHlk63JJcAotvsqtoqiKwjNK4EbnNgKilAqt1iRio",
);
print(String.fromEnvironment('baseURL'));
runApp(const MzanziInnovationHub());
}

View File

@@ -1,39 +1,29 @@
// ignore: file_names
class AppUser {
final int idUser;
final String UserName;
final int idusers;
final String email;
final int docOffice_id;
final String fname;
final String lname;
final String title;
const AppUser({
required this.idUser,
required this.UserName,
required this.docOffice_id,
required this.fname,
required this.lname,
required this.title,
});
const AppUser(
this.idusers,
this.email,
this.docOffice_id,
this.fname,
this.lname,
this.title,
);
factory AppUser.fromJson(Map<String, dynamic> json) {
return switch (json) {
{
'idUser': int idUser,
'UserName': String UserName,
'docOffice_id': int docOffice_id,
'fname': String fname,
'lname': String lname,
'title': String title,
} =>
AppUser(
idUser: idUser,
UserName: UserName,
docOffice_id: docOffice_id,
fname: fname,
lname: lname,
title: title,
),
_ => throw const FormatException('Failed to load album.'),
};
factory AppUser.fromJson(dynamic json) {
return AppUser(
json['idusers'],
json['email'],
json['docOffice_id'],
json['fname'],
json['lname'],
json['title'],
);
}
}

View File

@@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
import 'package:patient_manager/components/homeTileGrid.dart';
import 'package:patient_manager/components/myAppBar.dart';
import 'package:patient_manager/components/myAppDrawer.dart';
import 'package:patient_manager/components/homeAppDrawer.dart';
import 'package:patient_manager/components/myErrorMessage.dart';
import 'package:patient_manager/main.dart';
class Home extends StatefulWidget {
@@ -22,7 +23,7 @@ class _HomeState extends State<Home> {
if (res.user!.email != null) {
//print("emai not null");
useremail = res.user!.email!;
//print(useremail);
//print("Home Page: $useremail");
}
}
@@ -38,8 +39,30 @@ class _HomeState extends State<Home> {
if (snapshot.connectionState == ConnectionState.done) {
return Scaffold(
appBar: const MyAppBar(barTitle: "Mzansi Innovation Hub"),
drawer: MyAppDrawer(
drawerTitle: useremail,
drawer: HomeAppDrawer(userEmail: useremail),
floatingActionButton: FloatingActionButton.extended(
label: const Text(
"Test Allert",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
backgroundColor: Colors.blueAccent,
onPressed: () {
// Navigator.of(context)
// .pushNamed('/patient-manager/add', arguments: widget.userEmail);
showDialog(
context: context,
builder: (context) => const MyErrorMessage(
errorType: "Internet Connection",
),
);
},
icon: const Icon(
Icons.warning_amber_rounded,
color: Colors.white,
),
),
body: HomeTileGrid(
userEmail: useremail,

View File

@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:patient_manager/components/myErrorMessage.dart';
import 'package:patient_manager/components/myTextInput.dart';
import 'package:patient_manager/components/mybutton.dart';
import 'package:patient_manager/objects/appUser.dart';
@@ -29,7 +30,7 @@ class _AddPatientState extends State<AddPatient> {
final medNameController = TextEditingController();
final medSchemeController = TextEditingController();
final addressController = TextEditingController();
final docOfficeIdApiUrl = "http://localhost:80/docOffices/user/";
final docOfficeIdApiUrl = "http://localhost:80/users/profile/";
final apiUrl = "http://localhost:80/patients/insert/";
late int futureDocOfficeId;
@@ -44,10 +45,27 @@ class _AddPatientState extends State<AddPatient> {
//print(futureDocOfficeId);
});
} else {
internetConnectionPopUp();
throw Exception('failed to load patients');
}
}
bool isFieldsFilled() {
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) {
return false;
} else {
return true;
}
}
Future<void> addPatientAPICall() async {
await getOfficeIdByUser(docOfficeIdApiUrl + widget.userEmail);
print(futureDocOfficeId.toString());
@@ -76,10 +94,19 @@ class _AddPatientState extends State<AddPatient> {
"${fnameController.text} ${lnameController.text} Successfully added";
messagePopUp(message);
} else {
messagePopUp("error");
internetConnectionPopUp();
}
}
void internetConnectionPopUp() {
showDialog(
context: context,
builder: (context) {
return const MyErrorMessage(errorType: "Internet Connection");
},
);
}
void messagePopUp(error) {
showDialog(
context: context,
@@ -129,7 +156,7 @@ class _AddPatientState extends State<AddPatient> {
controller: fnameController,
hintText: "First Name",
editable: true,
required: false,
required: true,
),
),
],
@@ -243,7 +270,19 @@ class _AddPatientState extends State<AddPatient> {
width: 500.0,
height: 100.0,
child: MyButton(
onTap: addPatientAPICall,
onTap: () {
if (isFieldsFilled()) {
addPatientAPICall();
} else {
showDialog(
context: context,
builder: (context) {
return const MyErrorMessage(
errorType: "Input Error");
},
);
}
},
buttonText: "Add",
),
),

View File

@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:patient_manager/components/myErrorMessage.dart';
import 'package:patient_manager/components/myTextInput.dart';
import 'package:patient_manager/components/mybutton.dart';
import 'package:patient_manager/objects/appUser.dart';
@@ -31,7 +32,7 @@ class _EditPatientState extends State<EditPatient> {
final medNameController = TextEditingController();
final medSchemeController = TextEditingController();
final addressController = TextEditingController();
final docOfficeIdApiUrl = "http://localhost:80/docOffices/user/";
final docOfficeIdApiUrl = "http://localhost:80/users/profile/";
final apiUrlEdit = "http://localhost:80/patients/update/";
final apiUrlDelete = "http://localhost:80/patients/delete/";
late int futureDocOfficeId;
@@ -48,18 +49,19 @@ class _EditPatientState extends State<EditPatient> {
//print(futureDocOfficeId);
});
} else {
internetConnectionPopUp();
throw Exception('failed to load patients');
}
}
Future<void> updatePatientApiCall() async {
print("Here1");
//print("Here1");
//userEmail = getLoginUserEmail() as String;
print(userEmail);
print("Here2");
//print(userEmail);
//print("Here2");
await getOfficeIdByUser(docOfficeIdApiUrl + userEmail);
print(futureDocOfficeId.toString());
print("Here3");
//print(futureDocOfficeId.toString());
//print("Here3");
var response = await http.put(
Uri.parse(apiUrlEdit),
headers: <String, String>{
@@ -78,27 +80,27 @@ class _EditPatientState extends State<EditPatient> {
"doc_office_id": futureDocOfficeId,
}),
);
print("Here4");
print(response.statusCode);
//print("Here4");
//print(response.statusCode);
if (response.statusCode == 200) {
Navigator.of(context).pushNamed('/patient-manager', arguments: userEmail);
String message =
"${fnameController.text} ${lnameController.text} Successfully Updated";
messagePopUp(message);
} else {
messagePopUp("error ${response.statusCode}");
internetConnectionPopUp();
}
}
Future<void> deletePatientApiCall() async {
print("Here1");
//print("Here1");
//userEmail = getLoginUserEmail() as String;
print(userEmail);
print("Here2");
//print(userEmail);
//print("Here2");
await getOfficeIdByUser(docOfficeIdApiUrl + userEmail);
print("Office ID: ${futureDocOfficeId.toString()}");
print("OPatient ID No: ${idController.text}");
print("Here3");
//print("Office ID: ${futureDocOfficeId.toString()}");
//print("OPatient ID No: ${idController.text}");
//print("Here3");
var response = await http.delete(
Uri.parse(apiUrlDelete),
headers: <String, String>{
@@ -109,15 +111,15 @@ class _EditPatientState extends State<EditPatient> {
"doc_office_id": futureDocOfficeId,
}),
);
print("Here4");
print(response.statusCode);
//print("Here4");
//print(response.statusCode);
if (response.statusCode == 200) {
Navigator.of(context).pushNamed('/patient-manager', arguments: userEmail);
String message =
"${fnameController.text} ${lnameController.text} Successfully Deleted";
messagePopUp(message);
} else {
messagePopUp("error ${response.statusCode}");
internetConnectionPopUp();
}
}
@@ -138,6 +140,131 @@ class _EditPatientState extends State<EditPatient> {
);
}
void internetConnectionPopUp() {
showDialog(
context: context,
builder: (context) {
return const MyErrorMessage(errorType: "Internet Connection");
},
);
}
Widget deletePatientPopUp() {
return Dialog(
child: Stack(
children: [
Container(
padding: const EdgeInsets.all(10.0),
width: 500.0,
height: 475.0,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(25.0),
border: Border.all(color: Colors.blueAccent, width: 5.0),
),
child: Column(
//mainAxisSize: MainAxisSize.max,
children: [
const Icon(
Icons.warning_amber_rounded,
size: 100,
color: Colors.blueAccent,
),
const SizedBox(height: 15),
const Text(
"Are you sure you want to delete this?",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.blueAccent,
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: const TextStyle(
color: Colors.black,
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 15),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 25.0),
child: Text(
"Here's what you'll be deleting:",
style: TextStyle(
color: Colors.black,
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 10),
const Padding(
padding: 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: Colors.black,
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(
width: 300,
height: 100,
child: MyButton(
onTap: deletePatientApiCall, buttonText: "Delete"))
],
),
),
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,
),
),
),
],
),
);
}
bool isFieldsFilled() {
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) {
return false;
} else {
return true;
}
}
@override
void initState() {
getLoginUserEmail();
@@ -190,38 +317,7 @@ class _EditPatientState extends State<EditPatient> {
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.warning),
Text("Warning"),
],
),
content: Text(
"You are trying to delete the patient ${fnameController.text} ${lnameController.text}.\n\n" +
"Please note that this patient will be deleted permenantly and can not be retored\n\n" +
"Would you like to delete patient?"),
actions: [
TextButton(
onPressed: () {
deletePatientApiCall();
},
child: const Text(
"Yes",
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text("No"),
)
],
),
builder: (context) => deletePatientPopUp(),
);
},
)
@@ -362,7 +458,17 @@ class _EditPatientState extends State<EditPatient> {
height: 100.0,
child: MyButton(
onTap: () {
updatePatientApiCall();
if (isFieldsFilled()) {
updatePatientApiCall();
} else {
showDialog(
context: context,
builder: (context) {
return const MyErrorMessage(
errorType: "Input Error");
},
);
}
},
buttonText: "Update",
),

View File

@@ -4,23 +4,11 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:patient_manager/components/buildPatientList.dart';
import 'package:patient_manager/components/myAppBar.dart';
import 'package:patient_manager/components/myAppDrawer.dart';
import 'package:http/http.dart' as http;
import 'package:patient_manager/components/mySearchInput.dart';
import 'package:patient_manager/components/patManAppDrawer.dart';
import 'package:patient_manager/objects/patients.dart';
Future<List<Patient>> fetchPatients(String endpoint) async {
final response = await http.get(Uri.parse(endpoint));
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');
}
}
class PatientManager extends StatefulWidget {
final String userEmail;
@@ -39,6 +27,20 @@ class _PatientManagerState extends State<PatientManager> {
late Future<List<Patient>> futurePatients;
String searchString = "";
Future<List<Patient>> fetchPatients(String endpoint) async {
//print("Patien manager page: $endpoint");
final response = await http.get(Uri.parse(endpoint));
//print(response.statusCode);
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');
}
}
@override
void initState() {
futurePatients = fetchPatients(endpoint + widget.userEmail);
@@ -49,7 +51,7 @@ class _PatientManagerState extends State<PatientManager> {
Widget build(BuildContext context) {
return Scaffold(
appBar: const MyAppBar(barTitle: "Patient Manager"),
drawer: MyAppDrawer(drawerTitle: widget.userEmail),
drawer: PatManAppDrawer(userEmail: widget.userEmail),
//floatingActionButtonLocation: FloatingActionButtonLocation.endTop,
floatingActionButton: FloatingActionButton.extended(
label: const Text(
@@ -105,7 +107,7 @@ class _PatientManagerState extends State<PatientManager> {
],
);
} else {
return const MyAppDrawer(drawerTitle: "Error pulling email");
return const PatManAppDrawer(userEmail: "Error pulling email");
}
},
),

View File

@@ -0,0 +1,75 @@
import 'package:flutter/material.dart';
import 'package:google_nav_bar/google_nav_bar.dart';
import 'package:patient_manager/components/myAppBar.dart';
import 'package:patient_manager/components/profileOfficeUpdate.dart';
import 'package:patient_manager/components/profileUserUpdate.dart';
import 'package:patient_manager/objects/appUser.dart';
class ProfileUpdate extends StatefulWidget {
final AppUser signedInUser;
//final String userEmail;
const ProfileUpdate({
super.key,
//required this.userEmail,
required this.signedInUser,
});
@override
State<ProfileUpdate> createState() => _ProfileUpdateState();
}
class _ProfileUpdateState extends State<ProfileUpdate> {
int _selectedIndex = 0;
late List<Widget> _widgetOptions;
@override
void initState() {
_widgetOptions = <Widget>[
//Center(child: Text("User profile")),
ProfileUserUpdate(
signedInUser: widget.signedInUser,
),
ProfileOfficeUpdate(
signedInUser: widget.signedInUser,
),
];
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const MyAppBar(barTitle: "Update Profile"),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: Padding(
padding: const EdgeInsets.symmetric(horizontal: 150.0, vertical: 5.0),
child: GNav(
//hoverColor: Colors.lightBlueAccent,
iconSize: 35.0,
activeColor: Colors.white,
tabBackgroundColor: Colors.blueAccent,
gap: 20,
//padding: EdgeInsets.all(15),
tabs: const [
GButton(
icon: Icons.perm_identity,
text: "User Profile",
),
GButton(
icon: Icons.business,
text: "Office Profile",
),
],
selectedIndex: _selectedIndex,
onTabChange: (index) {
setState(() {
_selectedIndex = index;
});
},
),
),
);
}
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:patient_manager/components/myErrorMessage.dart';
import 'package:patient_manager/components/myPassInput.dart';
import 'package:patient_manager/components/myTextInput.dart';
import 'package:patient_manager/components/mybutton.dart';
@@ -32,20 +33,18 @@ class _SignInState extends State<SignIn> {
});
//Navigator.of(context).pushNamed('/homme');
}
} on AuthException catch (error) {
loginError(error.message);
} on AuthException {
loginError();
//emailController.clear();
passwordController.clear();
}
}
void loginError(error) {
void loginError() {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(error),
);
return const MyErrorMessage(errorType: "Invalid Credentials");
},
);
}
@@ -109,7 +108,19 @@ class _SignInState extends State<SignIn> {
height: 100.0,
child: MyButton(
onTap: () {
signUserIn();
if (emailController.text.isEmpty ||
passwordController.text.isEmpty) {
showDialog(
context: context,
builder: (context) {
return const MyErrorMessage(
errorType: "Input Error");
},
);
} else {
signUserIn();
}
if (successfulSignIn) {
Navigator.of(context).pushNamed('/homme');
}

View File

@@ -1,14 +1,15 @@
// ignore: file_names
import 'package:flutter/material.dart';
import 'package:patient_manager/Authentication/authCheck.dart';
import 'package:patient_manager/components/myAppBar.dart';
import 'package:patient_manager/pages/patientAdd.dart';
import 'package:patient_manager/components/signInOrRegister.dart';
import 'package:patient_manager/objects/appUser.dart';
import 'package:patient_manager/objects/patients.dart';
import 'package:patient_manager/pages/home.dart';
import 'package:patient_manager/pages/patientAdd.dart';
import 'package:patient_manager/pages/patientEdit.dart';
import 'package:patient_manager/pages/patientManager.dart';
import 'package:patient_manager/pages/patientView.dart';
import '../pages/patientEdit.dart';
import 'package:patient_manager/pages/profileUpdate.dart';
class RouteGenerator {
static Route<dynamic> generateRoute(RouteSettings settings) {
@@ -17,10 +18,13 @@ class RouteGenerator {
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (_) => const AuthCheck());
case '/home':
return MaterialPageRoute(builder: (_) => const Home());
case '/patient-manager':
if (args is String) {
//print("route generator: $args");
return MaterialPageRoute(
builder: (_) => PatientManager(
userEmail: args,
@@ -28,6 +32,7 @@ class RouteGenerator {
);
}
return _errorRoute();
case '/patient-manager/add':
if (args is String) {
return MaterialPageRoute(
@@ -37,6 +42,7 @@ class RouteGenerator {
);
}
return _errorRoute();
case '/patient-manager/patient':
if (args is Patient) {
return MaterialPageRoute(
@@ -46,6 +52,7 @@ class RouteGenerator {
);
}
return _errorRoute();
case '/patient-manager/patient/edit':
if (args is Patient) {
return MaterialPageRoute(
@@ -55,6 +62,15 @@ class RouteGenerator {
);
}
return _errorRoute();
case '/patient-manager/profile':
if (args is AppUser) {
return MaterialPageRoute(
builder: (_) => ProfileUpdate(signedInUser: args),
);
}
return _errorRoute();
case '/signin':
return MaterialPageRoute(builder: (_) => const SignInOrRegister());
// //case '/signIn':

View File

@@ -384,6 +384,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
google_nav_bar:
dependency: "direct main"
description:
name: google_nav_bar
sha256: "1c8e3882fa66ee7b74c24320668276ca23affbd58f0b14a24c1e5590f4d07ab0"
url: "https://pub.dev"
source: hosted
version: "5.0.6"
gotrue:
dependency: transitive
description:

View File

@@ -40,7 +40,7 @@ dependencies:
supabase_auth_ui: ^0.4.1
supabase_flutter: ^2.4.0
http: ^1.2.1
#internet_file: ^1.2.0
google_nav_bar: ^5.0.6
mysql_client: ^0.0.27
args: 2.5.0

View File

@@ -14,6 +14,7 @@ app.include_router(fileStorage.router)
origins = [
"http://localhost",
"http://localhost:80",
"api",
"*"
]

View File

@@ -5,8 +5,30 @@ from ..database import dbConnection
router = APIRouter()
class userRequest(BaseModel):
email: str
DocOfficeID: int
patientID: int
#get user by email & doc Office ID
@router.get("/users/profile/{email}", tags="users")
async def read_all_users(email: str):
db = dbConnection.dbConnect()
cursor = db.cursor()
query = "SELECT * FROM users where email = %s"
cursor.execute(query, (email.lower(),))
items = [
{"idusers": item[0],
"email": item[1],
"docOffice_id": item[2],
"fname":item[3],
"lname":item[4],
"title": item[5]
}
for item in cursor.fetchall()
]
cursor.close()
db.close()
return items[0]
# Get List of all files
@router.get("/users/", tags="users")
@@ -18,12 +40,11 @@ async def read_all_users():
items = [
{
"idUser": item[0],
"UserName": item[1],
"Password": item[2],
"docOffice_ID": item[3],
"fname": item[4],
"lname": item[5],
"title": item[6],
"email": item[1],
"docOffice_ID": item[2],
"fname": item[3],
"lname": item[4],
"title": item[5],
}
for item in cursor.fetchall()
]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
database/binlog.000042 Normal file

Binary file not shown.

BIN
database/binlog.000043 Normal file

Binary file not shown.

BIN
database/binlog.000044 Normal file

Binary file not shown.

BIN
database/binlog.000045 Normal file

Binary file not shown.

BIN
database/binlog.000046 Normal file

Binary file not shown.

BIN
database/binlog.000047 Normal file

Binary file not shown.

BIN
database/binlog.000048 Normal file

Binary file not shown.

BIN
database/binlog.000049 Normal file

Binary file not shown.

BIN
database/binlog.000050 Normal file

Binary file not shown.

BIN
database/binlog.000051 Normal file

Binary file not shown.

View File

@@ -1,5 +1,6 @@
4294967294,449
4294967278,385
4294967278,389
4294967279,331
4294967293,131
4294967293,130
4294967293,129
@@ -141,157 +142,155 @@
4243767282,0
4243767281,0
4294967293,0
4294967279,85
4294967279,202
4294967279,86
4294967279,87
4294967279,426
4294967279,89
4294967279,427
4294967279,203
4294967279,204
4294967279,428
4294967279,90
4294967279,91
4294967279,205
4294967279,92
4294967279,206
4294967279,430
4294967279,429
4294967279,208
4294967279,93
4294967279,431
4294967279,207
4294967279,432
4294967279,94
4294967279,95
4294967279,209
4294967279,96
4294967279,211
4294967279,434
4294967279,210
4294967279,212
4294967279,97
4294967279,435
4294967279,98
4294967279,213
4294967279,474
4294967279,99
4294967279,437
4294967279,100
4294967279,215
4294967279,277
4294967279,214
4294967279,216
4294967279,101
4294967279,218
4294967279,102
4294967279,217
4294967279,103
4294967279,220
4294967279,463
4294967279,104
4294967279,471
4294967279,219
4294967279,221
4294967279,223
4294967279,105
4294967279,106
4294967279,225
4294967279,107
4294967279,511
4294967279,108
4294967279,227
4294967279,229
4294967279,232
4294967279,234
4294967279,109
4294967279,110
4294967279,231
4294967279,233
4294967279,438
4294967279,239
4294967279,111
4294967279,112
4294967279,236
4294967279,237
4294967279,238
4294967279,241
4294967279,235
4294967279,240
4294967279,113
4294967279,114
4294967279,115
4294967279,116
4294967279,243
4294967279,245
4294967279,249
4294967279,247
4294967279,242
4294967279,244
4294967279,248
4294967279,117
4294967279,118
4294967279,246
4294967279,119
4294967279,120
4294967279,251
4294967279,253
4294967279,439
4294967279,250
4294967279,440
4294967279,252
4294967279,254
4294967279,121
4294967279,255
4294967279,122
4294967279,123
4294967279,441
4294967279,449
4294967279,124
4294967279,256
4294967279,450
4294967279,442
4294967279,451
4294967279,125
4294967279,320
4294967279,126
4294967279,443
4294967279,127
4294967279,459
4294967279,128
4294967279,452
4294967279,444
4294967279,453
4294967279,454
4294967279,258
4294967279,129
4294967279,445
4294967279,130
4294967279,446
4294967279,131
4294967278,428
4294967278,280
4294967279,132
4294967278,257
4294967278,258
4294967278,413
4294967278,261
4294967278,4
4294967278,5
4294967278,6
4294967278,7
4294967278,282
4294967278,281
4294967278,262
4294967278,293
4294967278,265
4294967278,264
4294967278,8
4294967278,263
4294967278,291
4294967278,9
4294967278,10
4294967278,11
4294967278,283
4294967278,323
4294967278,160
4294967278,48
4294967278,322
4294967278,159
4294967278,47
4294967278,321
4294967278,158
4294967278,46
4294967278,320
4294967278,157
4294967278,45
4294967278,319
4294967278,156
4294967278,44
4294967278,318
4294967278,155
4294967278,43
4294967278,154
4294967278,42
4294967278,316
4294967278,153
4294967278,41
4294967278,315
4294967278,152
4294967278,40
4294967278,314
4294967278,151
4294967278,39
4294967278,313
4294967278,150
4294967278,38
4294967278,312
4294967278,149
4294967278,37
4294967278,302
4294967278,148
4294967278,36
4294967278,409
4294967278,147
4294967278,35
4294967278,288
4294967278,146
4294967278,34
4294967278,301
4294967278,145
4294967278,33
4294967278,300
4294967278,144
4294967278,32
4294967278,299
4294967278,143
4294967278,31
4294967278,298
4294967278,142
4294967278,30
4294967278,310
4294967278,141
4294967278,29
4294967278,297
4294967278,140
4294967278,28
4294967278,309
4294967278,139
4294967278,27
4294967278,279
4294967278,138
4294967278,26
4294967278,278
4294967278,137
4294967278,25
4294967278,277
4294967278,136
4294967278,24
4294967278,276
4294967278,135
4294967278,23
4294967278,287
4294967278,134
4294967278,22
4294967278,275
4294967278,133
4294967278,21
4294967278,296
4294967278,274
4294967278,20
4294967278,295
4294967278,273
4294967278,19
4294967278,286
4294967278,272
4294967278,18
4294967278,285
4294967278,271
4294967278,17
4294967278,292
4294967278,270
4294967278,16
4294967278,417
4294967278,269
4294967278,15
4294967278,284
4294967278,268
4294967278,14
4294967278,283
4294967278,385
4294967278,13
4294967278,291
4294967278,266
4294967278,12
4294967278,282
4294967278,265
4294967278,11
4294967278,281
4294967278,264
4294967278,10
4294967278,293
4294967278,263
4294967278,9
4294967278,262
4294967278,8
4294967278,261
4294967278,7
4294967278,280
4294967278,413
4294967278,6
4294967278,428
4294967278,258
4294967278,5
4294967278,257
4294967278,4
4294967278,3
4294967279,454
4294967279,446
4294967279,132
4294967279,453
4294967279,445
4294967279,131
4294967279,452
4294967279,258
4294967279,130
4294967279,459
4294967279,444
4294967279,129
4294967279,451
4294967279,443
4294967279,128
4294967279,450
4294967279,320
4294967279,127
4294967279,449
4294967279,442

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -9,10 +9,10 @@ services:
- .:/app
depends_on:
- mysqldb
networks:
default:
aliases:
- mih-api-hub
# networks:
# default:
# aliases:
# - mih-api-hub
mysqldb:
#build: ./database/