finalizare 1.0

This commit is contained in:
andrei-mihnea-cerbu
2024-05-21 12:10:53 +03:00
parent f7795f7519
commit 1cc1d34003
11268 changed files with 2102399 additions and 10909 deletions
@@ -0,0 +1,183 @@
@page "/patient/appointments"
@attribute [Authorize(Roles = UserRoles.Patient)]
@layout PatientLayout
@inject IPatientManagementService PatientManagementService;
@inject IAuthenticationService AuthenticationService;
<h3>Doctors</h3>
@if (!string.IsNullOrEmpty(_statusMessage))
{
<div class="alert @(_isError ? "alert-danger" : "alert-success")" role="alert">
@_statusMessage
</div>
}
<div class="card-deck">
@if (_doctors == null || !_doctors.Any())
{
<div class="alert alert-info center-content" style="width: 100%; height: 200px;">No doctors available.</div>
}
else
{
@foreach (var doctor in _doctors)
{
<div class="card doctor-card">
<div class="doctor-card-content">
<div class="user-icon"></div>
<div class="doctor-info">
<h5 class="card-title">Dr. @doctor.Name</h5>
<p class="card-text">@doctor.Email</p>
<button class="btn btn-primary" @onclick="() => ShowAppointmentOverlay(doctor)">Book Appointment</button>
</div>
</div>
</div>
}
}
</div>
@if (_showAppointmentOverlay)
{
<div class="overlay">
<div class="overlay-content">
<h5>Book Appointment with Dr. @_selectedDoctor.Name</h5>
<EditForm Model="@_dateAndTimeAppointment" OnValidSubmit="BookAppointment">
<div class="form-group">
<label for="date">Date</label>
<InputDate id="date" class="form-control" @bind-Value="_dateAndTimeAppointment.AppointmentDate" TValue="DateTime"/>
</div>
<div class="form-group">
<label for="time">Time</label>
<InputText id="time" class="form-control" @bind-Value="_dateAndTimeAppointment.FormattedAppointmentTime"/>
</div>
<button type="submit" class="btn btn-primary">Book</button>
<button type="button" class="btn btn-secondary" @onclick="HideAppointmentOverlay">Cancel</button>
</EditForm>
</div>
</div>
}
<style>
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.overlay-content {
background: white;
padding: 20px;
border-radius: 10px;
width: 400px;
text-align: center;
}
.doctor-card {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin-bottom: 20px;
}
.doctor-card-content {
display: flex;
align-items: center;
padding: 20px;
}
.user-icon {
background-image: url('user.png'); /* Ensure the path is correct */
background-size: cover;
width: 100px;
height: 100px;
border-radius: 50%;
margin-right: 20px;
}
.doctor-info {
display: flex;
flex-direction: column;
align-items: flex-start;
}
</style>
@code {
private List<Doctor> _doctors;
private Doctor _selectedDoctor;
private bool _showAppointmentOverlay = false;
private NewAppointmentModel _newAppointmentModel = new();
private DateAndTimeAppointment _dateAndTimeAppointment = new();
private string _statusMessage = string.Empty;
private bool _isError = false;
protected override async Task OnInitializedAsync()
{
await LoadDoctors();
}
private async Task LoadDoctors()
{
_doctors = await PatientManagementService.GetAllDoctorsAsync();
}
private void ShowAppointmentOverlay(Doctor doctor)
{
_selectedDoctor = doctor;
_newAppointmentModel = new NewAppointmentModel
{
DoctorId = doctor.Id
};
_showAppointmentOverlay = true;
}
private void HideAppointmentOverlay()
{
_showAppointmentOverlay = false;
}
private async Task BookAppointment()
{
var userInfo = await AuthenticationService.GetUserInformation();
_newAppointmentModel.PatientId = userInfo.Id;
_newAppointmentModel.Appointment = _dateAndTimeAppointment.GetAppointment();
var appointment = await PatientManagementService.BookAppointmentAsync(_newAppointmentModel);
_statusMessage = appointment.Message;
_isError = appointment.StatusCode != 201;
if (!_isError)
{
_showAppointmentOverlay = false;
}
}
class DateAndTimeAppointment
{
public DateTime AppointmentDate { get; set; } = DateTime.Now.Date;
public string FormattedAppointmentTime = DateTime.Now.ToString("HH:mm");
public DateTime GetAppointment()
{
// Parse FormattedAppointmentTime to a TimeSpan
if (TimeSpan.TryParse(FormattedAppointmentTime, out var appointmentTime))
{
// Combine AppointmentDate with appointmentTime
return AppointmentDate.Add(appointmentTime);
}
throw new FormatException("FormattedAppointmentTime is not in the correct format.");
}
}
}
+128
View File
@@ -0,0 +1,128 @@
@page "/patient/chat"
@attribute [Authorize(Roles = UserRoles.Patient)]
@layout PatientLayout
@inject IPatientManagementService PatientManagementService
@inject IAuthenticationService AuthenticationService
<h3>Chat with Doctors</h3>
@if (!string.IsNullOrEmpty(statusMessage))
{
<div class="alert @(isSuccess ? "alert-success" : "alert-danger")" role="alert">
@statusMessage
</div>
}
<div class="row">
<div class="col-md-4">
<div class="list-group">
@foreach (var doctor in doctors)
{
<button class="list-group-item list-group-item-action" @onclick="() => OpenChat(doctor.Id, doctor.Name)">
Dr. @doctor.Name
</button>
}
</div>
</div>
<div class="col-md-8">
@if (selectedDoctor != null)
{
<div class="card">
<div class="card-header">
Chat with Dr. @selectedDoctor.Name
<button class="btn btn-sm btn-danger float-right" @onclick="CloseChat">Close</button>
</div>
<div class="card-body chat-body">
@foreach (var message in messages)
{
<div class="chat-message @(message.UserId == userInfo.Id ? "chat-message-sender" : "chat-message-receiver")">
<strong>@(message.UserId == userInfo.Id ? "You" : selectedDoctor.Name):</strong> @message.Content
</div>
}
</div>
<div class="card-footer">
<input type="text" class="form-control" @bind="newMessage" @onkeydown="HandleKeyDown" placeholder="Type your message..."/>
<button class="btn btn-primary mt-2" @onclick="SendMessage">Send</button>
</div>
</div>
}
</div>
</div>
<style>
.chat-body {
height: 300px;
overflow-y: scroll;
}
.chat-message {
margin-bottom: 10px;
}
.chat-message-sender {
text-align: right;
}
.chat-message-receiver {
text-align: left;
}
</style>
@code {
private string statusMessage = string.Empty;
private bool isSuccess = false;
private List<Doctor> doctors = new();
private Doctor selectedDoctor;
private List<Message> messages = new();
private string newMessage = string.Empty;
private UserInformation userInfo;
protected override async Task OnInitializedAsync()
{
userInfo = await AuthenticationService.GetUserInformation();
doctors = await PatientManagementService.GetAllDoctorsAsync();
}
private async Task OpenChat(Guid doctorId, string doctorName)
{
selectedDoctor = new Doctor { Id = doctorId, Name = doctorName };
var chat = await PatientManagementService.GetConversationAsync(userInfo.Id, doctorId);
messages = chat?.Messages ?? new List<Message>();
StateHasChanged();
}
private void CloseChat()
{
selectedDoctor = null;
messages.Clear();
}
private async Task SendMessage()
{
if (!string.IsNullOrWhiteSpace(newMessage) && selectedDoctor != null)
{
var success = await PatientManagementService.SendMessageAsync(userInfo.Id, selectedDoctor.Id, newMessage);
if (success)
{
messages.Add(new Message(userInfo.Id, newMessage));
newMessage = string.Empty;
StateHasChanged();
}
else
{
statusMessage = "Failed to send message.";
isSuccess = false;
}
}
}
private void HandleKeyDown(KeyboardEventArgs e)
{
if (e.Key == "Enter")
{
SendMessage().GetAwaiter().GetResult();
}
}
}
@@ -0,0 +1,122 @@
@page "/patient/dashboard"
@attribute [Authorize(Roles = UserRoles.Patient)]
@layout PatientLayout
@inject IAuthenticationService AuthenticationService
@inject IPatientManagementService PatientManagementService
@inject NavigationManager NavigationManager
<h3>Dashboard</h3>
@if (errorMessage != string.Empty)
{
<div class="alert alert-danger" role="alert">
@errorMessage
</div>
}
@if (username != null)
{
<p>Welcome, @username!</p>
<p>Here you have listed your active appointments.</p>
}
@if (appointments != null && appointments.Count > 0)
{
<table class="table">
<thead>
<tr>
<th>Doctor</th>
<th>Time</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach (var appointment in appointments)
{
foreach (var appointmentTime in appointment.AppointmentsList)
{
<tr>
<td>Dr. @doctors[Guid.Parse(appointment.DoctorId)]</td>
<td>@appointmentTime.ToString("g")</td>
<td>
<button class="btn btn-danger" @onclick="() => DeleteAppointment(appointment, appointmentTime)">Delete</button>
</td>
</tr>
}
}
</tbody>
</table>
}
else if (errorMessage == string.Empty)
{
<p>No appointments exist.</p>
}
@code {
private string username;
private List<Appointment> appointments = new();
private Dictionary<Guid, string> doctors = new();
private string errorMessage = string.Empty;
protected override async Task OnInitializedAsync()
{
try
{
var userInfo = await AuthenticationService.GetUserInformation();
username = userInfo.Name;
appointments = await PatientManagementService.GetAppointmentsAsync(userInfo.Id);
var allDoctors = await PatientManagementService.GetAllDoctorsAsync();
doctors = allDoctors.ToDictionary(doc => doc.Id, doc => doc.Name);
if (!appointments.Any())
{
errorMessage = "No appointments exist.";
}
}
catch (Exception ex)
{
errorMessage = $"Failed to load data: {ex.Message}";
}
}
private async Task DeleteAppointment(Appointment appointment, DateTime appointmentTime)
{
try
{
var success = await PatientManagementService.CancelAppointmentAsync(
Guid.Parse(appointment.DoctorId),
Guid.Parse(appointment.PatientId),
appointmentTime
);
if (success)
{
var appList = appointments.FirstOrDefault(a => a.Id == appointment.Id)?.AppointmentsList;
if (appList != null)
{
appList.Remove(appointmentTime);
if (!appList.Any())
{
appointments.Remove(appointment);
}
}
if (!appointments.Any())
{
errorMessage = "No appointments exist.";
}
}
else
{
errorMessage = "Failed to delete the appointment.";
}
StateHasChanged();
}
catch (Exception ex)
{
errorMessage = $"Error deleting appointment: {ex.Message}";
}
}
}
@@ -0,0 +1,137 @@
@page "/patient/doctor_access"
@attribute [Authorize(Roles = UserRoles.Patient)]
@layout PatientLayout
@inject IPatientManagementService PatientManagementService
@inject IAuthenticationService AuthenticationService
@inject NavigationManager NavigationManager
<h3>Manage Access to Medical History</h3>
@if (!string.IsNullOrEmpty(statusMessage))
{
<div class="alert @(isSuccess ? "alert-success" : "alert-danger")" role="alert">
@statusMessage
</div>
}
<div class="row mt-4">
@foreach (var doctor in doctors)
{
<div class="col-md-4 mb-4">
<div class="card">
<div class="card-body text-center">
<div>
<strong>Status: </strong>
@if (doctorHasAccess(doctor.Id))
{
<span class="text-success">Granted</span>
}
else
{
<span class="text-danger">Revoked</span>
}
</div>
<img src="user.png" style="width: 70px" alt="Doctor" class="img-fluid mt-3"/>
<h5 class="mt-3">Dr. @doctor.Name</h5>
<button class="btn btn-success mt-2" @onclick="() => GrantAccess(doctor.Id)">Grant Access</button>
<button class="btn btn-warning mt-2" @onclick="() => RevokeAccess(doctor.Id)">Revoke Access</button>
</div>
</div>
</div>
}
</div>
@code {
private string statusMessage;
private bool isSuccess;
private List<Doctor> doctors = new();
private List<Guid> grantedAccessDoctors = new();
private Guid medicalRecordId;
protected override async Task OnInitializedAsync()
{
await LoadDoctorsAndAccessStatus();
}
private async Task LoadDoctorsAndAccessStatus()
{
var userInfo = await AuthenticationService.GetUserInformation();
var medicalHistory = await PatientManagementService.GetMedicalHistoryAsync(userInfo.Id);
if (medicalHistory != null)
{
medicalRecordId = medicalHistory.Id;
doctors = await PatientManagementService.GetAllDoctorsAsync();
await UpdateDoctorAccessStatuses(userInfo.Id);
}
else
{
statusMessage = "Failed to load medical history.";
isSuccess = false;
}
}
private async Task UpdateDoctorAccessStatuses(Guid patientId)
{
if (medicalRecordId != Guid.Empty)
{
foreach (var doctor in doctors)
{
if (await PatientManagementService.CheckForAccessAsync(medicalRecordId, doctor.Id))
{
grantedAccessDoctors.Add(doctor.Id);
}
}
StateHasChanged();
}
}
private bool doctorHasAccess(Guid doctorId)
{
return grantedAccessDoctors.Contains(doctorId);
}
private async Task GrantAccess(Guid doctorId)
{
if (medicalRecordId != Guid.Empty)
{
var success = await PatientManagementService.GrantAccessAsync(medicalRecordId, doctorId);
if (success)
{
grantedAccessDoctors.Add(doctorId);
statusMessage = "Access granted successfully.";
isSuccess = true;
}
else
{
statusMessage = "Failed to grant access.";
isSuccess = false;
}
StateHasChanged();
}
}
private async Task RevokeAccess(Guid doctorId)
{
if (medicalRecordId != Guid.Empty)
{
var success = await PatientManagementService.RevokeAccessAsync(medicalRecordId, doctorId);
if (success)
{
grantedAccessDoctors.Remove(doctorId);
statusMessage = "Access revoked successfully.";
isSuccess = true;
}
else
{
statusMessage = "Failed to revoke access.";
isSuccess = false;
}
StateHasChanged();
}
}
}
@@ -0,0 +1,74 @@
@page "/patient/doctors"
@attribute [Authorize(Roles = UserRoles.Patient)]
@layout PatientLayout
@inject IPatientManagementService PatientManagementService
<h3 class="text-center mb-4">Meet Our Doctors</h3>
@if (doctors is null)
{
<p class="text-center"><em>Loading doctors...</em></p>
}
else if (doctors.Count == 0)
{
<p class="text-center">No doctors are available at the moment.</p>
}
else
{
<div class="row">
@foreach (var doctor in doctors)
{
<div class="col-md-4 mb-4">
<div class="card h-100" @onclick="() => ToggleOverlay(doctor)">
<div class="card-body text-center">
<img src="user.png" alt="Doctor Image" class="img-fluid rounded-circle mb-2" style="width: 70px; height: 70px;">
<h5 class="card-title">@doctor.Name</h5>
<p class="card-text"><small class="text-muted">@doctor.Email</small></p>
</div>
</div>
</div>
}
</div>
@if (selectedDoctor != null)
{
<div class="modal" tabindex="-1" style="display:block; background-color: rgba(0,0,0,0.5);">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Doctor Details</h5>
<button type="button" class="close" @onclick="() => ToggleOverlay(null)">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Name: @selectedDoctor.Name</p>
<p>Email: @selectedDoctor.Email</p>
<p>Description: @((MarkupString)FormatDescription(selectedDoctor.Description))</p>
</div>
</div>
</div>
</div>
}
}
@code {
private List<Doctor> doctors;
private Doctor selectedDoctor;
protected override async Task OnInitializedAsync()
{
doctors = await PatientManagementService.GetAllDoctorsAsync();
}
private void ToggleOverlay(Doctor doctor)
{
selectedDoctor = doctor;
}
private string FormatDescription(string description)
{
// This is now handled directly in the modal body with MarkupString
return description?.Replace(Environment.NewLine, "<br />") ?? string.Empty;
}
}
@@ -0,0 +1,143 @@
@page "/patient/medical_history"
@attribute [Authorize(Roles = UserRoles.Patient)]
@layout PatientLayout
@inject IPatientManagementService PatientManagementService
@inject IAuthenticationService AuthenticationService
@inject NavigationManager NavigationManager
@inject IJSRuntime JSRuntime
<script src="fileHelper.js"></script>
<script src="downloadFile.js"></script>
<h3>Medical History</h3>
@if (!string.IsNullOrEmpty(statusMessage))
{
<div class="alert @(isSuccess ? "alert-success" : "alert-danger")" role="alert">
@statusMessage
</div>
}
@if (uploadedFile != null)
{
<div class="card mt-4">
<div class="card-header">
Medical History
</div>
<div class="card-body">
<img src="pngtree-pdf-file-icon-png-png-image_4899509.png" style="width:100px" alt="Medical Document" id="medicalDocument"/>
<div class="mt-4">
<button class="btn btn-primary" @onclick="PromptFileUpdate">Update</button>
<button class="btn btn-danger" @onclick="DeleteFile">Delete</button>
<button class="btn btn-secondary" @onclick="DownloadFile">Download</button>
</div>
</div>
</div>
}
else
{
<div class="card mt-4">
<div class="card-header">
Upload Medical History
</div>
<div class="card-body">
<InputFile OnChange="HandleFileSelected"/>
</div>
</div>
}
@code {
private MedicalHistory uploadedFile;
private string statusMessage;
private bool isSuccess;
protected override async Task OnInitializedAsync()
{
await CheckMedicalHistory();
}
private async Task CheckMedicalHistory()
{
var userInfo = await AuthenticationService.GetUserInformation();
uploadedFile = await PatientManagementService.GetMedicalHistoryAsync(userInfo.Id);
StateHasChanged();
}
private async Task HandleFileSelected(InputFileChangeEventArgs e)
{
var file = e.File;
if (file == null)
{
statusMessage = "No file selected.";
isSuccess = false;
StateHasChanged();
return;
}
// Check if the file MIME type is 'application/pdf'
if (!file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase))
{
statusMessage = "Only PDF files are allowed.";
isSuccess = false;
StateHasChanged();
return;
}
using var memoryStream = new MemoryStream();
await file.OpenReadStream().CopyToAsync(memoryStream);
var fileContent = memoryStream.ToArray();
var userInfo = await AuthenticationService.GetUserInformation();
var response = await PatientManagementService.UploadMedicalHistoryAsync(userInfo.Id, fileContent);
statusMessage = response.Message;
isSuccess = response.StatusCode == HttpStatusCodes.Created; // Assume HttpStatusCodes.Created represents successful upload
await CheckMedicalHistory();
StateHasChanged();
}
private void PromptFileUpdate()
{
JSRuntime.InvokeVoidAsync("document.getElementById", "hiddenFileInput");
}
private async Task DeleteFile()
{
if (uploadedFile != null)
{
var success = await PatientManagementService.DeleteMedicalHistoryAsync(uploadedFile.Id);
if (success)
{
uploadedFile = null;
statusMessage = "File deleted successfully.";
isSuccess = true;
StateHasChanged();
}
else
{
statusMessage = "Failed to delete file.";
isSuccess = false;
}
}
}
private async Task DownloadFile()
{
if (uploadedFile != null)
{
var fileContent = await PatientManagementService.DownloadMedicalHistoryAsync(uploadedFile.Id);
if (fileContent != null)
{
var base64 = Convert.ToBase64String(fileContent);
var href = $"data:application/octet-stream;base64,{base64}";
await JSRuntime.InvokeVoidAsync("downloadFile", "MedicalHistory.pdf", href);
}
}
}
}
@@ -0,0 +1,92 @@
@page "/patient/profile"
@attribute [Authorize(Roles = UserRoles.Patient)]
@layout PatientLayout
@inject NavigationManager Navigation
@inject IPatientManagementService PatientManagementService
@inject IAuthenticationService AuthenticationService
<h3>Profile</h3>
@if (!string.IsNullOrEmpty(updateMessage))
{
<div class="alert alert-danger mt-3">@updateMessage</div>
}
<div class="card mt-4">
<div class="card-header">
Update Profile
</div>
<div class="card-body">
<EditForm Model="profileModel" OnValidSubmit="UpdateProfile">
<div class="form-group">
<label for="name">Name</label>
<InputText id="name" class="form-control" @bind-Value="profileModel.Name"/>
</div>
<div class="form-group">
<label for="email">Email</label>
<InputText id="email" class="form-control" @bind-Value="profileModel.Email"/>
</div>
<div class="form-group">
<label for="password">Password</label>
<InputText id="password" type="password" class="form-control" @bind-Value="profileModel.Password"/>
</div>
<button type="submit" class="btn btn-primary">Save Changes</button>
</EditForm>
</div>
</div>
<div class="mt-4">
<button class="btn btn-danger" @onclick="DeleteAccount">Delete Account</button>
@if (!string.IsNullOrEmpty(deleteMessage))
{
<div class="alert alert-danger mt-3">@deleteMessage</div>
}
</div>
@code {
private Patient profileModel = new();
private string updateMessage = string.Empty;
private string deleteMessage = string.Empty;
protected override async Task OnInitializedAsync()
{
var userInfo = await AuthenticationService.GetUserInformation();
if (userInfo != null)
{
profileModel = await PatientManagementService.GetPatientProfileAsync(userInfo.Id);
}
}
private async Task UpdateProfile()
{
updateMessage = string.Empty;
var success = await PatientManagementService.UpdatePatientProfileAsync(profileModel);
if (success)
{
Navigation.NavigateTo("/dashboard/patient", true);
}
else
{
updateMessage = "An error occurred while updating your profile. Please try again.";
}
}
private async Task DeleteAccount()
{
deleteMessage = string.Empty;
var success = await PatientManagementService.DeletePatientProfileAsync(profileModel.Id);
if (success)
{
await AuthenticationService.RemoveAuthToken();
Navigation.NavigateTo("/goodbye");
}
else
{
deleteMessage = "An error occurred while deleting your account. Please try again.";
}
}
}