Dashboard Page - Appointments:

- create separate dashboard components for doctor and patient
- add appointments to dashboard components
- add code to backend for appointments retrieval
This commit is contained in:
ElenitaMLG
2024-04-30 12:48:19 +03:00
parent 1ff7f51aa6
commit 7e38d60802
17 changed files with 665 additions and 30 deletions
@@ -38,4 +38,20 @@ public class AppointmentsController : BaseApiController
var response = await handler.HandleDeleteAppointment(dto).ConfigureAwait(false); var response = await handler.HandleDeleteAppointment(dto).ConfigureAwait(false);
return StatusCode(response.StatusCode, response); return StatusCode(response.StatusCode, response);
} }
[HttpGet("patient/{id:guid}")]
public async Task<ActionResult<BaseResponse>> 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<ActionResult<BaseResponse>> GetAppointmentsByDoctor(Guid id)
{
var handler = new AppointmentManagementHandler(_appointmentsMongoDbService, _patientRepository, _doctorRepository);
var response = await handler.HandleGetAppointmentsByDoctorId(id);
return StatusCode(response.StatusCode, response);
}
} }
@@ -116,4 +116,50 @@ public class AppointmentManagementHandler
Data = null Data = null
}; };
} }
public async Task<BaseResponse> HandleGetAppointmentsByPatientId(Guid patientId)
{
var criteria = new List<(string FieldName, string Value)>
{
("PatientId", patientId.ToString())
};
var appointments = await _appointmentsMongoDbService.FindAsync<Appointment>(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<BaseResponse> HandleGetAppointmentsByDoctorId(Guid doctorId)
{
var criteria = new List<(string FieldName, string Value)>
{
("DoctorId", doctorId.ToString())
};
var appointments = await _appointmentsMongoDbService.FindAsync<Appointment>(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
};
}
} }
+1 -1
View File
@@ -10,7 +10,7 @@ public class Appointment
public string Id { get; private set; } public string Id { get; private set; }
public string PatientId { get; private set; } public string PatientId { get; private set; }
public string DoctorId { get; private set; } public string DoctorId { get; private set; }
public List<DateTime> AppointmentsList { get; } public List<DateTime> AppointmentsList { get; set; }
public void SetId(string id) public void SetId(string id)
{ {
@@ -6,6 +6,8 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/AuthLayout.css"/> <link rel="stylesheet" href="/AuthLayout.css"/>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</head> </head>
<body> <body>
<div class="background"></div> <div class="background"></div>
+33 -17
View File
@@ -1,28 +1,44 @@
@page "/dashboard/{Role}" @page "/dashboard/{Role}"
@using HealthcareManagerUiWebAssem.Services.User
@layout AuthLayout
@inject UserService UserService
<head> <head>
<title>Dashboard</title> <title>Dashboard</title>
</head> </head>
<link rel="stylesheet" href="/bootstrap/dist/css/bootstrap.min.css"/> <link rel="stylesheet" href="/bootstrap/dist/css/bootstrap.min.css"/>
<h3>login success</h3> <link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.22.4/dist/bootstrap-table.min.css">
<h1>@displayMessage</h1> <link href="https://cdn.datatables.net/2.0.4/css/dataTables.bootstrap5.css" rel="stylesheet">
<div class="position-relative"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<NavLink class="btn btn-primary m-2 position-absolute top-0 end-0" href="@($"/my-profile/{Role}")">My Profile</NavLink>
</div>
@code {
[Parameter] public string Role { get; set; } <style>
private string displayMessage; .select,
#locale {
protected override void OnInitialized() width: 100%;
{ }
var userId = UserService.UserId; .like {
displayMessage = $"Login success for {Role} with ID: {userId}"; margin-right: 10px;
} }
.modal-backdrop.show {
display: none !important;
}
</style>
<NavLink class="btn btn-primary m-2 position-absolute top-0 end-0" href="@($"/my-profile/{Role}")">My Profile</NavLink>
@switch (Role)
{
case "doctor":
<DoctorDashboard/>
break;
case "patient":
<PatientDashboard/>
break;
default:
<p>Unknown role. Please contact support.</p>
break;
}
@code {
[Parameter] public string Role { get; set; }
} }
@@ -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
<div class="container mt-3">
<h2 class="landingTitle">Doctor Menu</h2>
<p class="landingSubtitle">Here you can manage appointments, review your patients' medical history and chat with your patients.</p>
<div id="accordion">
<div class="card">
<div class="card-header">
<a class="btn" data-bs-toggle="collapse" href="#collapseOne">
Appointments
</a>
</div>
<div id="collapseOne" class="collapse show" data-bs-parent="#accordion">
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>Patient Name</th>
<th>Appointment Time</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
@foreach (var appointment in appointments)
{
@foreach (var date in appointment.AppointmentsList)
{
<tr>
<td>@GetPatientName(new Guid(appointment.PatientId))</td>
<td>@date.ToString("yyyy-MM-dd HH:mm")</td>
<td><button class="btn btn-danger" @onclick="(() => DeleteAppointment(appointment))">Delete</button></td>
</tr>
}
}
</tbody>
</table>
<AppointmentTableComponent appointments="appointments" OnAppointmentDeleted="HandleAppointmentDeleted" />
</div>
</div>
</div>
<!-- ... other patient-specific cards ... -->
</div>
</div>
@code {
private List<Patient> patients;
private string selectedDoctorId;
private DateTime? selectedDate;
private List<Appointment> appointments;
private List<byte[]> 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<List<Appointment>>(response.Data, jsonOptions) ?? new List<Appointment>();
}
else
{
appointments = new List<Appointment>();
}
}
private async Task InitializePatients()
{
var response = await ProfileService.GetPatients();
var jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
if (response.StatusCode == HttpStatusCodes.OK)
{
patients = JsonSerializer.Deserialize<List<Patient>>(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;
}
}
@@ -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
<div class="container mt-3">
<h2 class="landingTitle">Patient Menu</h2>
<p class="landingSubtitle">Here you can manage appointments, review your medical history and chat with doctors.</p>
<div id="accordion">
<div class="card">
<div class="card-header">
<a class="btn" data-bs-toggle="collapse" href="#collapseOne">
Appointments
</a>
</div>
<div id="collapseOne" class="collapse show" data-bs-parent="#accordion">
<div class="card-body">
<button @onclick="ShowAddAppointmentModal" class="btn btn-primary">Add Appointment</button>
<table class="table">
<thead>
<tr>
<th>Doctor Name</th>
<th>Appointment Time</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
@foreach (var appointment in appointments)
{
@foreach (var date in appointment.AppointmentsList)
{
<tr>
<td>@GetDoctorName(new Guid(appointment.DoctorId))</td>
<td>@date.ToString("yyyy-MM-dd HH:mm")</td>
<td><button class="btn btn-danger" @onclick="(() => DeleteAppointment(appointment))">Delete</button></td>
</tr>
}
}
</tbody>
</table>
<AppointmentTableComponent appointments="appointments" OnAppointmentDeleted="HandleAppointmentDeleted" />
</div>
</div>
</div>
<!-- ... other patient-specific cards ... -->
</div>
<div class="modal" id="addAppointmentModal" tabindex="-1" aria-labelledby="addAppointmentModalLabel" data-backdrop="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addAppointmentModalLabel">Add Appointment</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
@if (doctors != null)
{
<select @bind="selectedDoctorId" class="form-select">
<option value="">Select a doctor</option>
@foreach (var doctor in doctors)
{
<option value="@doctor.Id">@doctor.Name</option>
}
</select>
<input type="datetime-local" @bind="selectedDate" class="form-control" />
}
else
{
<p>Loading doctors...</p>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button @onclick="CreateAppointment" type="button" class="btn btn-primary">Create Appointment</button>
</div>
</div>
</div>
</div>
</div>
@code {
private List<Doctor> doctors;
private string selectedDoctorId;
private DateTime? selectedDate;
private List<Appointment> appointments;
private List<byte[]> 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<List<Appointment>>(response.Data, jsonOptions) ?? new List<Appointment>();
}
else
{
appointments = new List<Appointment>();
}
}
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<List<Doctor>>(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;
}
}
@@ -17,14 +17,6 @@
<Folder Include="Components\" /> <Folder Include="Components\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<AdditionalFiles Include="Components\Layout\MainLayout.razor" />
<AdditionalFiles Include="Components\Layout\NavMenu.razor" />
<AdditionalFiles Include="Components\Pages\Counter.razor" />
<AdditionalFiles Include="Components\Pages\Home.razor" />
<AdditionalFiles Include="Components\Pages\Weather.razor" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<_ContentIncludedByDefault Remove="Layout\AuthLayout.razor" /> <_ContentIncludedByDefault Remove="Layout\AuthLayout.razor" />
<_ContentIncludedByDefault Remove="Layout\MainLayout.razor" /> <_ContentIncludedByDefault Remove="Layout\MainLayout.razor" />
+9
View File
@@ -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<DateTime> AppointmentsList { get; set; } = [];
}
@@ -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; }
}
+2
View File
@@ -2,6 +2,7 @@ using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using HealthcareManagerUiWebAssem; using HealthcareManagerUiWebAssem;
using HealthcareManagerUiWebAssem.Services.Appointment;
using HealthcareManagerUiWebAssem.Services.Authentication; using HealthcareManagerUiWebAssem.Services.Authentication;
using HealthcareManagerUiWebAssem.Services.Http; using HealthcareManagerUiWebAssem.Services.Http;
using HealthcareManagerUiWebAssem.Services.Profile; using HealthcareManagerUiWebAssem.Services.Profile;
@@ -22,5 +23,6 @@ builder.Services.AddScoped<IRequestHttpService, RequestHttpService>();
builder.Services.AddScoped<UserService>(); builder.Services.AddScoped<UserService>();
builder.Services.AddScoped<IAuthenticationService, AuthenticationService>(); builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();
builder.Services.AddScoped<IProfileService, ProfileService>(); builder.Services.AddScoped<IProfileService, ProfileService>();
builder.Services.AddScoped<IAppointmentService, AppointmentService>();
await builder.Build().RunAsync(); await builder.Build().RunAsync();
@@ -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<BaseResponse> 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<BaseResponse> 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<BaseResponse> 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<BaseResponse> 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
};
}
}
}
@@ -0,0 +1,14 @@
using HealthcareManagerUiWebAssem.Models;
namespace HealthcareManagerUiWebAssem.Services.Appointment;
public interface IAppointmentService
{
Task<BaseResponse> GetAppointmentsByPatient(Guid patientId);
Task<BaseResponse> GetAppointmentsByDoctor(Guid doctorId);
Task<BaseResponse> CreateAppointment(AppointmentManagementModel model);
Task<BaseResponse> DeleteAppointment(AppointmentManagementModel model);
}
@@ -52,7 +52,7 @@ public class AuthenticationService : IAuthenticationService
{ {
try try
{ {
var response = await _requestHttpService.PostAsync("/Doctors/register", userRegistrationModel); var response = await _requestHttpService.PostAsync("/Patients/register", userRegistrationModel);
return response; return response;
} }
catch (Exception ex) catch (Exception ex)
@@ -12,4 +12,6 @@ public interface IRequestHttpService
Task<BaseResponse> PutAsync(string uri, object data, IDictionary<string, string> headers = null); Task<BaseResponse> PutAsync(string uri, object data, IDictionary<string, string> headers = null);
Task<BaseResponse> DeleteAsync(string uri, Guid id, IDictionary<string, string> headers = null); Task<BaseResponse> DeleteAsync(string uri, Guid id, IDictionary<string, string> headers = null);
Task<BaseResponse> DeleteByFilterAsync(string uri, object data, IDictionary<string, string> headers = null);
} }
@@ -79,6 +79,18 @@ public class RequestHttpService : IRequestHttpService
Thread.Sleep(1000); Thread.Sleep(1000);
return await HandleResponse(response, uri); return await HandleResponse(response, uri);
} }
public async Task<BaseResponse> DeleteByFilterAsync(string uri, object data, IDictionary<string, string>? headers = null)
{
headers ??= new Dictionary<string, string>();
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<string, string>? headers) private async void AddHeaders(HttpRequestMessage request, IDictionary<string, string>? headers)
{ {
+7 -3
View File
@@ -1,4 +1,4 @@
body, html { body, html {
height: 100%; height: 100%;
width: 100%; width: 100%;
margin: 0; margin: 0;
@@ -12,11 +12,11 @@ body {
font-style: normal; font-style: normal;
position: relative; position: relative;
z-index: 0; /* Ensures the background is under the content */ z-index: 0; /* Ensures the background is under the content */
overflow: scroll !important;
} }
.background { .background {
position: absolute; position: fixed;
top: -5%; top: -5%;
left: -5%; left: -5%;
width: 110%; width: 110%;
@@ -68,4 +68,8 @@ body {
display: flex; display: flex;
flex-direction: row; /* This will align the buttons side by side */ flex-direction: row; /* This will align the buttons side by side */
justify-content: center; /* Center the buttons within the container */ justify-content: center; /* Center the buttons within the container */
}
.modal-backdrop.show {
display: none !important;
} }