From 7e38d608024040dbe9a5eb3c59c490e27b82c84f Mon Sep 17 00:00:00 2001 From: ElenitaMLG Date: Tue, 30 Apr 2024 12:48:19 +0300 Subject: [PATCH] Dashboard Page - Appointments: - create separate dashboard components for doctor and patient - add appointments to dashboard components - add code to backend for appointments retrieval --- .../API/Controllers/AppointmentsController.cs | 16 ++ .../AppointmentManagementHandler.cs | 46 +++ backend/Core/Entities/Appointment.cs | 2 +- frontend/Components/Layout/AuthLayout.razor | 2 + frontend/Components/Pages/DashboardPage.razor | 50 ++-- .../Components/Pages/DoctorDashboard.razor | 155 ++++++++++ .../Components/Pages/PatientDashboard.razor | 269 ++++++++++++++++++ frontend/HealthcareManagerUiWebAssem.csproj | 8 - frontend/Models/Appointment.cs | 9 + frontend/Models/AppointmentManagementModel.cs | 8 + frontend/Program.cs | 2 + .../Appointment/AppointmentService.cs | 88 ++++++ .../Appointment/IAppointmentService.cs | 14 + .../Authentication/AuthenticationService.cs | 2 +- frontend/Services/Http/IRequestHttpService.cs | 2 + frontend/Services/Http/RequestHttpService.cs | 12 + frontend/wwwroot/AuthLayout.css | 10 +- 17 files changed, 665 insertions(+), 30 deletions(-) create mode 100644 frontend/Components/Pages/DoctorDashboard.razor create mode 100644 frontend/Components/Pages/PatientDashboard.razor create mode 100644 frontend/Models/Appointment.cs create mode 100644 frontend/Models/AppointmentManagementModel.cs create mode 100644 frontend/Services/Appointment/AppointmentService.cs create mode 100644 frontend/Services/Appointment/IAppointmentService.cs diff --git a/backend/API/Controllers/AppointmentsController.cs b/backend/API/Controllers/AppointmentsController.cs index 5647309..a0b4098 100644 --- a/backend/API/Controllers/AppointmentsController.cs +++ b/backend/API/Controllers/AppointmentsController.cs @@ -38,4 +38,20 @@ public class AppointmentsController : BaseApiController var response = await handler.HandleDeleteAppointment(dto).ConfigureAwait(false); return StatusCode(response.StatusCode, response); } + + [HttpGet("patient/{id:guid}")] + public async Task> GetAppointmentsByPatient(Guid id) + { + var handler = new AppointmentManagementHandler(_appointmentsMongoDbService, _patientRepository, _doctorRepository); + var response = await handler.HandleGetAppointmentsByPatientId(id); + return StatusCode(response.StatusCode, response); + } + + [HttpGet("doctor/{id:guid}")] + public async Task> GetAppointmentsByDoctor(Guid id) + { + var handler = new AppointmentManagementHandler(_appointmentsMongoDbService, _patientRepository, _doctorRepository); + var response = await handler.HandleGetAppointmentsByDoctorId(id); + return StatusCode(response.StatusCode, response); + } } \ No newline at end of file diff --git a/backend/Application/Endpoints/Appointments/AppointmentManagementHandler.cs b/backend/Application/Endpoints/Appointments/AppointmentManagementHandler.cs index e528e9f..528ed3b 100644 --- a/backend/Application/Endpoints/Appointments/AppointmentManagementHandler.cs +++ b/backend/Application/Endpoints/Appointments/AppointmentManagementHandler.cs @@ -116,4 +116,50 @@ public class AppointmentManagementHandler Data = null }; } + + public async Task HandleGetAppointmentsByPatientId(Guid patientId) + { + var criteria = new List<(string FieldName, string Value)> + { + ("PatientId", patientId.ToString()) + }; + var appointments = await _appointmentsMongoDbService.FindAsync(criteria); + + if (appointments.Count == 0) + return new BaseResponse + { + StatusCode = HttpStatusCodes.NotFound, + Message = $"Appointments not found in system for patient with id {patientId}.", + Data = null + }; + return new BaseResponse + { + StatusCode = HttpStatusCodes.OK, + Message = $"Appointments for patient with id {patientId} successfully retrieved.", + Data = appointments + }; + } + + public async Task HandleGetAppointmentsByDoctorId(Guid doctorId) + { + var criteria = new List<(string FieldName, string Value)> + { + ("DoctorId", doctorId.ToString()) + }; + var appointments = await _appointmentsMongoDbService.FindAsync(criteria); + + if (appointments.Count == 0) + return new BaseResponse + { + StatusCode = HttpStatusCodes.NotFound, + Message = $"Appointments not found in system for doctor with id {doctorId}.", + Data = null + }; + return new BaseResponse + { + StatusCode = HttpStatusCodes.OK, + Message = $"Appointments for doctor with id {doctorId} successfully retrieved.", + Data = appointments + }; + } } \ No newline at end of file diff --git a/backend/Core/Entities/Appointment.cs b/backend/Core/Entities/Appointment.cs index 5935351..328d61f 100644 --- a/backend/Core/Entities/Appointment.cs +++ b/backend/Core/Entities/Appointment.cs @@ -10,7 +10,7 @@ public class Appointment public string Id { get; private set; } public string PatientId { get; private set; } public string DoctorId { get; private set; } - public List AppointmentsList { get; } + public List AppointmentsList { get; set; } public void SetId(string id) { diff --git a/frontend/Components/Layout/AuthLayout.razor b/frontend/Components/Layout/AuthLayout.razor index 0e02478..5e7f3d5 100644 --- a/frontend/Components/Layout/AuthLayout.razor +++ b/frontend/Components/Layout/AuthLayout.razor @@ -6,6 +6,8 @@ + +
diff --git a/frontend/Components/Pages/DashboardPage.razor b/frontend/Components/Pages/DashboardPage.razor index b399872..5bf8bc8 100644 --- a/frontend/Components/Pages/DashboardPage.razor +++ b/frontend/Components/Pages/DashboardPage.razor @@ -1,28 +1,44 @@ @page "/dashboard/{Role}" -@using HealthcareManagerUiWebAssem.Services.User - -@layout AuthLayout -@inject UserService UserService Dashboard -

login success

-

@displayMessage

-
- My Profile -
+ + + -@code { - [Parameter] public string Role { get; set; } - private string displayMessage; - - protected override void OnInitialized() - { - var userId = UserService.UserId; - displayMessage = $"Login success for {Role} with ID: {userId}"; + + +My Profile + +@switch (Role) +{ + case "doctor": + + break; + case "patient": + + break; + default: +

Unknown role. Please contact support.

+ break; +} + + +@code { + [Parameter] public string Role { get; set; } } \ No newline at end of file diff --git a/frontend/Components/Pages/DoctorDashboard.razor b/frontend/Components/Pages/DoctorDashboard.razor new file mode 100644 index 0000000..0ad2be9 --- /dev/null +++ b/frontend/Components/Pages/DoctorDashboard.razor @@ -0,0 +1,155 @@ +@using System.Text.Json +@using HealthcareManagerUiWebAssem.Models +@using HealthcareManagerUiWebAssem.Services.Profile +@using HealthcareManagerUiWebAssem.Services.Appointment +@using HealthcareManagerUiWebAssem.Services.User +@inject UserService UserService +@inject IProfileService ProfileService +@inject IAppointmentService AppointmentService +@inject IJSRuntime JS + +
+

Doctor Menu

+

Here you can manage appointments, review your patients' medical history and chat with your patients.

+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + @foreach (var appointment in appointments) + + { + + @foreach (var date in appointment.AppointmentsList) + + { + + + + + + + + + + + + } + + } + + + +
Patient NameAppointment TimeDelete
@GetPatientName(new Guid(appointment.PatientId))@date.ToString("yyyy-MM-dd HH:mm")
+ +
+
+
+ +
+
+ +@code { + private List patients; + private string selectedDoctorId; + private DateTime? selectedDate; + private List appointments; + private List medicalHistoryFiles; + [Parameter] public string Role { get; set; } + + protected override async Task OnInitializedAsync() + { + await InitializePatients(); + await LoadAppointments(); + } + + private async Task LoadAppointments() + { + var response = await AppointmentService.GetAppointmentsByDoctor(UserService.UserId); + var jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + if (response.StatusCode == HttpStatusCodes.OK) + { + appointments = JsonSerializer.Deserialize>(response.Data, jsonOptions) ?? new List(); + } + else + { + appointments = new List(); + } + } + + private async Task InitializePatients() + { + var response = await ProfileService.GetPatients(); + var jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + if (response.StatusCode == HttpStatusCodes.OK) + { + patients = JsonSerializer.Deserialize>(response.Data, jsonOptions); + } + else + { + // Handle error + } + } + + + + private void AddMedicalHistoryFile() + + { + + // Logic for adding a new appointment + + } + + private async Task DeleteAppointment(Appointment appointmentToDelete) + + { + + var appointmentModelToDelete = new AppointmentManagementModel + + { + + PatientId = new Guid(appointmentToDelete.PatientId), + + DoctorId = new Guid(appointmentToDelete.DoctorId), + + Appointment = appointmentToDelete.AppointmentsList.FirstOrDefault() + + }; + + await AppointmentService.DeleteAppointment(appointmentModelToDelete); + + appointments.Remove(appointmentToDelete); + + } + + + + private string GetPatientName(Guid patientId) + { + var patient = patients.Select(d => d).FirstOrDefault(d => d.Id == patientId); + return patient?.Name ?? string.Empty; + } +} diff --git a/frontend/Components/Pages/PatientDashboard.razor b/frontend/Components/Pages/PatientDashboard.razor new file mode 100644 index 0000000..8eca740 --- /dev/null +++ b/frontend/Components/Pages/PatientDashboard.razor @@ -0,0 +1,269 @@ +@using System.Text.Json +@using HealthcareManagerUiWebAssem.Models +@using HealthcareManagerUiWebAssem.Services.Profile +@using HealthcareManagerUiWebAssem.Services.Appointment +@using HealthcareManagerUiWebAssem.Services.User +@inject UserService UserService +@inject IProfileService ProfileService +@inject IAppointmentService AppointmentService +@inject IJSRuntime JS + +
+

Patient Menu

+

Here you can manage appointments, review your medical history and chat with doctors.

+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + @foreach (var appointment in appointments) + + { + + @foreach (var date in appointment.AppointmentsList) + + { + + + + + + + + + + + + } + + } + + + +
Doctor NameAppointment TimeDelete
@GetDoctorName(new Guid(appointment.DoctorId))@date.ToString("yyyy-MM-dd HH:mm")
+ +
+
+
+ +
+ +
+ +@code { + private List doctors; + private string selectedDoctorId; + private DateTime? selectedDate; + private List appointments; + private List medicalHistoryFiles; + [Parameter] public string Role { get; set; } + + protected override async Task OnInitializedAsync() + { + await InitializeDoctors(); + await LoadAppointments(); + } + + private async Task LoadAppointments() + { + var response = await AppointmentService.GetAppointmentsByPatient(UserService.UserId); + var jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + if (response.StatusCode == HttpStatusCodes.OK) + { + appointments = JsonSerializer.Deserialize>(response.Data, jsonOptions) ?? new List(); + } + else + { + appointments = new List(); + } + } + + private async Task ShowAddAppointmentModal() + { + await JS.InvokeVoidAsync("eval", "new bootstrap.Modal(document.getElementById('addAppointmentModal')).show();"); + } + + private async Task InitializeDoctors() + { + var response = await ProfileService.GetDoctors(); + var jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + if (response.StatusCode == HttpStatusCodes.OK) + { + doctors = JsonSerializer.Deserialize>(response.Data, jsonOptions); + } + else + { + // Handle error + } + } + + private async Task CreateAppointment() + + { + + if (string.IsNullOrEmpty(selectedDoctorId) || !selectedDate.HasValue) + + { + + // Handle validation + + return; + + } + + + + // Call AddAppointment method, which is not shown here + + var createAppointmentModel = new AppointmentManagementModel + + { + + PatientId = UserService.UserId, + + DoctorId = new Guid(selectedDoctorId), + + Appointment = (DateTime)selectedDate + + }; + + + + await AppointmentService.CreateAppointment(createAppointmentModel); + + // You'll need to pass UserService.UserId, selectedDoctorId, and selectedDate.Value to it + + + + // Close the modal after creation + + await JS.InvokeVoidAsync("eval", "new bootstrap.Modal(document.getElementById('addAppointmentModal')).hide();"); + + + + // Optionally, refresh the appointments list + + } + + + + private void AddMedicalHistoryFile() + + { + + // Logic for adding a new appointment + + } + + private async Task DeleteAppointment(Appointment appointmentToDelete) + + { + + var appointmentModelToDelete = new AppointmentManagementModel + + { + + PatientId = new Guid(appointmentToDelete.PatientId), + + DoctorId = new Guid(appointmentToDelete.DoctorId), + + Appointment = appointmentToDelete.AppointmentsList.FirstOrDefault() + + }; + + await AppointmentService.DeleteAppointment(appointmentModelToDelete); + + appointments.Remove(appointmentToDelete); + + } + + + + private string GetDoctorName(Guid doctorId) + { + var doctor = doctors.Select(d => d).FirstOrDefault(d => d.Id == doctorId); + return doctor?.Name ?? string.Empty; + } +} diff --git a/frontend/HealthcareManagerUiWebAssem.csproj b/frontend/HealthcareManagerUiWebAssem.csproj index d023326..d904147 100644 --- a/frontend/HealthcareManagerUiWebAssem.csproj +++ b/frontend/HealthcareManagerUiWebAssem.csproj @@ -17,14 +17,6 @@ - - - - - - - - <_ContentIncludedByDefault Remove="Layout\AuthLayout.razor" /> <_ContentIncludedByDefault Remove="Layout\MainLayout.razor" /> diff --git a/frontend/Models/Appointment.cs b/frontend/Models/Appointment.cs new file mode 100644 index 0000000..ab65d08 --- /dev/null +++ b/frontend/Models/Appointment.cs @@ -0,0 +1,9 @@ +namespace HealthcareManagerUiWebAssem.Models; + +public class Appointment +{ + public string Id { get; set; } + public string PatientId { get; set; } + public string DoctorId { get; set; } + public List AppointmentsList { get; set; } = []; +} \ No newline at end of file diff --git a/frontend/Models/AppointmentManagementModel.cs b/frontend/Models/AppointmentManagementModel.cs new file mode 100644 index 0000000..e4c7226 --- /dev/null +++ b/frontend/Models/AppointmentManagementModel.cs @@ -0,0 +1,8 @@ +namespace HealthcareManagerUiWebAssem.Models; + +public class AppointmentManagementModel +{ + public Guid DoctorId { get; set; } + public Guid PatientId { get; set; } + public DateTime Appointment { get; set; } +} \ No newline at end of file diff --git a/frontend/Program.cs b/frontend/Program.cs index 7221201..001c44a 100644 --- a/frontend/Program.cs +++ b/frontend/Program.cs @@ -2,6 +2,7 @@ using Blazored.LocalStorage; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using HealthcareManagerUiWebAssem; +using HealthcareManagerUiWebAssem.Services.Appointment; using HealthcareManagerUiWebAssem.Services.Authentication; using HealthcareManagerUiWebAssem.Services.Http; using HealthcareManagerUiWebAssem.Services.Profile; @@ -22,5 +23,6 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); await builder.Build().RunAsync(); diff --git a/frontend/Services/Appointment/AppointmentService.cs b/frontend/Services/Appointment/AppointmentService.cs new file mode 100644 index 0000000..822a612 --- /dev/null +++ b/frontend/Services/Appointment/AppointmentService.cs @@ -0,0 +1,88 @@ +using HealthcareManagerUiWebAssem.Models; +using HealthcareManagerUiWebAssem.Services.Http; + +namespace HealthcareManagerUiWebAssem.Services.Appointment; + +public class AppointmentService : IAppointmentService +{ + private readonly IRequestHttpService _requestHttpService; + + public AppointmentService(IRequestHttpService requestHttpService) + { + _requestHttpService = requestHttpService; + } + + public async Task GetAppointmentsByPatient(Guid patientId) + { + try + { + var response = await _requestHttpService.GetByIdAsync("/Appointments/patient", patientId); + return response; + } + catch (Exception ex) + { + return new BaseResponse + { + StatusCode = HttpStatusCodes.InternalServerError, + Data = null, + Message = ex.Message + }; + } + } + + public async Task GetAppointmentsByDoctor(Guid doctorId) + { + try + { + var response = await _requestHttpService.GetByIdAsync("/Appointments/doctor", doctorId); + return response; + } + catch (Exception ex) + { + return new BaseResponse + { + StatusCode = HttpStatusCodes.InternalServerError, + Data = null, + Message = ex.Message + }; + } + } + + + public async Task CreateAppointment( + AppointmentManagementModel model) + { + try + { + var response = await _requestHttpService.PostAsync("/Appointments", model); + return response; + } + catch (Exception ex) + { + return new BaseResponse + { + StatusCode = HttpStatusCodes.InternalServerError, + Data = null, + Message = ex.Message + }; + } + } + + public async Task DeleteAppointment(AppointmentManagementModel model) + { + try + { + var response = await _requestHttpService.DeleteByFilterAsync("/Appointments", model); + return response; + } + catch (Exception ex) + { + return new BaseResponse + { + StatusCode = HttpStatusCodes.InternalServerError, + Data = null, + Message = ex.Message + }; + } + } +} \ No newline at end of file diff --git a/frontend/Services/Appointment/IAppointmentService.cs b/frontend/Services/Appointment/IAppointmentService.cs new file mode 100644 index 0000000..1804e32 --- /dev/null +++ b/frontend/Services/Appointment/IAppointmentService.cs @@ -0,0 +1,14 @@ +using HealthcareManagerUiWebAssem.Models; + +namespace HealthcareManagerUiWebAssem.Services.Appointment; + +public interface IAppointmentService +{ + Task GetAppointmentsByPatient(Guid patientId); + + Task GetAppointmentsByDoctor(Guid doctorId); + + Task CreateAppointment(AppointmentManagementModel model); + + Task DeleteAppointment(AppointmentManagementModel model); +} \ No newline at end of file diff --git a/frontend/Services/Authentication/AuthenticationService.cs b/frontend/Services/Authentication/AuthenticationService.cs index 13fe1f6..8832fe1 100644 --- a/frontend/Services/Authentication/AuthenticationService.cs +++ b/frontend/Services/Authentication/AuthenticationService.cs @@ -52,7 +52,7 @@ public class AuthenticationService : IAuthenticationService { try { - var response = await _requestHttpService.PostAsync("/Doctors/register", userRegistrationModel); + var response = await _requestHttpService.PostAsync("/Patients/register", userRegistrationModel); return response; } catch (Exception ex) diff --git a/frontend/Services/Http/IRequestHttpService.cs b/frontend/Services/Http/IRequestHttpService.cs index f9b59aa..10c5a97 100644 --- a/frontend/Services/Http/IRequestHttpService.cs +++ b/frontend/Services/Http/IRequestHttpService.cs @@ -12,4 +12,6 @@ public interface IRequestHttpService Task PutAsync(string uri, object data, IDictionary headers = null); Task DeleteAsync(string uri, Guid id, IDictionary headers = null); + + Task DeleteByFilterAsync(string uri, object data, IDictionary headers = null); } \ No newline at end of file diff --git a/frontend/Services/Http/RequestHttpService.cs b/frontend/Services/Http/RequestHttpService.cs index 1d0af57..3677d6c 100644 --- a/frontend/Services/Http/RequestHttpService.cs +++ b/frontend/Services/Http/RequestHttpService.cs @@ -79,6 +79,18 @@ public class RequestHttpService : IRequestHttpService Thread.Sleep(1000); return await HandleResponse(response, uri); } + + public async Task DeleteByFilterAsync(string uri, object data, IDictionary? headers = null) + { + headers ??= new Dictionary(); + var request = new HttpRequestMessage(HttpMethod.Delete, $"{_apiEndpoint}{uri}"); + AddHeaders(request, headers); + request.Content = new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8, "application/json"); + + var response = await _httpClient.SendAsync(request); + Thread.Sleep(1000); + return await HandleResponse(response, uri); + } private async void AddHeaders(HttpRequestMessage request, IDictionary? headers) { diff --git a/frontend/wwwroot/AuthLayout.css b/frontend/wwwroot/AuthLayout.css index b882325..9274a63 100644 --- a/frontend/wwwroot/AuthLayout.css +++ b/frontend/wwwroot/AuthLayout.css @@ -1,4 +1,4 @@ -body, html { +body, html { height: 100%; width: 100%; margin: 0; @@ -12,11 +12,11 @@ body { font-style: normal; position: relative; z-index: 0; /* Ensures the background is under the content */ - + overflow: scroll !important; } .background { - position: absolute; + position: fixed; top: -5%; left: -5%; width: 110%; @@ -68,4 +68,8 @@ body { display: flex; flex-direction: row; /* This will align the buttons side by side */ justify-content: center; /* Center the buttons within the container */ +} + +.modal-backdrop.show { + display: none !important; } \ No newline at end of file