From 7f7262a2d1fbfab2542dc85a01c3dc87c63bd2e5 Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 11 Feb 2025 11:37:06 +0200 Subject: [PATCH 01/12] remove unused code --- .../appointment/appointments.dart | 189 ------------------ 1 file changed, 189 deletions(-) diff --git a/Frontend/lib/mih_packages/appointment/appointments.dart b/Frontend/lib/mih_packages/appointment/appointments.dart index 6a675cda..0c00cbb2 100644 --- a/Frontend/lib/mih_packages/appointment/appointments.dart +++ b/Frontend/lib/mih_packages/appointment/appointments.dart @@ -1,13 +1,9 @@ -import 'dart:convert'; - import 'package:Mzansi_Innovation_Hub/mih_objects/arguments.dart'; 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'; @@ -17,7 +13,6 @@ 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 { @@ -48,183 +43,6 @@ class _PatientAccessRequestState extends State { late Future> accessRequestResults; late Future> personalQueueResults; - Future> 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 patientQueue = List.from( - l.map((model) => AccessRequest.fromJson(model))); - //print("Here3"); - //print(patientQueue); - return patientQueue; - } else { - throw Exception('failed to load patients'); - } - } - - List filterSearchResults(List accessList) { - List 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 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, - enableSearch: false, - ), - ), - 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 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 patientQueueList) { if (patientQueueList.isNotEmpty) { return Expanded( @@ -313,7 +131,6 @@ class _PatientAccessRequestState extends State { FutureBuilder( future: personalQueueResults, builder: (context, snapshot) { - //return displayQueueList(snapshot.requireData); if (snapshot.connectionState == ConnectionState.waiting) { return const Expanded( child: Center(child: Mihloadingcircle())); @@ -349,12 +166,6 @@ class _PatientAccessRequestState extends State { @override void initState() { - // selectedDropdown = "All"; - // filterController.text = "All"; - // filterController.addListener(refreshList); - // setState(() { - // accessRequestResults = fetchAccessRequests(); - // }); appointmentDateController.addListener(checkforchange); setState(() { personalQueueResults = MIHApiCalls.fetchPersonalAppointmentsAPICall( From fd0d84bd410de63a6f15117b0282b571c43265bd Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 11 Feb 2025 15:37:53 +0200 Subject: [PATCH 02/12] Add Appointment api to get, add, updae, delete --- backend/database/dbConnection.py | 8 ++ backend/main.py | 2 + backend/routers/appointments.py | 157 +++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 backend/routers/appointments.py diff --git a/backend/database/dbConnection.py b/backend/database/dbConnection.py index 75686c36..fc16e946 100644 --- a/backend/database/dbConnection.py +++ b/backend/database/dbConnection.py @@ -32,6 +32,14 @@ def dbMzansiWalletConnect(): database="mzansi_wallet" ) +def dbMzansiCalendarConnect(): + return mysql.connector.connect( + host="mysqldb", + user="root", + passwd="C@rtoon1995", + database="mzansi_calendar" + ) + def dbAllConnect(): return mysql.connector.connect( host="mysqldb", diff --git a/backend/main.py b/backend/main.py index b38b7809..ec371cf4 100644 --- a/backend/main.py +++ b/backend/main.py @@ -2,6 +2,7 @@ from fastapi import FastAPI, Depends, HTTPException from pydantic import BaseModel # from .routers import docOffices, patients, patients_files, patients_notes, users, fileStorage, medicine import routers.docOffices as docOffices +import routers.appointments as appointments import routers.patients as patients import routers.patients_files as patients_files import routers.patients_notes as patients_notes @@ -92,6 +93,7 @@ app.include_router(business.router) app.include_router(notifications.router) app.include_router(mzansi_wallet.router) app.include_router(icd10_codes.router) +app.include_router(appointments.router) # Check if server is up @app.get("/", tags=["Server Check"]) diff --git a/backend/routers/appointments.py b/backend/routers/appointments.py new file mode 100644 index 00000000..7d597a84 --- /dev/null +++ b/backend/routers/appointments.py @@ -0,0 +1,157 @@ +import mysql.connector +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel +#from ..database import dbConnection +import database +from datetime import datetime, timedelta, date +#SuperToken Auth from front end +from supertokens_python.recipe.session.framework.fastapi import verify_session +from supertokens_python.recipe.session import SessionContainer +from fastapi import Depends + +router = APIRouter() + +class appointmentInsertRequest(BaseModel): + app_id: str + business_id: str + title: str + description: str + date: str + time: str + +class appointmentUpdateRequest(BaseModel): + idappointments: int + title: str + description: str + date: str + time: str + +class appointmentDeleteRequest(BaseModel): + idappointments: int + +# Get List of all files by patient +@router.get("/appointments/business/{business_id}", tags=["Appointments"]) +async def read_all_appointments_by_business_id(business_id: str, date: str, session: SessionContainer = Depends(verify_session())): #, session: SessionContainer = Depends(verify_session()) + db = database.dbConnection.dbMzansiCalendarConnect() + requestDate = datetime.strptime(date, '%Y-%m-%d').date() + cursor = db.cursor() + query = "SELECT appointments.idappointments, appointments.business_id, " + query += "appointments.app_id, appointments.date_time, " + query += "appointments.title, appointments.description " + query += "from mzansi_calendar.appointments " + query = query + "where appointments.business_id = %s and date_time like '" + str(requestDate) + "%' " + query += "ORDER BY date_time ASC" + cursor.execute(query, (business_id,)) + items = [ + { + "idappointments": item[0], + "business_id": item[1], + "app_id": item[2], + "date_time": item[3], + "title": item[4], + "description": item[5], + } + for item in cursor.fetchall() + ] + cursor.close() + db.close() + return items + +# Get List of all files by patient +@router.get("/appointments/personal/{app_id}", tags=["Appointments"]) +async def read_all_appointments_by_business_id(app_id: str, date: str, session: SessionContainer = Depends(verify_session())): #, session: SessionContainer = Depends(verify_session()) + db = database.dbConnection.dbMzansiCalendarConnect() + requestDate = datetime.strptime(date, '%Y-%m-%d').date() + cursor = db.cursor() + query = "SELECT appointments.idappointments, appointments.app_id, " + query += "appointments.business_id, appointments.date_time, " + query += "appointments.title, appointments.description " + query += "from mzansi_calendar.appointments " + query = query + "where appointments.app_id = %s and date_time like '" + str(requestDate) + "%' " + query += "ORDER BY date_time ASC" + + cursor.execute(query, (app_id,)) + items = [ + { + "idappointments": item[0], + "app_id": item[1], + "business_id": item[2], + "date_time": item[3], + "title": item[4], + "description": item[5], + } + for item in cursor.fetchall() + ] + cursor.close() + db.close() + return items + +# Insert Patient note into table +@router.post("/appointment/insert/", tags=["Appointments"], status_code=201) +async def insert_appointment(itemRequest : appointmentInsertRequest, session: SessionContainer = Depends(verify_session())): #, session: SessionContainer = Depends(verify_session()) + date_time = itemRequest.date + " " + itemRequest.time + ":00" + db = database.dbConnection.dbMzansiCalendarConnect() + cursor = db.cursor() + query = "insert into appointments " + query += "(app_id, business_id, title, description, date_time) " + query += "values (%s, %s, %s, %s, %s)" + notetData = (itemRequest.app_id, + itemRequest.business_id, + itemRequest.title, + itemRequest.description, + date_time, + ) + try: + cursor.execute(query, notetData) + except Exception as error: + #raise HTTPException(status_code=404, detail="Failed to Create Record") + return {"message": error} + db.commit() + cursor.close() + db.close() + return {"message": "Successfully Created file Record"} + +# Update Patient on table +@router.put("/appointment/update/", tags=["Appointments"]) +async def Update_appointment(itemRequest : appointmentUpdateRequest, session: SessionContainer = Depends(verify_session())): #, session: SessionContainer = Depends(verify_session()) + + date_time = itemRequest.date + " " + itemRequest.time + ":00" + + db = database.dbConnection.dbMzansiCalendarConnect() + cursor = db.cursor() + query = "update appointments " + query += "set date_time=%s, title=%s, description=%s " + query += "where idappointments=%s" + patientData = (date_time, + itemRequest.title, + itemRequest.description, + itemRequest.idappointments) + try: + cursor.execute(query, patientData) + except Exception as error: + print(error) + raise HTTPException(status_code=404, detail="Failed to Update Record") + #return {"query": query, "message": error} + db.commit() + cursor.close() + db.close() + return {"message": "Successfully Updated Record"} + +# Update Patient on table +@router.delete("/appointment/delete/", tags=["Appointments"]) +async def Delete_appointment(itemRequest : appointmentDeleteRequest, session: SessionContainer = Depends(verify_session())): #, session: SessionContainer = Depends(verify_session()) + db = database.dbConnection.dbMzansiCalendarConnect() + cursor = db.cursor() + query = "delete from appointments " + query += "where idappointments=%s" + try: + cursor.execute(query, (str(itemRequest.idappointments),)) + except Exception as error: + print(error) + raise HTTPException(status_code=404, detail="Failed to Delete Appointment") + #return {"query": query, "message": error} + db.commit() + cursor.close() + db.close() + return {"message": "Successfully deleted Appointment"} + \ No newline at end of file From ea1ee870d4b15f61f56edc30892e94704525813d Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 11 Feb 2025 15:38:15 +0200 Subject: [PATCH 03/12] fix appointment notifications --- .../lib/mih_components/mih_layout/mih_notification_drawer.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Frontend/lib/mih_components/mih_layout/mih_notification_drawer.dart b/Frontend/lib/mih_components/mih_layout/mih_notification_drawer.dart index 16ecfe09..5b273147 100644 --- a/Frontend/lib/mih_components/mih_layout/mih_notification_drawer.dart +++ b/Frontend/lib/mih_components/mih_layout/mih_notification_drawer.dart @@ -1,3 +1,4 @@ +import 'package:Mzansi_Innovation_Hub/mih_objects/arguments.dart'; import 'package:flutter/material.dart'; import '../../main.dart'; import 'package:supertokens_flutter/http.dart' as http; @@ -35,6 +36,7 @@ class _MIHNotificationDrawerState extends State { Navigator.of(context).pop(); Navigator.of(context).pushNamed( "/", + arguments: AuthArguments(true, false), ); Navigator.of(context).pushNamed( widget.notifications[index].action_path, From 72e3227e801c6fa1ad1a695b06f93aeb9c558058 Mon Sep 17 00:00:00 2001 From: yaso Date: Tue, 11 Feb 2025 15:40:46 +0200 Subject: [PATCH 04/12] standardise response --- backend/routers/appointments.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/routers/appointments.py b/backend/routers/appointments.py index 7d597a84..6856fbbd 100644 --- a/backend/routers/appointments.py +++ b/backend/routers/appointments.py @@ -35,8 +35,8 @@ async def read_all_appointments_by_business_id(business_id: str, date: str, sess db = database.dbConnection.dbMzansiCalendarConnect() requestDate = datetime.strptime(date, '%Y-%m-%d').date() cursor = db.cursor() - query = "SELECT appointments.idappointments, appointments.business_id, " - query += "appointments.app_id, appointments.date_time, " + query = "SELECT appointments.idappointments, appointments.app_id, " + query += "appointments.business_id, appointments.date_time, " query += "appointments.title, appointments.description " query += "from mzansi_calendar.appointments " query = query + "where appointments.business_id = %s and date_time like '" + str(requestDate) + "%' " @@ -45,8 +45,8 @@ async def read_all_appointments_by_business_id(business_id: str, date: str, sess items = [ { "idappointments": item[0], - "business_id": item[1], - "app_id": item[2], + "app_id": item[1], + "business_id": item[2], "date_time": item[3], "title": item[4], "description": item[5], From cb7b584b637c7990ac7efaaa6e8eaa7cb90551f9 Mon Sep 17 00:00:00 2001 From: yaso Date: Wed, 12 Feb 2025 12:29:29 +0200 Subject: [PATCH 05/12] add mzansi calendar package --- .../appointment/mzansi_calendar.dart | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 Frontend/lib/mih_packages/appointment/mzansi_calendar.dart diff --git a/Frontend/lib/mih_packages/appointment/mzansi_calendar.dart b/Frontend/lib/mih_packages/appointment/mzansi_calendar.dart new file mode 100644 index 00000000..2ca03290 --- /dev/null +++ b/Frontend/lib/mih_packages/appointment/mzansi_calendar.dart @@ -0,0 +1,74 @@ +import 'package:Mzansi_Innovation_Hub/mih_components/mih_package/mih_app.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_package/mih_app_action.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_package/mih_app_tools.dart'; +import 'package:Mzansi_Innovation_Hub/mih_objects/app_user.dart'; +import 'package:Mzansi_Innovation_Hub/mih_objects/arguments.dart'; +import 'package:Mzansi_Innovation_Hub/mih_packages/appointment/appointments.dart'; +import 'package:flutter/material.dart'; + +class MzansiCalendar extends StatefulWidget { + final AppUser signedInUser; + const MzansiCalendar({ + super.key, + required this.signedInUser, + }); + + @override + State createState() => _MzansiCalendarState(); +} + +class _MzansiCalendarState extends State { + int _selcetedIndex = 0; + + @override + Widget build(BuildContext context) { + return MihApp( + appActionButton: getAction(), + appTools: getTools(), + appBody: getToolBody(), + selectedbodyIndex: _selcetedIndex, + onIndexChange: (newValue) { + setState(() { + _selcetedIndex = newValue; + }); + print("Index: $_selcetedIndex"); + }, + ); + } + + MihAppAction getAction() { + return MihAppAction( + icon: const Icon(Icons.arrow_back), + iconSize: 35, + onTap: () { + Navigator.of(context).pop(); + Navigator.of(context).popAndPushNamed( + '/', + arguments: AuthArguments(true, false), + ); + }, + ); + } + + MihAppTools getTools() { + Map temp = {}; + temp[const Icon(Icons.calendar_month)] = () { + setState(() { + _selcetedIndex = 0; + }); + }; + + return MihAppTools( + tools: temp, + selcetedIndex: _selcetedIndex, + ); + } + + List getToolBody() { + List toolBodies = [ + //appointment here + Appointments(signedInUser: widget.signedInUser), + ]; + return toolBodies; + } +} From 0f02ade8ffd730cb68984abb0b55cf635199d825 Mon Sep 17 00:00:00 2001 From: yaso Date: Wed, 12 Feb 2025 12:29:41 +0200 Subject: [PATCH 06/12] create appointment object --- Frontend/lib/mih_objects/appointment.dart | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Frontend/lib/mih_objects/appointment.dart diff --git a/Frontend/lib/mih_objects/appointment.dart b/Frontend/lib/mih_objects/appointment.dart new file mode 100644 index 00000000..92942996 --- /dev/null +++ b/Frontend/lib/mih_objects/appointment.dart @@ -0,0 +1,39 @@ +class Appointment { + final int idappointments; + final String app_id; + final String business_id; + final String date_time; + final String title; + final String description; + + const Appointment({ + required this.idappointments, + required this.app_id, + required this.business_id, + required this.date_time, + required this.title, + required this.description, + }); + + factory Appointment.fromJson(Map json) { + return switch (json) { + { + "idappointments": int idappointments, + 'app_id': String app_id, + 'business_id': String business_id, + 'date_time': String date_time, + 'title': String title, + 'description': String description, + } => + Appointment( + idappointments: idappointments, + app_id: app_id, + business_id: business_id, + date_time: date_time, + title: title, + description: description, + ), + _ => throw const FormatException('Failed to load album.'), + }; + } +} From a0a00f45a268c89fbf763c755f2e792ec2220f48 Mon Sep 17 00:00:00 2001 From: yaso Date: Wed, 12 Feb 2025 12:30:02 +0200 Subject: [PATCH 07/12] display appoinbtments in tool body --- .../appointment/appointments.dart | 284 ++++++++++++++---- 1 file changed, 226 insertions(+), 58 deletions(-) diff --git a/Frontend/lib/mih_packages/appointment/appointments.dart b/Frontend/lib/mih_packages/appointment/appointments.dart index 0c00cbb2..88054f00 100644 --- a/Frontend/lib/mih_packages/appointment/appointments.dart +++ b/Frontend/lib/mih_packages/appointment/appointments.dart @@ -1,19 +1,27 @@ +import 'package:Mzansi_Innovation_Hub/mih_apis/mih_mzansi_calendar_apis.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_button.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_date_input.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_multiline_text_input.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_text_input.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_time_input.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_layout/mih_window.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_package/mih-app_tool_body.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_pop_up_messages/mih_error_message.dart'; +import 'package:Mzansi_Innovation_Hub/mih_objects/appointment.dart'; import 'package:Mzansi_Innovation_Hub/mih_objects/arguments.dart'; +import 'package:Mzansi_Innovation_Hub/mih_packages/appointment/builder/build_appointment_list.dart'; import 'package:flutter/material.dart'; import '../../main.dart'; import '../../mih_apis/mih_api_calls.dart'; import '../../mih_components/mih_calendar.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 'builder/build_appointment_list.dart'; class Appointments extends StatefulWidget { final AppUser signedInUser; @@ -30,6 +38,14 @@ class Appointments extends StatefulWidget { class _PatientAccessRequestState extends State { TextEditingController filterController = TextEditingController(); TextEditingController appointmentDateController = TextEditingController(); + final TextEditingController _appointmentTitleController = + TextEditingController(); + final TextEditingController _appointmentDescriptionIDController = + TextEditingController(); + final TextEditingController _appointmentDateController = + TextEditingController(); + final TextEditingController _appointmentTimeController = + TextEditingController(); String baseUrl = AppEnviroment.baseApiUrl; String errorCode = ""; @@ -42,20 +58,26 @@ class _PatientAccessRequestState extends State { late Future> accessRequestResults; late Future> personalQueueResults; + late Future> personalAppointmentResults; - Widget displayQueueList(List patientQueueList) { - if (patientQueueList.isNotEmpty) { + Widget displayAppointmentList(List appointmentList) { + if (appointmentList.isNotEmpty) { return Expanded( child: BuildAppointmentList( - patientQueue: patientQueueList, + appointmentList: appointmentList, signedInUser: widget.signedInUser, + titleController: _appointmentTitleController, + descriptionIDController: _appointmentDescriptionIDController, + dateController: _appointmentDateController, + timeController: _appointmentTimeController, ), ); } return Expanded( child: Padding( padding: const EdgeInsets.only(top: 35.0), - child: Center( + child: Align( + alignment: Alignment.center, child: Text( "No Appointments for $selectedDay", style: TextStyle( @@ -70,8 +92,122 @@ class _PatientAccessRequestState extends State { ); } + void addAppointmentWindow() { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) { + return MIHWindow( + fullscreen: false, + windowTitle: "Add Appointment", + windowTools: [], + onWindowTapClose: () { + Navigator.of(context).pop(); + _appointmentDateController.clear(); + _appointmentTimeController.clear(); + _appointmentTitleController.clear(); + _appointmentDescriptionIDController.clear(); + }, + windowBody: [ + SizedBox( + // width: 500, + child: MIHTextField( + controller: _appointmentTitleController, + hintText: "Title", + editable: true, + required: true, + ), + ), + const SizedBox(height: 10), + SizedBox( + // width: 500, + child: MIHDateField( + controller: _appointmentDateController, + lableText: "Date", + required: true, + ), + ), + const SizedBox(height: 10), + SizedBox( + // width: 500, + child: MIHTimeField( + controller: _appointmentTimeController, + lableText: "Time", + required: true, + ), + ), + const SizedBox(height: 10), + SizedBox( + // width: 500, + height: 250, + child: MIHMLTextField( + controller: _appointmentDescriptionIDController, + hintText: "Description", + editable: true, + required: true, + ), + ), + const SizedBox(height: 20), + SizedBox( + width: 500, + height: 50, + child: MIHButton( + onTap: () { + addAppointmentCall(); + }, + buttonText: "Add", + buttonColor: + MzanziInnovationHub.of(context)!.theme.successColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ), + ], + ); + }, + ); + } + + bool isAppointmentInputValid() { + if (_appointmentTitleController.text.isEmpty || + _appointmentDescriptionIDController.text.isEmpty || + _appointmentDateController.text.isEmpty || + _appointmentTimeController.text.isEmpty) { + return false; + } else { + return true; + } + } + + void addAppointmentCall() { + if (isAppointmentInputValid()) { + MihMzansiCalendarApis.addPersonalAppointment( + widget.signedInUser, + widget.signedInUser.app_id, + _appointmentTitleController.text, + _appointmentDescriptionIDController.text, + _appointmentDateController.text, + _appointmentTimeController.text, + context, + ); + } else { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Input Error"); + }, + ); + } + checkforchange(); + } + void checkforchange() { setState(() { + personalAppointmentResults = + MihMzansiCalendarApis.getPersonalAppointments( + widget.signedInUser.app_id, + selectedDay, + ); personalQueueResults = MIHApiCalls.fetchPersonalAppointmentsAPICall( selectedDay, widget.signedInUser.app_id, @@ -109,50 +245,80 @@ class _PatientAccessRequestState extends State { ); } - 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, + Widget getBody() { + return Stack( + children: [ + Column( children: [ - FutureBuilder( - future: personalQueueResults, - builder: (context, snapshot) { - 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, - ), - ); - } + // const Text( + // "Appointments", + // style: TextStyle( + // fontWeight: FontWeight.bold, + // fontSize: 25, + // ), + // ), + 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: personalAppointmentResults, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Expanded( + child: Center(child: Mihloadingcircle())); + } else if (snapshot.connectionState == + ConnectionState.done && + snapshot.hasData) { + return displayAppointmentList(snapshot.requireData); + } else { + return Center( + child: Text( + "Error pulling appointments", + style: TextStyle( + fontSize: 25, + color: MzanziInnovationHub.of(context)! + .theme + .errorColor()), + textAlign: TextAlign.center, + ), + ); + } + }), + ], + ) ], - ) + ), + Positioned( + right: 0, + bottom: 0, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50), + color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + ), + child: IconButton( + color: MzanziInnovationHub.of(context)!.theme.primaryColor(), + onPressed: () { + addAppointmentWindow(); + }, + icon: const Icon( + Icons.add, + size: 50, + ), + ), + )) ], ); } @@ -161,6 +327,10 @@ class _PatientAccessRequestState extends State { void dispose() { filterController.dispose(); appointmentDateController.dispose(); + _appointmentDateController.dispose(); + _appointmentTimeController.dispose(); + _appointmentTitleController.dispose(); + _appointmentDescriptionIDController.dispose(); super.dispose(); } @@ -168,6 +338,11 @@ class _PatientAccessRequestState extends State { void initState() { appointmentDateController.addListener(checkforchange); setState(() { + personalAppointmentResults = + MihMzansiCalendarApis.getPersonalAppointments( + widget.signedInUser.app_id, + selectedDay, + ); personalQueueResults = MIHApiCalls.fetchPersonalAppointmentsAPICall( selectedDay, widget.signedInUser.app_id, @@ -178,16 +353,9 @@ class _PatientAccessRequestState extends State { @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 MihAppToolBody( + borderOn: true, + bodyItem: getBody(), ); } } From afca4baedf144f4b4cd4cda3baedae8c2d34b6a4 Mon Sep 17 00:00:00 2001 From: yaso Date: Wed, 12 Feb 2025 12:30:16 +0200 Subject: [PATCH 08/12] appointment list builder created --- .../builder/build_appointment_list.dart | 482 ++++++++++++++---- 1 file changed, 394 insertions(+), 88 deletions(-) diff --git a/Frontend/lib/mih_packages/appointment/builder/build_appointment_list.dart b/Frontend/lib/mih_packages/appointment/builder/build_appointment_list.dart index 15d9151f..35f98784 100644 --- a/Frontend/lib/mih_packages/appointment/builder/build_appointment_list.dart +++ b/Frontend/lib/mih_packages/appointment/builder/build_appointment_list.dart @@ -1,26 +1,41 @@ +import 'package:Mzansi_Innovation_Hub/main.dart'; +import 'package:Mzansi_Innovation_Hub/mih_apis/mih_mzansi_calendar_apis.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_button.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_date_input.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_multiline_text_input.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_text_input.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_inputs_and_buttons/mih_time_input.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_layout/mih_window.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_pop_up_messages/mih_delete_message.dart'; +import 'package:Mzansi_Innovation_Hub/mih_components/mih_pop_up_messages/mih_error_message.dart'; +import 'package:Mzansi_Innovation_Hub/mih_env/env.dart'; +import 'package:Mzansi_Innovation_Hub/mih_objects/app_user.dart'; +import 'package:Mzansi_Innovation_Hub/mih_objects/appointment.dart'; 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; + final List appointmentList; final AppUser signedInUser; + final TextEditingController titleController; + final TextEditingController descriptionIDController; + final TextEditingController dateController; + final TextEditingController timeController; const BuildAppointmentList({ super.key, - required this.patientQueue, + required this.appointmentList, required this.signedInUser, + required this.titleController, + required this.descriptionIDController, + required this.dateController, + required this.timeController, }); @override - State createState() => _BuildPatientsListState(); + State createState() => _BuildAppointmentListState(); } -class _BuildPatientsListState extends State { +class _BuildAppointmentListState extends State { String baseAPI = AppEnviroment.baseApiUrl; TextEditingController dateController = TextEditingController(); TextEditingController timeController = TextEditingController(); @@ -29,83 +44,374 @@ class _BuildPatientsListState extends State { TextEditingController lnameController = TextEditingController(); TextEditingController daysExtensionController = TextEditingController(); int counter = 0; + late double width; + late double height; - 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); + double getPaddingSize() { + if (MzanziInnovationHub.of(context)!.theme.screenType == "desktop") { + return (width / 10); + } else { + return 0.0; + } + } - line234 += - widget.patientQueue[index].date_time.split('T')[1].substring(0, 5); + Widget displayAppointment(int index) { + String heading = + "${widget.appointmentList[index].date_time.split('T')[1].substring(0, 5)} - ${widget.appointmentList[index].title.toUpperCase()}"; + String description = widget.appointmentList[index].description; + DateTime now = new DateTime.now(); + int hourNow = int.parse(now.toString().split(' ')[1].substring(0, 2)); + String date = + new DateTime(now.year, now.month, now.day).toString().split(' ')[0]; + String appointDate = widget.appointmentList[index].date_time.split('T')[0]; + int appointHour = int.parse( + widget.appointmentList[index].date_time.split('T')[1].substring(0, 2)); + // print("Date Time Now: $now"); + // print("Hour Now: $hourNow"); + // print("Date: $date"); + // print("Appointment Date: $appointDate"); + // print("Appointment Hour: $appointHour"); + Color appointmentColor = + MzanziInnovationHub.of(context)!.theme.secondaryColor(); + if (date == appointDate) { + if (appointHour < hourNow) { + appointmentColor = + MzanziInnovationHub.of(context)!.theme.messageTextColor(); + } else if (appointHour == hourNow) { + appointmentColor = + MzanziInnovationHub.of(context)!.theme.successColor(); + } + } else if (DateTime.parse(appointDate).isBefore(DateTime.parse(date))) { + appointmentColor = + MzanziInnovationHub.of(context)!.theme.messageTextColor(); + } - return ListTile( - title: Text( - title, - style: TextStyle( - color: MzanziInnovationHub.of(context)!.theme.secondaryColor(), + return Padding( + padding: const EdgeInsets.all(5.0), + child: Container( + decoration: BoxDecoration( + border: Border.all( + width: 3.0, + color: appointmentColor, + ), + borderRadius: BorderRadius.circular(20)), + child: ListTile( + title: Text( + heading, + style: TextStyle( + color: appointmentColor, + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text( + description, + style: TextStyle( + color: appointmentColor, + overflow: TextOverflow.ellipsis, + ), + ), + onTap: () { + setState(() { + widget.titleController.text = widget.appointmentList[index].title; + widget.descriptionIDController.text = + widget.appointmentList[index].description; + widget.dateController.text = + widget.appointmentList[index].date_time.split('T')[0]; + widget.timeController.text = widget + .appointmentList[index].date_time + .split('T')[1] + .substring(0, 5); + }); + appointmentDetailsWindow(index); + }, ), ), - 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") { + void appointmentDetailsWindow(int index) { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) { + return MIHWindow( + fullscreen: false, + windowTitle: "Appointment Details", + windowTools: [ + Visibility( + visible: canEditAppointment(index), + child: IconButton( + onPressed: () { + deleteAppointmentConfirmationWindow(index); + }, + icon: const Icon(Icons.delete), + ), + ), + ], + onWindowTapClose: () { + Navigator.of(context).pop(); + widget.dateController.clear(); + widget.timeController.clear(); + widget.titleController.clear(); + widget.descriptionIDController.clear(); + }, + windowBody: [ + SizedBox( + // width: 500, + child: MIHTextField( + controller: widget.titleController, + hintText: "Title", + editable: false, + required: false, + ), + ), + const SizedBox(height: 10), + SizedBox( + // width: 500, + child: MIHTextField( + controller: widget.dateController, + hintText: "Date", + editable: false, + required: false, + )), + const SizedBox(height: 10), + SizedBox( + // width: 500, + child: MIHTextField( + controller: widget.timeController, + hintText: "Time", + editable: false, + required: false, + )), + const SizedBox(height: 10), + SizedBox( + // width: 500, + height: 250, + child: MIHMLTextField( + controller: widget.descriptionIDController, + hintText: "Description", + editable: false, + required: false, + ), + ), + const SizedBox(height: 20), + Visibility( + visible: canEditAppointment(index), + child: SizedBox( + width: 500, + height: 50, + child: MIHButton( + onTap: () { + appointmentUpdateWindow(index); + }, + buttonText: "Edit", + buttonColor: + MzanziInnovationHub.of(context)!.theme.secondaryColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ), + ), + // SizedBox( + // width: 500, + // height: 50, + // child: MIHButton( + // onTap: () { + // addAppointmentCall(); + // checkforchange(); + // }, + // buttonText: "Add", + // buttonColor: + // MzanziInnovationHub.of(context)!.theme.successColor(), + // textColor: + // MzanziInnovationHub.of(context)!.theme.primaryColor(), + // ), + // ), + ], + ); + }, + ); + } + + void appointmentUpdateWindow(int index) { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) { + return MIHWindow( + fullscreen: false, + windowTitle: "Update Appointment", + windowTools: [], + onWindowTapClose: () { + setState(() { + widget.titleController.text = widget.appointmentList[index].title; + widget.descriptionIDController.text = + widget.appointmentList[index].description; + widget.dateController.text = + widget.appointmentList[index].date_time.split('T')[0]; + widget.timeController.text = widget + .appointmentList[index].date_time + .split('T')[1] + .substring(0, 5); + }); + Navigator.of(context).pop(); + }, + windowBody: [ + SizedBox( + // width: 500, + child: MIHTextField( + controller: widget.titleController, + hintText: "Title", + editable: true, + required: true, + ), + ), + const SizedBox(height: 10), + SizedBox( + // width: 500, + child: MIHDateField( + controller: widget.dateController, + lableText: "Date", + required: true, + ), + ), + const SizedBox(height: 10), + SizedBox( + // width: 500, + child: MIHTimeField( + controller: widget.timeController, + lableText: "Time", + required: true, + ), + ), + const SizedBox(height: 10), + SizedBox( + // width: 500, + height: 250, + child: MIHMLTextField( + controller: widget.descriptionIDController, + hintText: "Description", + editable: true, + required: true, + ), + ), + const SizedBox(height: 20), + Wrap( + alignment: WrapAlignment.center, + runSpacing: 10, + spacing: 10, + children: [ + SizedBox( + width: 500, + height: 50, + child: MIHButton( + onTap: () { + updateAppointmentCall(index); + }, + buttonText: "Update", + buttonColor: + MzanziInnovationHub.of(context)!.theme.successColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ), + SizedBox( + width: 500, + height: 50, + child: MIHButton( + onTap: () { + setState(() { + widget.titleController.text = + widget.appointmentList[index].title; + widget.descriptionIDController.text = + widget.appointmentList[index].description; + widget.dateController.text = widget + .appointmentList[index].date_time + .split('T')[0]; + widget.timeController.text = widget + .appointmentList[index].date_time + .split('T')[1] + .substring(0, 5); + }); + Navigator.of(context).pop(); + }, + buttonText: "Cancel", + buttonColor: + MzanziInnovationHub.of(context)!.theme.errorColor(), + textColor: + MzanziInnovationHub.of(context)!.theme.primaryColor(), + ), + ), + ], + ) + ], + ); + }, + ); + } + + bool isAppointmentInputValid() { + if (widget.titleController.text.isEmpty || + widget.descriptionIDController.text.isEmpty || + widget.dateController.text.isEmpty || + widget.timeController.text.isEmpty) { + return false; + } else { + return true; + } + } + + void deleteAppointmentConfirmationWindow(int index) { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) { + return MIHDeleteMessage( + deleteType: "Appointment", + onTap: () { + deleteAppointmentCall(index); + }); + }, + ); + } + + void updateAppointmentCall(int index) { + if (isAppointmentInputValid()) { + MihMzansiCalendarApis.updatePersonalAppointment( + widget.signedInUser, + widget.appointmentList[index].idappointments, + widget.titleController.text, + widget.descriptionIDController.text, + widget.dateController.text, + widget.timeController.text, + context, + ); + } else { + showDialog( + context: context, + builder: (context) { + return const MIHErrorMessage(errorType: "Input Error"); + }, + ); + } + } + + void deleteAppointmentCall(int index) { + MihMzansiCalendarApis.deleteLoyaltyCardAPICall( + widget.signedInUser, + widget.appointmentList[index].idappointments, + context, + ); + } + + bool canEditAppointment(int index) { + if (widget.appointmentList[index].business_id == "") { 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(); @@ -116,21 +422,21 @@ class _BuildPatientsListState extends State { @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); - }, + var size = MediaQuery.of(context).size; + setState(() { + width = size.width; + height = size.height; + }); + return Padding( + padding: EdgeInsets.symmetric(horizontal: getPaddingSize()), + child: ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: widget.appointmentList.length, + itemBuilder: (context, index) { + return displayAppointment(index); + }, + ), ); } } From 2ef92ff7071d04feec2048a110a2f9da91095b82 Mon Sep 17 00:00:00 2001 From: yaso Date: Wed, 12 Feb 2025 12:30:39 +0200 Subject: [PATCH 09/12] change prevois appointment to queue --- .../appointment/builder/build_queue_list.dart | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 Frontend/lib/mih_packages/appointment/builder/build_queue_list.dart diff --git a/Frontend/lib/mih_packages/appointment/builder/build_queue_list.dart b/Frontend/lib/mih_packages/appointment/builder/build_queue_list.dart new file mode 100644 index 00000000..ac758be1 --- /dev/null +++ b/Frontend/lib/mih_packages/appointment/builder/build_queue_list.dart @@ -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 BuildQueueList extends StatefulWidget { + final List patientQueue; + final AppUser signedInUser; + + const BuildQueueList({ + super.key, + required this.patientQueue, + required this.signedInUser, + }); + + @override + State createState() => _BuildQueueListState(); +} + +class _BuildQueueListState extends State { + 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); + }, + ); + } +} From 9b53433136caae243847d80a2efa333a3cc29abb Mon Sep 17 00:00:00 2001 From: yaso Date: Wed, 12 Feb 2025 12:30:53 +0200 Subject: [PATCH 10/12] add route for /calendar --- Frontend/lib/mih_router/routeGenerator.dart | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Frontend/lib/mih_router/routeGenerator.dart b/Frontend/lib/mih_router/routeGenerator.dart index 444d9501..b94d66fc 100644 --- a/Frontend/lib/mih_router/routeGenerator.dart +++ b/Frontend/lib/mih_router/routeGenerator.dart @@ -1,4 +1,5 @@ import 'package:Mzansi_Innovation_Hub/mih_components/mih_package/test/package_test.dart'; +import 'package:Mzansi_Innovation_Hub/mih_packages/appointment/mzansi_calendar.dart'; import 'package:Mzansi_Innovation_Hub/mih_packages/calculator/calculator.dart'; import 'package:Mzansi_Innovation_Hub/mih_packages/mih_policy_tos/mih_privacy_polocy.dart'; import 'package:Mzansi_Innovation_Hub/mih_packages/mih_policy_tos/mih_terms_of_service.dart'; @@ -8,7 +9,6 @@ import 'package:Mzansi_Innovation_Hub/mih_packages/mzansi_wallet/mzansi_wallet.d 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'; @@ -217,9 +217,26 @@ class RouteGenerator { //print("route generator: $args"); return MaterialPageRoute( settings: settings, - builder: (_) => Appointments( + builder: (_) => MzansiCalendar( signedInUser: args, ), + // Appointments( + // signedInUser: args, + // ), + ); + } + return _errorRoute(); + case '/appointments': + if (args is AppUser) { + //print("route generator: $args"); + return MaterialPageRoute( + settings: settings, + builder: (_) => MzansiCalendar( + signedInUser: args, + ), + // Appointments( + // signedInUser: args, + // ), ); } return _errorRoute(); From 25862b86b1e39c9376a99664b76838519c400b2f Mon Sep 17 00:00:00 2001 From: yaso Date: Wed, 12 Feb 2025 12:31:40 +0200 Subject: [PATCH 11/12] add delete confirmation prompt for appointment delete --- .../mih_delete_message.dart | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/Frontend/lib/mih_components/mih_pop_up_messages/mih_delete_message.dart b/Frontend/lib/mih_components/mih_pop_up_messages/mih_delete_message.dart index 229057eb..df7e0cf4 100644 --- a/Frontend/lib/mih_components/mih_pop_up_messages/mih_delete_message.dart +++ b/Frontend/lib/mih_components/mih_pop_up_messages/mih_delete_message.dart @@ -297,6 +297,88 @@ class _MIHDeleteMessageState extends State { ); } + void setDeleteAppointment() { + messageTypes["Appointment"] = 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 appointment will be deleted permanently from your calendar. 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]; } @@ -317,7 +399,7 @@ class _MIHDeleteMessageState extends State { setDeleteNote(); setFileNote(); setDeleteEmployee(); - + setDeleteAppointment(); //print(size); // setState(() { // width = size.width; From fceb26535e6ed19b250b7f71e2134bcaca30db49 Mon Sep 17 00:00:00 2001 From: yaso Date: Wed, 12 Feb 2025 12:31:59 +0200 Subject: [PATCH 12/12] create api cal hepler for appointmets --- .../mih_apis/mih_mzansi_calendar_apis.dart | 241 ++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 Frontend/lib/mih_apis/mih_mzansi_calendar_apis.dart diff --git a/Frontend/lib/mih_apis/mih_mzansi_calendar_apis.dart b/Frontend/lib/mih_apis/mih_mzansi_calendar_apis.dart new file mode 100644 index 00000000..99d950f1 --- /dev/null +++ b/Frontend/lib/mih_apis/mih_mzansi_calendar_apis.dart @@ -0,0 +1,241 @@ +import 'dart:convert'; + +import 'package:Mzansi_Innovation_Hub/mih_components/mih_pop_up_messages/mih_loading_circle.dart'; +import 'package:Mzansi_Innovation_Hub/mih_objects/app_user.dart'; +import 'package:Mzansi_Innovation_Hub/mih_objects/appointment.dart'; +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/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'; + +class MihMzansiCalendarApis { + final baseAPI = AppEnviroment.baseApiUrl; + + /// This function is used to fetch a list of appointment for a personal user. + /// + /// Patameters: + /// app_id, + /// date (yyyy-mm-dd), + /// + /// Returns Future>. + static Future> getPersonalAppointments( + String app_id, + String date, + ) async { + //print("Patien manager page: $endpoint"); + final response = await http.get(Uri.parse( + "${AppEnviroment.baseApiUrl}/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 personalAppointments = + List.from(l.map((model) => Appointment.fromJson(model))); + //print("Here3"); + //print(patientQueue); + return personalAppointments; + } else { + throw Exception('failed to fatch loyalty cards'); + } + } + + /// This function is used to Delete loyalty card from users mzansi Calendar. + /// + /// Patameters:- + /// AppUser signedInUser, + /// int idloyalty_cards, + /// BuildContext context, + /// + /// Returns VOID (TRIGGERS NOTIGICATIOPN ON SUCCESS) + static Future deleteLoyaltyCardAPICall( + AppUser signedInUser, + int idappointments, + BuildContext context, + ) async { + var response = await http.delete( + Uri.parse("${AppEnviroment.baseApiUrl}/appointment/delete/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + body: jsonEncode({"idappointments": idappointments}), + ); + //print("Here4"); + //print(response.statusCode); + if (response.statusCode == 200) { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + Navigator.of(context).pushNamed( + '/calendar', + arguments: signedInUser, + ); + String message = + "The appointment has been deleted successfully. This means it will no longer be visible in your Calendar."; + successPopUp(message, context); + } else { + internetConnectionPopUp(context); + } + } + + /// This function is used to add an appointment to users mzansi Calendar. + /// + /// Patameters:- + /// AppUser signedInUser, + /// String app_id, + /// String title, + /// String description, + /// String date, + /// String time, + /// BuildContext context, + /// + /// Returns VOID (TRIGGERS SUCCESS pop up) + static Future addPersonalAppointment( + AppUser signedInUser, + String app_id, + String title, + String description, + String date, + String time, + BuildContext context, + ) async { + loadingPopUp(context); + var response = await http.post( + Uri.parse("${AppEnviroment.baseApiUrl}/appointment/insert/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + body: jsonEncode({ + "app_id": app_id, + "business_id": "", + "title": title, + "description": description, + "date": date, + "time": time, + }), + ); + if (response.statusCode == 201) { + Navigator.pop(context); + Navigator.pop(context); + Navigator.pop(context); + String message = + "Your appointment \"$title\" for the $date $title has been deleted."; + + // Navigator.pop(context); + Navigator.of(context).pushNamed( + '/calendar', + arguments: signedInUser, + ); + successPopUp(message, context); + } else { + Navigator.pop(context); + internetConnectionPopUp(context); + } + } + + /// This function is used to update an appointment to users mzansi Calendar. + /// + /// Patameters:- + /// AppUser signedInUser, + /// String app_id, + /// int idappointments, + /// String title, + /// String description, + /// String date, + /// String time, + /// BuildContext context, + /// + /// Returns VOID (TRIGGERS SUCCESS pop up) + static Future updatePersonalAppointment( + AppUser signedInUser, + int idappointments, + String title, + String description, + String date, + String time, + BuildContext context, + ) async { + loadingPopUp(context); + var response = await http.put( + Uri.parse("${AppEnviroment.baseApiUrl}/appointment/update/"), + headers: { + "Content-Type": "application/json; charset=UTF-8" + }, + body: jsonEncode({ + "idappointments": idappointments, + "title": title, + "description": description, + "date": date, + "time": time, + }), + ); + if (response.statusCode == 200) { + Navigator.pop(context); + Navigator.pop(context); + Navigator.pop(context); + String message = + "Your appointment \"$title\" has been updates to the $date $title."; + + Navigator.pop(context); + Navigator.of(context).pushNamed( + '/calendar', + arguments: signedInUser, + ); + successPopUp(message, context); + } else { + Navigator.pop(context); + 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, + ); + }, + ); + } + + static void loadingPopUp(BuildContext context) { + showDialog( + context: context, + builder: (context) { + return const Mihloadingcircle(); + }, + ); + } +}