finalizare 1.0
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="HealthcareManager@90.84.229.219" uuid="1483d760-7f0d-4e68-8949-b3bef04f73e7">
|
||||
<driver-ref>postgresql</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<configured-by-url>true</configured-by-url>
|
||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:postgresql://90.84.229.219:5432/HealthcareManager?password=postgres&user=andrei_cerbu</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<file url="PROJECT" libraries="{bootstrap}" />
|
||||
</component>
|
||||
</project>
|
||||
+35
-12
@@ -1,12 +1,35 @@
|
||||
<Router AppAssembly="@typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(AuthLayout)" />
|
||||
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||
</Found>
|
||||
<NotFound>
|
||||
<PageTitle>Not found</PageTitle>
|
||||
<LayoutView Layout="@typeof(AuthLayout)">
|
||||
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
<CascadingAuthenticationState>
|
||||
<RoleBasedRedirect/>
|
||||
<Router AppAssembly="@typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
|
||||
<Authorizing>
|
||||
<p>Loading...</p>
|
||||
</Authorizing>
|
||||
<NotAuthorized>
|
||||
<NotAuthorizedHandler/>
|
||||
</NotAuthorized>
|
||||
</AuthorizeRouteView>
|
||||
</Found>
|
||||
<NotFound >
|
||||
<LayoutView Layout="@typeof(MainLayout)">
|
||||
<PageNotFoundHandler/>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
</CascadingAuthenticationState>
|
||||
|
||||
|
||||
@code {
|
||||
|
||||
private class RedirectToLogin : ComponentBase
|
||||
{
|
||||
[Inject] private NavigationManager Navigation { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Navigation.NavigateTo("/login");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,15 @@
|
||||
|
||||
public class ApplicationSettings
|
||||
{
|
||||
public string ApiKey { get; set; }
|
||||
public string ApiEndpoint { get; set; }
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
public JwtSettings Jwt { get; set; }
|
||||
public string BaseAddress { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class JwtSettings
|
||||
{
|
||||
public string SecretKey { get; set; } = string.Empty;
|
||||
public string Issuer { get; set; } = string.Empty;
|
||||
public string Audience { get; set; } = string.Empty;
|
||||
public int ExpirationTime { get; set; } = 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Net.Http.Headers;
|
||||
using Blazored.LocalStorage;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem;
|
||||
|
||||
public class AuthenticationHttpMessageHandler(ILocalStorageService localStorageService, ApplicationSettings apiSettings)
|
||||
: DelegatingHandler
|
||||
{
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// Add API key to request headers
|
||||
request.Headers.Add("ApiKey", apiSettings.ApiKey);
|
||||
|
||||
// Retrieve JWT token from local storage
|
||||
var token = await localStorageService.GetItemAsync<string>("authToken", cancellationToken);
|
||||
if (!string.IsNullOrEmpty(token))
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
@namespace HealthcareManagerUiWebAssem.Components
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="/">MyApp</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/dashboard">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/users">Users</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/settings">Settings</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<button class="nav-link btn btn-link" @onclick="Logout">Logout</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
@code {
|
||||
|
||||
private async Task Logout()
|
||||
{
|
||||
await AuthenticationService.Logout();
|
||||
// Redirect the user to the login page or somewhere relevant
|
||||
NavigationManager.NavigateTo("/login", true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
@namespace HealthcareManagerUiWebAssem.Components
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="/">MyApp</a>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/login">Login</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/register">Register</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/reset-password">Reset Password</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -0,0 +1,45 @@
|
||||
@namespace HealthcareManagerUiWebAssem.Components
|
||||
|
||||
<div class="sidebar">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="/">MyApp</a>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/doctor/dashboard">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/doctor/profile">Profile</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/doctor/patients_medical_history">Patients' Medical History</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/doctor/ai">Sickness Software</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/doctor/chat">Chat</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<button class="nav-link btn btn-link" @onclick="Logout">Logout</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
@code {
|
||||
|
||||
private async Task Logout()
|
||||
{
|
||||
await AuthenticationService.Logout();
|
||||
// Redirect the user to the login page or somewhere relevant
|
||||
NavigationManager.NavigateTo("/login", true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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>
|
||||
<body>
|
||||
<div class="background"></div>
|
||||
<div class="container">
|
||||
@Body
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,11 +0,0 @@
|
||||
@if (!string.IsNullOrEmpty(ErrorMessage))
|
||||
{
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<strong>Error:</strong> @ErrorMessage
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public string ErrorMessage { get; set; }
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
@page "/"
|
||||
|
||||
@layout AuthLayout
|
||||
<head>
|
||||
<title>Choose Role</title>
|
||||
</head>
|
||||
<link rel="stylesheet" href="bootstrap/dist/css/bootstrap.min.css"/>
|
||||
<div class="text-center mt-5 centered-menu">
|
||||
<h1 class="landingTitle">Welcome to Healthcare Manager</h1>
|
||||
<p class="landingSubtitle">Please select your role:</p>
|
||||
<div class="button-container">
|
||||
<NavLink class="btn btn-primary m-2" href="/login/doctor">I'm a Doctor</NavLink>
|
||||
<NavLink class="btn btn-secondary m-2" href="/login/patient">I'm a Patient</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,44 +0,0 @@
|
||||
@page "/dashboard/{Role}"
|
||||
|
||||
<head>
|
||||
<title>Dashboard</title>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/bootstrap/dist/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.22.4/dist/bootstrap-table.min.css">
|
||||
<link href="https://cdn.datatables.net/2.0.4/css/dataTables.bootstrap5.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<style>
|
||||
.select,
|
||||
#locale {
|
||||
width: 100%;
|
||||
}
|
||||
.like {
|
||||
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; }
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
@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;
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
@page "/login/{role}"
|
||||
@using System.Text.Json
|
||||
@using HealthcareManagerUiWebAssem.Models
|
||||
@using HealthcareManagerUiWebAssem.Services.Authentication
|
||||
@using HealthcareManagerUiWebAssem.Services.User
|
||||
@using HealthcareManagerUiWebAssem.Services.UserSessionInformation
|
||||
|
||||
@layout AuthLayout
|
||||
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
@inject IUserSessionInformation UserSessionInformation;
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject UserService UserService
|
||||
|
||||
|
||||
<head>
|
||||
<title>Login</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/LoginPage.css"/>
|
||||
<link rel="stylesheet" href="/bootstrap/dist/css/bootstrap.min.css"/>
|
||||
|
||||
<EditForm Model="userLoginModel" OnValidSubmit="@HandleLogin" FormName="LoginForm" class="login-form container mt-5">
|
||||
<DataAnnotationsValidator/>
|
||||
<ValidationSummary/>
|
||||
|
||||
<div class="card shadow-lg">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-center mb-3">Login</h3>
|
||||
<div class="form-group mb-3">
|
||||
<InputText id="email" class="form-control" placeholder="Email" @bind-Value="userLoginModel!.Email"></InputText>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<InputText id="password" class="form-control" type="password" placeholder="Password" @bind-Value="userLoginModel!.Password"></InputText>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100 mb-3">Log In</button>
|
||||
<div class="button-container">
|
||||
<NavLink class="btn btn-secondary m-2" href="@($"/register/{Role}")">Register</NavLink>
|
||||
<NavLink class="btn btn-secondary m-2" href="@($"/reset-password/{Role}")">Reset Password</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
<AlertMessage ErrorMessage="@errorMessage"/>
|
||||
|
||||
@code {
|
||||
[SupplyParameterFromForm] public UserLoginModel? userLoginModel { get; set; }
|
||||
private BaseResponse? loginResponse;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
userLoginModel ??= new UserLoginModel();
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public string Role { get; set; }
|
||||
private string errorMessage { get; set; }
|
||||
private bool loginSuccesful { get; set; }
|
||||
|
||||
private void ClearErrorMessage()
|
||||
{
|
||||
errorMessage = string.Empty;
|
||||
}
|
||||
|
||||
private async Task HandleLogin()
|
||||
{
|
||||
userLoginModel.UserType = Role;
|
||||
loginResponse = await AuthenticationService.Login(userLoginModel); // Store response instead of immediately processing
|
||||
if (loginResponse.StatusCode < 200 || loginResponse.StatusCode > 299)
|
||||
{
|
||||
errorMessage = loginResponse?.Message ?? "An error occurred."; // Handle error states immediately if required
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var userId = Guid.Empty;
|
||||
var authToken = "";
|
||||
Console.WriteLine("Printing headers");
|
||||
foreach (var (key, value) in loginResponse.Headers)
|
||||
{
|
||||
Console.WriteLine($"{key} => {value}");
|
||||
}
|
||||
|
||||
if (loginResponse.Headers.TryGetValue("Authorization", out var token))
|
||||
{
|
||||
authToken = token;
|
||||
}
|
||||
|
||||
if (Role.Equals("doctor", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var doctor = JsonSerializer.Deserialize<Doctor>(
|
||||
loginResponse.Data,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
await UserSessionInformation.SaveUserInformationAsync(doctor.Id, Role, doctor.Email, doctor.Name, authToken);
|
||||
userId = doctor.Id;
|
||||
}
|
||||
else if (Role.Equals("patient", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var patient = JsonSerializer.Deserialize<Patient>(
|
||||
loginResponse.Data,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
await UserSessionInformation.SaveUserInformationAsync(patient.Id, Role, patient.Email, patient.Name, authToken);
|
||||
userId = patient.Id;
|
||||
}
|
||||
|
||||
loginResponse = null;
|
||||
|
||||
UserService.SetUserId(userId);
|
||||
NavigationManager.NavigateTo($"/dashboard/{Role}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
@page "/my-profile/{Role}"
|
||||
@using HealthcareManagerUiWebAssem.Models
|
||||
@using HealthcareManagerUiWebAssem.Services.Authentication
|
||||
@using HealthcareManagerUiWebAssem.Services.Profile
|
||||
@using HealthcareManagerUiWebAssem.Services.User
|
||||
@using HealthcareManagerUiWebAssem.Services.UserSessionInformation
|
||||
|
||||
@layout AuthLayout
|
||||
@inject IUserSessionInformation UserSessionInformation;
|
||||
@inject IAuthenticationService AuthenticationService;
|
||||
@inject IProfileService ProfileService
|
||||
@inject UserService UserService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<head>
|
||||
<title>My Profile</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/LoginPage.css"/>
|
||||
<link rel="stylesheet" href="/bootstrap/dist/css/bootstrap.min.css"/>
|
||||
|
||||
<h3>My Profile</h3>
|
||||
@if (profile != null)
|
||||
{
|
||||
<EditForm Model="profile" OnValidSubmit="@HandleUpdateProfile" FormName="UpdateProfileForm" class="login-form container mt-5">
|
||||
<DataAnnotationsValidator/>
|
||||
<ValidationSummary/>
|
||||
|
||||
<div class="card shadow-lg">
|
||||
<div class="card-body">
|
||||
<div class="form-group mb-3">
|
||||
<label for="name">Name:</label>
|
||||
<InputText id="name" class="form-control" @bind-Value="@profile!.Name"></InputText>
|
||||
</div>
|
||||
|
||||
@if (Role.Equals("doctor", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
<div class="form-group mb-3">
|
||||
<label for="description">Description:</label>
|
||||
<InputText id="description" class="form-control" @bind-Value="@profile!.Description"></InputText>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="email">Email:</label>
|
||||
<InputText id="email" class="form-control" @bind-Value="@profile!.Email"></InputText>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="password">Password:</label>
|
||||
<InputText id="password" class="form-control" type="password" @bind-Value="@profile!.Password"></InputText>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Update</button>
|
||||
<button type="button" class="btn btn-danger" @onclick="@HandleDeleteProfile">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
|
||||
<AlertMessage ErrorMessage="@errorMessage"/>
|
||||
}
|
||||
|
||||
@code {
|
||||
|
||||
[SupplyParameterFromForm] public UserUpdateProfileModel? profile { get; set; }
|
||||
|
||||
[Parameter] public string Role { get; set; }
|
||||
private string errorMessage;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var tokenDto = new TokenRefreshModel(await UserSessionInformation.GetTokenAsync());
|
||||
var refreshResult = await AuthenticationService.RefreshToken(tokenDto);
|
||||
if (refreshResult.StatusCode < HttpStatusCodes.BadRequest)
|
||||
{
|
||||
NavigationManager.NavigateTo("/");
|
||||
}
|
||||
|
||||
profile ??= await UserService.InitializeProfile(Role, ProfileService);
|
||||
var userId = await UserSessionInformation.GetIdAsync();
|
||||
}
|
||||
|
||||
private async Task HandleUpdateProfile()
|
||||
{
|
||||
profile.Id = UserService.UserId;
|
||||
var response = Role switch
|
||||
{
|
||||
"doctor" => await ProfileService.UpdateDoctorProfile(profile),
|
||||
"patient" => await ProfileService.UpdatePatientProfile(profile),
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (response.StatusCode >= 200 && response.StatusCode <= 299)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/my-profile/{Role}");
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = response?.Message ?? "An error occurred.";
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleDeleteProfile()
|
||||
{
|
||||
var response = Role switch
|
||||
{
|
||||
"doctor" => await ProfileService.DeleteDoctorProfile(UserService.UserId),
|
||||
"patient" => await ProfileService.DeletePatientProfile(UserService.UserId),
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (response.StatusCode >= 200 && response.StatusCode <= 299)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/login/{Role}");
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = response?.Message ?? "An error occurred.";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
@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;
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
@page "/register/{role}"
|
||||
@using HealthcareManagerUiWebAssem.Models
|
||||
@using HealthcareManagerUiWebAssem.Services.Authentication
|
||||
|
||||
@layout AuthLayout
|
||||
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<head>
|
||||
<title>Register</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/LoginPage.css"/>
|
||||
<link rel="stylesheet" href="/bootstrap/dist/css/bootstrap.min.css"/>
|
||||
|
||||
<EditForm Model="userRegisterModel" OnValidSubmit="@HandleRegister" FormName="RegisterForm" class="login-form container mt-5">
|
||||
<DataAnnotationsValidator/>
|
||||
<ValidationSummary/>
|
||||
|
||||
<div class="card shadow-lg">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-center mb-3">Register</h3>
|
||||
<div class="form-group mb-3">
|
||||
<InputText id="name" class="form-control" placeholder="Name" @bind-Value="userRegisterModel!.Name"></InputText>
|
||||
</div>
|
||||
@if (Role.Equals("doctor", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
<div class="form-group mb-3">
|
||||
<InputText id="description" class="form-control" placeholder="Description" @bind-Value="userRegisterModel!.Description"></InputText>
|
||||
</div>
|
||||
}
|
||||
<div class="form-group mb-3">
|
||||
<InputText id="email" class="form-control" placeholder="Email" @bind-Value="userRegisterModel!.Email"></InputText>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<InputText id="password" class="form-control" type="password" placeholder="Password" @bind-Value="userRegisterModel!.Password"></InputText>
|
||||
</div>
|
||||
<div class="button-container">
|
||||
<button type="submit" class="btn btn-primary w-100 mb-3">Register</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
<AlertMessage ErrorMessage="@errorMessage"/>
|
||||
|
||||
@code {
|
||||
[SupplyParameterFromForm] public UserRegisterModel? userRegisterModel { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
userRegisterModel ??= new UserRegisterModel();
|
||||
}
|
||||
|
||||
[Parameter] public string Role { get; set; }
|
||||
private string errorMessage { get; set; }
|
||||
|
||||
private void ClearErrorMessage()
|
||||
{
|
||||
errorMessage = string.Empty;
|
||||
}
|
||||
|
||||
private async Task HandleRegister()
|
||||
{
|
||||
var response = Role switch
|
||||
{
|
||||
"doctor" => await AuthenticationService.RegisterDoctor(userRegisterModel),
|
||||
"patient" => await AuthenticationService.RegisterPatient(userRegisterModel),
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (response.StatusCode >= 200 && response.StatusCode <= 399)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/login/{Role}");
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = response.Message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
@page "/reset-password/{role}"
|
||||
@using HealthcareManagerUiWebAssem.Models
|
||||
@using HealthcareManagerUiWebAssem.Services.Authentication
|
||||
|
||||
@layout AuthLayout
|
||||
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<head>
|
||||
<title>Reset your password</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</head>
|
||||
<link rel="stylesheet" href="/LoginPage.css"/>
|
||||
<link rel="stylesheet" href="/bootstrap/dist/css/bootstrap.min.css"/>
|
||||
|
||||
<EditForm Model="userResetPasswordModel" OnValidSubmit="@HandleResetPassword" FormName="ResetPasswordForm" class="login-form container mt-5">
|
||||
<DataAnnotationsValidator/>
|
||||
<ValidationSummary/>
|
||||
|
||||
<div class="card shadow-lg">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-center mb-3">Register</h3>
|
||||
<div class="form-group mb-3">
|
||||
<InputText id="email" class="form-control" placeholder="Email" @bind-Value="userResetPasswordModel!.Email"></InputText>
|
||||
</div>
|
||||
<div class="form-group mb-3">
|
||||
<InputText id="password" class="form-control" type="password" placeholder="New Password" @bind-Value="userResetPasswordModel!.Password"></InputText>
|
||||
</div>
|
||||
<div class="button-container">
|
||||
<button type="submit" class="btn btn-primary w-100 mb-3">Reset Password</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
<AlertMessage ErrorMessage="@errorMessage"/>
|
||||
|
||||
@code {
|
||||
[SupplyParameterFromForm] public UserLoginModel? userResetPasswordModel { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
userResetPasswordModel ??= new UserLoginModel();
|
||||
}
|
||||
|
||||
[Parameter] public string Role { get; set; }
|
||||
private string errorMessage { get; set; }
|
||||
|
||||
private void ClearErrorMessage()
|
||||
{
|
||||
errorMessage = string.Empty;
|
||||
}
|
||||
|
||||
private async Task HandleResetPassword()
|
||||
{
|
||||
userResetPasswordModel.UserType = Role;
|
||||
var response = await AuthenticationService.ResetPassword(userResetPasswordModel);
|
||||
|
||||
if (response.StatusCode >= 200 && response.StatusCode <= 399)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/login/{Role}");
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = response.Message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
@namespace HealthcareManagerUiWebAssem.Components
|
||||
|
||||
<div class="sidebar">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<a class="navbar-brand" href="/">MyApp</a>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/patient/dashboard">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/patient/doctors">Meet Our Doctors</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/patient/profile">Profile</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/patient/appointments">Appointments</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/patient/medical_history">Medical History</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/patient/doctor_access">Doctor Access</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/patient/chat">Chat</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<button class="nav-link btn btn-link" @onclick="Logout">Logout</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
@code {
|
||||
|
||||
private async Task Logout()
|
||||
{
|
||||
await AuthenticationService.Logout();
|
||||
// Redirect the user to the login page or somewhere relevant
|
||||
NavigationManager.NavigateTo("/login", true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Blazored.LocalStorage;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem;
|
||||
|
||||
public class CustomAuthenticationStateProvider(ILocalStorageService localStorageService) : AuthenticationStateProvider
|
||||
{
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
var token = await localStorageService.GetItemAsync<string>("authToken");
|
||||
|
||||
if (string.IsNullOrEmpty(token))
|
||||
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
|
||||
|
||||
var claims = ParseClaimsFromJwt(token);
|
||||
var identity = new ClaimsIdentity(claims, "jwt", null, "role");
|
||||
|
||||
var user = new ClaimsPrincipal(identity);
|
||||
|
||||
return new AuthenticationState(user);
|
||||
}
|
||||
|
||||
public void NotifyUserAuthentication(string token)
|
||||
{
|
||||
var claims = ParseClaimsFromJwt(token);
|
||||
var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt", null, "role"));
|
||||
var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
|
||||
|
||||
NotifyAuthenticationStateChanged(authState);
|
||||
}
|
||||
|
||||
public void NotifyUserLogout()
|
||||
{
|
||||
var authState = Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())));
|
||||
NotifyAuthenticationStateChanged(authState);
|
||||
}
|
||||
|
||||
private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
|
||||
{
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var token = handler.ReadJwtToken(jwt);
|
||||
return token.Claims;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace HealthcareManagerUiWebAssem.Entities;
|
||||
|
||||
public class Appointment
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string PatientId { get; set; } = string.Empty;
|
||||
public string DoctorId { get; set; } = string.Empty;
|
||||
public List<DateTime> AppointmentsList { get; set; } = [];
|
||||
|
||||
public void SetId(string id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public void SetPatientId(string patientId)
|
||||
{
|
||||
PatientId = patientId;
|
||||
}
|
||||
|
||||
public void SetDoctorIid(string doctorId)
|
||||
{
|
||||
DoctorId = doctorId;
|
||||
}
|
||||
|
||||
public void AddAppointment(DateTime appointmentDate)
|
||||
{
|
||||
var utcAppointmentDate = new DateTime(appointmentDate.Year, appointmentDate.Month, appointmentDate.Day, 0, 0, 0,
|
||||
DateTimeKind.Utc);
|
||||
AppointmentsList.Add(utcAppointmentDate);
|
||||
}
|
||||
|
||||
public void RemoveAppointment(DateTime appointmentDate)
|
||||
{
|
||||
var utcAppointmentDate = new DateTime(appointmentDate.Year, appointmentDate.Month, appointmentDate.Day, 0, 0, 0,
|
||||
DateTimeKind.Utc);
|
||||
AppointmentsList.Remove(utcAppointmentDate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace HealthcareManagerUiWebAssem.Entities;
|
||||
|
||||
public class Chat
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public List<Message> Messages { get; set; } = [];
|
||||
|
||||
public void SetId(string id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public void SetMessages(List<Message> messages)
|
||||
{
|
||||
Messages = messages;
|
||||
}
|
||||
}
|
||||
|
||||
public class Message(Guid userId, string content)
|
||||
{
|
||||
public Guid UserId { get; set; } = userId;
|
||||
public string Content { get; set; } = content;
|
||||
|
||||
public void SetUserId(Guid userId)
|
||||
{
|
||||
UserId = userId;
|
||||
}
|
||||
|
||||
public void SetContent(string content)
|
||||
{
|
||||
Content = content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace HealthcareManagerUiWebAssem.Entities;
|
||||
|
||||
public class Doctor
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Entities;
|
||||
|
||||
public class MedicalHistory
|
||||
{
|
||||
[Key] public Guid Id { get; set; } = Guid.NewGuid();
|
||||
public Guid UserId { get; set; } = Guid.Empty;
|
||||
public byte[] Content { get; set; } = [];
|
||||
|
||||
public void SetId(Guid id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public void SetUserId(Guid userId)
|
||||
{
|
||||
UserId = userId;
|
||||
}
|
||||
|
||||
public void SetContent(byte[] content)
|
||||
{
|
||||
Content = content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace HealthcareManagerUiWebAssem.Entities;
|
||||
|
||||
public class Patient
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -7,20 +7,21 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
|
||||
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0"/>
|
||||
<PackageReference Include="itext7" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.5"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.3"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.3" PrivateAssets="all"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0-preview.2.24128.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0-preview.2.24128.5"/>
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.5.1"/>
|
||||
<PackageReference Include="UglyToad.PdfPig" Version="1.7.0-custom-5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Components\" />
|
||||
<Folder Include="wwwroot\css\Pages\"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_ContentIncludedByDefault Remove="Layout\AuthLayout.razor" />
|
||||
<_ContentIncludedByDefault Remove="Layout\MainLayout.razor" />
|
||||
<_ContentIncludedByDefault Remove="Layout\NavMenu.razor" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\js\DownloadFile.js"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
@inherits LayoutComponentBase
|
||||
<link href="css/Layout/AdminLayout.css" rel="stylesheet"/>
|
||||
<AdminNavbar/>
|
||||
|
||||
<div class="page-container mt-5">
|
||||
<div class="container">
|
||||
@Body
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
@inherits LayoutComponentBase
|
||||
<link href="css/Layout/AuthLayout.css" rel="stylesheet"/>
|
||||
<AuthNavbar/>
|
||||
|
||||
<div class="page-container mt-5">
|
||||
<div class="container">
|
||||
@Body
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
@inherits LayoutComponentBase
|
||||
<link href="css/Layout/DoctorLayout.css" rel="stylesheet"/>
|
||||
<DoctorNavBar/>
|
||||
|
||||
<div class="page-container mt-5">
|
||||
<div class="container">
|
||||
@Body
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
@inherits LayoutComponentBase
|
||||
<link href="css/Layout/MainLayout.css" rel="stylesheet"/>
|
||||
|
||||
<div class="page-container mt-5">
|
||||
<div class="container">
|
||||
@Body
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
@inherits LayoutComponentBase
|
||||
<link href="css/Layout/PatientLayout.css" rel="stylesheet"/>
|
||||
<PatientNavbar/>
|
||||
|
||||
<div class="page-container mt-5">
|
||||
<div class="container">
|
||||
@Body
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class ApiResponse<T>
|
||||
{
|
||||
public int StatusCode { get; set; }
|
||||
public string Message { get; set; } = string.Empty;
|
||||
public T? Data { get; set; }
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
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; } = [];
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class AppointmentManagementModel
|
||||
public class AppointmentCancellationRequest
|
||||
{
|
||||
public Guid DoctorId { get; set; }
|
||||
public Guid PatientId { get; set; }
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class BaseResponse
|
||||
{
|
||||
public int StatusCode { get; set; }
|
||||
public string? Message { get; set; }
|
||||
public string? Data { get; set; }
|
||||
public IDictionary<string, string>? Headers { get; set; } // New field to store headers
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class Doctor
|
||||
{
|
||||
[Key] public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class NewAppointmentModel
|
||||
{
|
||||
public Guid DoctorId { get; set; } = Guid.Empty;
|
||||
public Guid PatientId { get; set; } = Guid.Empty;
|
||||
public DateTime Appointment { get; set; } = DateTime.Now;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class PatientRegisterModel
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class Patient
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class PredictionResult
|
||||
{
|
||||
public string Disease { get; set; } = string.Empty;
|
||||
public double Probability { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class ResetPasswordModel
|
||||
{
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class ResetPasswordResponse
|
||||
{
|
||||
public int StatusCode { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string? Data { get; set; }
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class TokenRefreshModel
|
||||
{
|
||||
public TokenRefreshModel(string token)
|
||||
{
|
||||
Token = token;
|
||||
}
|
||||
|
||||
public string Token { get; private set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class UserInformation
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public Guid Id { get; set; } = Guid.Empty;
|
||||
public string Role { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class UserLoginModel
|
||||
{
|
||||
public string? UserType { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
[Required] public string Email { get; set; } = string.Empty;
|
||||
[Required] public string Password { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class UserRegisterModel
|
||||
{
|
||||
public string? UserType { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string Description { get; set; }
|
||||
[Required] public string Name { get; set; } = string.Empty;
|
||||
[Required] public string Email { get; set; } = string.Empty;
|
||||
[Required] public string Password { get; set; } = string.Empty;
|
||||
[Required] public string Role { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
public class UserUpdateProfileModel
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
@page "/admin/dashboard"
|
||||
@layout AdminLayout
|
||||
@attribute [Authorize(Roles = UserRoles.Admin)]
|
||||
|
||||
<h3>Admin Dashboard</h3>
|
||||
<p>Welcome to the doctor dashboard!</p>
|
||||
@@ -0,0 +1,128 @@
|
||||
@page "/admin/users"
|
||||
@using Microsoft.JSInterop
|
||||
@inject IAdminManagementService AdminManagementService
|
||||
@inject IJSRuntime JsRuntime
|
||||
|
||||
@layout AdminLayout
|
||||
@attribute [Authorize(Roles = UserRoles.Admin)]
|
||||
|
||||
<h3>User Management Page</h3>
|
||||
|
||||
@if (_doctors == null || _patients == null)
|
||||
{
|
||||
<p>
|
||||
<em>Loading...</em>
|
||||
</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h4>Doctors</h4>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var doctor in _doctors)
|
||||
{
|
||||
<tr>
|
||||
<td>@doctor.Name</td>
|
||||
<td>@doctor.Email</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" @onclick="() => DeleteDoctor(doctor.Id)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Patients</h4>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var patient in _patients)
|
||||
{
|
||||
<tr>
|
||||
<td>@patient.Name</td>
|
||||
<td>@patient.Email</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" @onclick="() => DeletePatient(patient.Id)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Doctor> _doctors = [];
|
||||
private List<Patient> _patients = [];
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var doctorsResult = await AdminManagementService.GetDoctors();
|
||||
var patientsResult = await AdminManagementService.GetPatients();
|
||||
|
||||
_doctors = doctorsResult.Data;
|
||||
_patients = patientsResult.Data;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_errorMessage = $"Error loading data: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteDoctor(Guid id)
|
||||
{
|
||||
var confirmed = await JsRuntime.InvokeAsync<bool>("confirm", $"Are you sure you want to delete this doctor?");
|
||||
if (confirmed)
|
||||
{
|
||||
var success = await AdminManagementService.DeleteDoctor(id);
|
||||
if (success)
|
||||
{
|
||||
var doctorsResult = await AdminManagementService.GetDoctors();
|
||||
_doctors = doctorsResult.Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorMessage = "Error deleting doctor.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeletePatient(Guid id)
|
||||
{
|
||||
var confirmed = await JsRuntime.InvokeAsync<bool>("confirm", $"Are you sure you want to delete this patient?");
|
||||
if (confirmed)
|
||||
{
|
||||
var success = await AdminManagementService.DeletePatient(id);
|
||||
if (success)
|
||||
{
|
||||
var patientsResult = await AdminManagementService.GetPatients();
|
||||
_patients = patientsResult.Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorMessage = "Error deleting patient.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
@page "/goodbye"
|
||||
@layout AuthLayout
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<h3>Goodbye</h3>
|
||||
|
||||
<div class="card mt-4">
|
||||
<div class="card-body">
|
||||
<p>Thank you for using our service. We're sorry to see you go.</p>
|
||||
<button class="btn btn-primary" @onclick="RedirectToHome">Go to Home Page</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
private void RedirectToHome()
|
||||
{
|
||||
NavigationManager.NavigateTo("/");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
@page "/"
|
||||
@layout AuthLayout
|
||||
|
||||
<h1>Welcome to HealthcareManagerApp</h1>
|
||||
<p>Your Health, Our Priority.</p>
|
||||
|
||||
<p>At HealthcareManagerApp, we believe in providing the best care for our patients and ensuring seamless operations for our healthcare providers.</p>
|
||||
<p>Join us in our mission to make healthcare accessible and efficient for everyone.</p>
|
||||
|
||||
<p>
|
||||
<a class="btn btn-primary" href="/login">Login</a>
|
||||
<a class="btn btn-secondary" href="/register">Register</a>
|
||||
</p>
|
||||
@@ -0,0 +1,81 @@
|
||||
@page "/login"
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
@layout AuthLayout
|
||||
<h3>Login</h3>
|
||||
|
||||
@if (!string.IsNullOrEmpty(_errorMessage))
|
||||
{
|
||||
<div class="alert alert-danger mt-2">@_errorMessage</div>
|
||||
}
|
||||
|
||||
<EditForm Model="_loginModel" OnValidSubmit="HandleLogin">
|
||||
<div class="form-group">
|
||||
<label>Email:</label>
|
||||
<InputText class="form-control" @bind-Value="_loginModel.Email"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password:</label>
|
||||
<InputText class="form-control" @bind-Value="_loginModel.Password" type="password"/>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Login</button>
|
||||
</EditForm>
|
||||
|
||||
@code {
|
||||
private readonly UserLoginModel _loginModel = new();
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
private async Task HandleLogin()
|
||||
{
|
||||
_errorMessage = string.Empty;
|
||||
var response = await AuthenticationService.Login(_loginModel);
|
||||
|
||||
if (response.StatusCode == 200)
|
||||
{
|
||||
var token = await AuthenticationService.GetAuthToken();
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jwtToken = handler.ReadJwtToken(token);
|
||||
var role = jwtToken.Claims.FirstOrDefault(c => c.Type == "role")?.Value;
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case UserRoles.Admin:
|
||||
Navigation.NavigateTo("/admin/dashboard", true);
|
||||
break;
|
||||
case UserRoles.Doctor:
|
||||
Navigation.NavigateTo("/doctor/dashboard", true);
|
||||
break;
|
||||
case UserRoles.Patient:
|
||||
Navigation.NavigateTo("/patient/dashboard", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorMessage = response.Message; // Display error message
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var token = await AuthenticationService.GetAuthToken();
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jwtToken = handler.ReadJwtToken(token);
|
||||
var role = jwtToken.Claims.FirstOrDefault(c => c.Type == "role")?.Value;
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case UserRoles.Admin:
|
||||
Navigation.NavigateTo("/admin/dashboard", true);
|
||||
break;
|
||||
case UserRoles.Doctor:
|
||||
Navigation.NavigateTo("/doctor/dashboard", true);
|
||||
break;
|
||||
case UserRoles.Patient:
|
||||
Navigation.NavigateTo("/patient/dashboard", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
@page "/register"
|
||||
@inject NavigationManager Navigation
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
@layout AuthLayout
|
||||
|
||||
<h3>Register</h3>
|
||||
|
||||
@if (!string.IsNullOrEmpty(_errorMessage))
|
||||
{
|
||||
<div class="alert alert-danger mt-2">@_errorMessage</div>
|
||||
}
|
||||
|
||||
|
||||
<EditForm Model="_registerModel" OnValidSubmit="HandleRegister">
|
||||
<div class="form-group">
|
||||
<label>Name:</label>
|
||||
<InputText class="form-control" @bind-Value="_registerModel.Name"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Email:</label>
|
||||
<InputText class="form-control" @bind-Value="_registerModel.Email"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password:</label>
|
||||
<InputText class="form-control" @bind-Value="_registerModel.Password" type="password"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Role:</label>
|
||||
<InputSelect class="form-control" @bind-Value="_registerModel.Role">
|
||||
<option value="">Select a role</option>
|
||||
<option value="Doctor">Doctor</option>
|
||||
<option value="Patient">Patient</option>
|
||||
</InputSelect>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Register</button>
|
||||
</EditForm>
|
||||
|
||||
@code {
|
||||
private readonly UserRegisterModel _registerModel = new();
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
private async Task HandleRegister()
|
||||
{
|
||||
_errorMessage = string.Empty;
|
||||
var response = await AuthenticationService.Register(_registerModel);
|
||||
|
||||
if (response.StatusCode == 201)
|
||||
{
|
||||
// Registration successful, navigate to login page
|
||||
Navigation.NavigateTo("/login", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorMessage = response.Message; // Display error message
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var token = await AuthenticationService.GetAuthToken();
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jwtToken = handler.ReadJwtToken(token);
|
||||
var role = jwtToken.Claims.FirstOrDefault(c => c.Type == "role")?.Value;
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case UserRoles.Admin:
|
||||
Navigation.NavigateTo("/admin/dashboard", true);
|
||||
break;
|
||||
case UserRoles.Doctor:
|
||||
Navigation.NavigateTo("/doctor/dashboard", true);
|
||||
break;
|
||||
case UserRoles.Patient:
|
||||
Navigation.NavigateTo("/patient/dashboard", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
@page "/reset-password"
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
@inject NavigationManager Navigation
|
||||
@layout AuthLayout
|
||||
|
||||
<h3>Reset Password</h3>
|
||||
|
||||
@if (!string.IsNullOrEmpty(_errorMessage))
|
||||
{
|
||||
<div class="alert alert-danger mt-2">@_errorMessage</div>
|
||||
}
|
||||
|
||||
<EditForm Model="_resetPasswordModel" OnValidSubmit="HandleResetPassword">
|
||||
<div class="form-group">
|
||||
<label>Email:</label>
|
||||
<InputText class="form-control" @bind-Value="_resetPasswordModel.Email"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>New Password:</label>
|
||||
<InputText class="form-control" @bind-Value="_resetPasswordModel.Password" type="password"/>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Reset Password</button>
|
||||
</EditForm>
|
||||
|
||||
@code {
|
||||
private readonly ResetPasswordModel _resetPasswordModel = new();
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
private async Task HandleResetPassword()
|
||||
{
|
||||
_errorMessage = string.Empty;
|
||||
var response = await AuthenticationService.ResetPassword(_resetPasswordModel);
|
||||
|
||||
if (response.StatusCode == 200)
|
||||
{
|
||||
Navigation.NavigateTo("/login", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorMessage = response.Message; // Display error message
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
@page "/doctor/ai"
|
||||
|
||||
|
||||
@attribute [Authorize(Roles = UserRoles.Doctor)]
|
||||
@layout DoctorLayout
|
||||
@inject IDoctorManagementService DoctorManagementService
|
||||
|
||||
<h3>AI Sickness Prediction</h3>
|
||||
|
||||
@if (!string.IsNullOrEmpty(statusMessage))
|
||||
{
|
||||
<div class="alert @(isSuccess ? "alert-success" : "alert-danger")" role="alert">
|
||||
@statusMessage
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
Upload PDF Document
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<InputFile OnChange="HandleFileSelected"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (predictions != null && predictions.Count > 0)
|
||||
{
|
||||
<div class="mt-4">
|
||||
<h4>Predictions:</h4>
|
||||
<ul>
|
||||
@foreach (var prediction in predictions)
|
||||
{
|
||||
<li>@prediction.Disease - Probability: @prediction.Probability%</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private string statusMessage = string.Empty;
|
||||
private bool isSuccess = false;
|
||||
private List<PredictionResult> predictions;
|
||||
|
||||
private async Task HandleFileSelected(InputFileChangeEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var file = e.File;
|
||||
await using var stream = file.OpenReadStream();
|
||||
using var memoryStream = new MemoryStream();
|
||||
await stream.CopyToAsync(memoryStream);
|
||||
var fileContent = memoryStream.ToArray();
|
||||
|
||||
var text = await ExtractTextFromPdf(fileContent);
|
||||
Console.WriteLine(text);
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
var result = await DoctorManagementService.GetSicknessPrediction(text);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
predictions = result.Data;
|
||||
statusMessage = result.Message;
|
||||
isSuccess = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
statusMessage = "Failed to get predictions from the AI service.";
|
||||
isSuccess = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
statusMessage = "Failed to extract text from PDF.";
|
||||
isSuccess = false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
statusMessage = $"Error: {ex.Message}";
|
||||
isSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> ExtractTextFromPdf(byte[] pdfBytes)
|
||||
{
|
||||
using var reader = new MemoryStream(pdfBytes);
|
||||
using var pdfReader = new PdfReader(reader);
|
||||
using var pdfDoc = new PdfDocument(pdfReader);
|
||||
var text = new StringBuilder();
|
||||
|
||||
for (int i = 1; i <= pdfDoc.GetNumberOfPages(); i++)
|
||||
{
|
||||
var strategy = new SimpleTextExtractionStrategy();
|
||||
var pageText = PdfTextExtractor.GetTextFromPage(pdfDoc.GetPage(i), strategy);
|
||||
text.AppendLine(pageText);
|
||||
}
|
||||
|
||||
return text.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
@page "/doctor/chat"
|
||||
|
||||
@attribute [Authorize(Roles = UserRoles.Doctor)]
|
||||
@layout DoctorLayout
|
||||
@inject IDoctorManagementService DoctorManagementService
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
|
||||
<h3>Chat with Patients</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 patient in patients)
|
||||
{
|
||||
<button class="list-group-item list-group-item-action" @onclick="() => OpenChat(patient.Id, patient.Name)">
|
||||
@patient.Name
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
@if (selectedPatient != null)
|
||||
{
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Chat with @selectedPatient.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" : selectedPatient.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<Patient> patients = new();
|
||||
private Patient selectedPatient;
|
||||
private List<Message> messages = new();
|
||||
private string newMessage = string.Empty;
|
||||
private UserInformation userInfo;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
userInfo = await AuthenticationService.GetUserInformation();
|
||||
patients = await DoctorManagementService.GetAllPatientsAsync();
|
||||
}
|
||||
|
||||
private async Task OpenChat(Guid patientId, string patientName)
|
||||
{
|
||||
selectedPatient = new Patient { Id = patientId, Name = patientName };
|
||||
var chat = await DoctorManagementService.GetConversationAsync(userInfo.Id, patientId);
|
||||
messages = chat?.Messages ?? new List<Message>();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void CloseChat()
|
||||
{
|
||||
selectedPatient = null;
|
||||
messages.Clear();
|
||||
}
|
||||
|
||||
private async Task SendMessage()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(newMessage) && selectedPatient != null)
|
||||
{
|
||||
var success = await DoctorManagementService.SendMessageAsync(userInfo.Id, selectedPatient.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 "/doctor/dashboard"
|
||||
@attribute [Authorize(Roles = UserRoles.Doctor)]
|
||||
@layout DoctorLayout
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
@inject IDoctorManagementService DoctorManagementService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<h3>Dashboard</h3>
|
||||
|
||||
@if (!string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
<div class="alert alert-danger" role="alert">
|
||||
@errorMessage
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrEmpty(username))
|
||||
{
|
||||
<p>Welcome, Dr. @username!</p>
|
||||
<p>Here you have listed your active appointments with patients.</p>
|
||||
}
|
||||
|
||||
@if (appointments != null && appointments.Count > 0)
|
||||
{
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Patient</th>
|
||||
<th>Time</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var appointment in appointments)
|
||||
{
|
||||
foreach (var appointmentTime in appointment.AppointmentsList)
|
||||
{
|
||||
<tr>
|
||||
<td>@patients[Guid.Parse(appointment.PatientId)]</td>
|
||||
<td>@appointmentTime.ToString("g")</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" @onclick="() => DeleteAppointment(appointment, appointmentTime)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
else if (string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
<p>No appointments exist.</p>
|
||||
}
|
||||
|
||||
@code {
|
||||
private string username;
|
||||
private List<Appointment> appointments = new();
|
||||
private Dictionary<Guid, string> patients = new();
|
||||
private string errorMessage = string.Empty;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var userInfo = await AuthenticationService.GetUserInformation();
|
||||
username = userInfo.Name;
|
||||
appointments = await DoctorManagementService.GetAppointmentsAsync(userInfo.Id);
|
||||
var allPatients = await DoctorManagementService.GetAllPatientsAsync();
|
||||
patients = allPatients.ToDictionary(pat => pat.Id, pat => pat.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 DoctorManagementService.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,101 @@
|
||||
@page "/doctor/patients_medical_history"
|
||||
@attribute [Authorize(Roles = UserRoles.Doctor)]
|
||||
@layout DoctorLayout
|
||||
@inject IDoctorManagementService DoctorManagementService
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
@inject IJSRuntime JSRuntime
|
||||
|
||||
<script src="fileHelper.js"></script>
|
||||
<script src="downloadFile.js"></script>
|
||||
|
||||
<h3>Patient Medical Histories</h3>
|
||||
|
||||
|
||||
@if (!string.IsNullOrEmpty(statusMessage))
|
||||
{
|
||||
<div class="alert @(isSuccess ? "alert-success" : "alert-danger")" role="alert">
|
||||
@statusMessage
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row mt-4">
|
||||
@foreach (var patient in patients)
|
||||
{
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-body text-center">
|
||||
<div>
|
||||
<strong>Status: </strong>
|
||||
@if (patientHasAccess(patient.Id))
|
||||
{
|
||||
<span class="text-success">Granted</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-danger">Revoked</span>
|
||||
}
|
||||
</div>
|
||||
<img src="user.png" style="width: 70px" alt="Patient" class="img-fluid mt-3"/>
|
||||
<h5 class="mt-3">@patient.Name</h5>
|
||||
<button class="btn btn-info mt-2" @onclick="() => DownloadMedicalHistory(patient.Id)" disabled="@(patientHasAccess(patient.Id) ? false : true)">Download Medical History</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string statusMessage;
|
||||
private bool isSuccess;
|
||||
private List<Patient> patients = new();
|
||||
private List<Guid> grantedAccessPatients = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadPatientsAndAccessStatus();
|
||||
}
|
||||
|
||||
private async Task LoadPatientsAndAccessStatus()
|
||||
{
|
||||
var userInfo = await AuthenticationService.GetUserInformation();
|
||||
patients = await DoctorManagementService.GetAllPatientsAsync();
|
||||
await UpdatePatientAccessStatuses(userInfo.Id);
|
||||
}
|
||||
|
||||
private async Task UpdatePatientAccessStatuses(Guid doctorId)
|
||||
{
|
||||
foreach (var patient in patients)
|
||||
{
|
||||
var medicalHistory = await DoctorManagementService.GetMedicalHistoryAsync(patient.Id);
|
||||
if (medicalHistory == null) continue;
|
||||
if (await DoctorManagementService.CheckForAccessAsync(medicalHistory.Id, doctorId))
|
||||
{
|
||||
grantedAccessPatients.Add(patient.Id);
|
||||
}
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private bool patientHasAccess(Guid patientId)
|
||||
{
|
||||
return grantedAccessPatients.Contains(patientId);
|
||||
}
|
||||
|
||||
private async Task DownloadMedicalHistory(Guid patientId)
|
||||
{
|
||||
var medicalHistory = await DoctorManagementService.DownloadMedicalHistoryAsync(patientId);
|
||||
if (medicalHistory != null)
|
||||
{
|
||||
var base64 = Convert.ToBase64String(medicalHistory);
|
||||
var href = $"data:application/octet-stream;base64,{base64}";
|
||||
await JSRuntime.InvokeVoidAsync("downloadFile", "MedicalHistory.pdf", href);
|
||||
}
|
||||
else
|
||||
{
|
||||
statusMessage = "Failed to download medical history.";
|
||||
isSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
@page "/doctor/profile"
|
||||
@attribute [Authorize(Roles = UserRoles.Doctor)]
|
||||
@layout DoctorLayout
|
||||
@inject NavigationManager Navigation
|
||||
@inject IDoctorManagementService DoctorManagementService
|
||||
@inject IAuthenticationService AuthenticationService
|
||||
|
||||
<h3>Profile</h3>
|
||||
|
||||
@if (!string.IsNullOrEmpty(updateMessage))
|
||||
{
|
||||
<div class="alert @(updateSuccess ? "alert-success" : "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-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="name">Name</label>
|
||||
<InputText id="name" class="form-control" @bind-Value="profileModel.Name"/>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="email">Email</label>
|
||||
<InputText id="email" class="form-control" @bind-Value="profileModel.Email"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="password">Password</label>
|
||||
<InputText id="password" type="password" class="form-control" @bind-Value="profileModel.Password"/>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="description">Description</label>
|
||||
<InputTextArea id="description" class="form-control" @bind-Value="profileModel.Description" rows="5"/>
|
||||
</div>
|
||||
</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 @(deleteSuccess ? "alert-success" : "alert-danger") mt-3">@deleteMessage</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private Doctor profileModel = new();
|
||||
private string updateMessage = string.Empty;
|
||||
private string deleteMessage = string.Empty;
|
||||
private bool updateSuccess = false;
|
||||
private bool deleteSuccess = false;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var userInfo = await AuthenticationService.GetUserInformation();
|
||||
if (userInfo != null)
|
||||
{
|
||||
profileModel = await DoctorManagementService.GetDoctorProfileAsync(userInfo.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateProfile()
|
||||
{
|
||||
updateMessage = string.Empty;
|
||||
updateSuccess = false;
|
||||
|
||||
var success = await DoctorManagementService.UpdateDoctorProfileAsync(profileModel);
|
||||
if (success)
|
||||
{
|
||||
updateMessage = "Profile updated successfully.";
|
||||
updateSuccess = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
updateMessage = "An error occurred while updating your profile. Please try again.";
|
||||
updateSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteAccount()
|
||||
{
|
||||
deleteMessage = string.Empty;
|
||||
deleteSuccess = false;
|
||||
|
||||
var success = await DoctorManagementService.DeleteDoctorProfileAsync(profileModel.Id);
|
||||
if (success)
|
||||
{
|
||||
await AuthenticationService.RemoveAuthToken();
|
||||
Navigation.NavigateTo("/goodbye");
|
||||
}
|
||||
else
|
||||
{
|
||||
deleteMessage = "An error occurred while deleting your account. Please try again.";
|
||||
deleteSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
@inject NavigationManager Navigation
|
||||
@layout MainLayout;
|
||||
<link href="css/Components/NotAuthorizedHandler.css" rel="stylesheet"/>
|
||||
|
||||
<div class="not-authorized-container">
|
||||
<h1>Access Denied</h1>
|
||||
<p>You do not have permission to view this page. This might be due to one of several reasons:</p>
|
||||
<ul>
|
||||
<li>You are not logged in. <a href="/login">Login</a> to continue.</li>
|
||||
<li>You do not have the necessary permissions to access this resource.</li>
|
||||
</ul>
|
||||
<p>If you think this is a mistake, please contact your administrator.</p>
|
||||
<button class="btn btn-primary" @onclick="GoHome">Return to Home Page</button>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
void GoHome()
|
||||
{
|
||||
Navigation.NavigateTo("/", true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
@inject NavigationManager Navigation
|
||||
@layout MainLayout;
|
||||
<link href="css/Components/PageNotFoundHandler.css" rel="stylesheet"/>
|
||||
|
||||
<div class="page-not-found-container">
|
||||
<h1>Page Not Found</h1>
|
||||
<p>The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.</p>
|
||||
<ul>
|
||||
<li>Check the URL for typos.</li>
|
||||
<li>Return to the <a href="/">Homepage</a>.</li>
|
||||
</ul>
|
||||
<button class="btn btn-primary" @onclick="GoHome">Return to Home Page</button>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
void GoHome()
|
||||
{
|
||||
Navigation.NavigateTo("/", true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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">×</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.";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+31
-15
@@ -1,28 +1,44 @@
|
||||
using Blazored.LocalStorage;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
using Blazored.LocalStorage;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using HealthcareManagerUiWebAssem;
|
||||
using HealthcareManagerUiWebAssem.Services.Appointment;
|
||||
using HealthcareManagerUiWebAssem.Services.AdminUserManagement;
|
||||
using HealthcareManagerUiWebAssem.Services.Authentication;
|
||||
using HealthcareManagerUiWebAssem.Services.Http;
|
||||
using HealthcareManagerUiWebAssem.Services.Profile;
|
||||
using HealthcareManagerUiWebAssem.Services.User;
|
||||
using HealthcareManagerUiWebAssem.Services.UserSessionInformation;
|
||||
using HealthcareManagerUiWebAssem.Services.DoctorManagement;
|
||||
using HealthcareManagerUiWebAssem.Services.PatientManagement;
|
||||
using HealthcareManagerUiWebAssem.Services.RequestHttp;
|
||||
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
builder.RootComponents.Add<App>("#app");
|
||||
builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
|
||||
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
|
||||
builder.Services.Configure<ApplicationSettings>(builder.Configuration.GetSection("ApiSettings"));
|
||||
builder.Services.AddHttpClient();
|
||||
// Configure API settings
|
||||
var apiSettings = builder.Configuration.GetSection("ApiSettings").Get<ApplicationSettings>();
|
||||
builder.Services.AddSingleton(apiSettings);
|
||||
|
||||
// Configure custom HttpClient handler
|
||||
builder.Services.AddTransient<AuthenticationHttpMessageHandler>();
|
||||
|
||||
builder.Services.AddHttpClient<IRequestHttpService, RequestHttpService>((sp, httpClient) =>
|
||||
{
|
||||
var settings = sp.GetRequiredService<ApplicationSettings>();
|
||||
httpClient.BaseAddress = new Uri(settings.BaseAddress);
|
||||
}).AddHttpMessageHandler<AuthenticationHttpMessageHandler>();
|
||||
|
||||
builder.Services.AddBlazoredLocalStorage();
|
||||
builder.Services.AddScoped<IUserSessionInformation, UserSessionInformation>();
|
||||
builder.Services.AddScoped<IRequestHttpService, RequestHttpService>();
|
||||
builder.Services.AddScoped<UserService>();
|
||||
builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();
|
||||
builder.Services.AddScoped<IProfileService, ProfileService>();
|
||||
builder.Services.AddScoped<IAppointmentService, AppointmentService>();
|
||||
builder.Services.AddScoped<IAdminManagementService, AdminManagementService>();
|
||||
builder.Services.AddScoped<IPatientManagementService, PatientManagementService>();
|
||||
builder.Services.AddScoped<IDoctorManagementService, DoctorManagementService>();
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
// Register CustomAuthenticationStateProvider as the AuthenticationStateProvider
|
||||
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();
|
||||
builder.Services.AddAuthorizationCore(config =>
|
||||
{
|
||||
config.AddPolicy("AdminOnly", policy => policy.RequireRole(UserRoles.Admin));
|
||||
config.AddPolicy("DoctorOnly", policy => policy.RequireRole(UserRoles.Doctor));
|
||||
config.AddPolicy("PatientOnly", policy => policy.RequireRole(UserRoles.Patient));
|
||||
});
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
@@ -0,0 +1,27 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem;
|
||||
|
||||
public class RoleBasedRedirect : ComponentBase
|
||||
{
|
||||
[CascadingParameter] private Task<AuthenticationState> authenticationStateTask { get; set; }
|
||||
[Inject] private NavigationManager NavigationManager { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var authState = await authenticationStateTask;
|
||||
var user = authState.User;
|
||||
|
||||
if (NavigationManager.Uri == NavigationManager.BaseUri)
|
||||
if (user.Identity.IsAuthenticated)
|
||||
{
|
||||
// Perform redirection based on the user's role
|
||||
if (user.IsInRole(UserRoles.Admin))
|
||||
NavigationManager.NavigateTo("/admin/dashboard");
|
||||
else if (user.IsInRole(UserRoles.Doctor))
|
||||
NavigationManager.NavigateTo("/doctor/dashboard");
|
||||
else if (user.IsInRole(UserRoles.Patient)) NavigationManager.NavigateTo("/patient/dashboard");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using HealthcareManagerUiWebAssem.Entities;
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
using HealthcareManagerUiWebAssem.Services.RequestHttp;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.AdminUserManagement;
|
||||
|
||||
public class AdminManagementService(IRequestHttpService requestHttpService) : IAdminManagementService
|
||||
{
|
||||
public async Task<ApiResponse<List<Doctor>>> GetDoctors()
|
||||
{
|
||||
var response = await requestHttpService.GetAsync("/api/Doctors");
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
return JsonSerializer.Deserialize<ApiResponse<List<Doctor>>>(responseContent,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<List<Patient>>> GetPatients()
|
||||
{
|
||||
var response = await requestHttpService.GetAsync("/api/Patients");
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
return JsonSerializer.Deserialize<ApiResponse<List<Patient>>>(responseContent,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteDoctor(Guid id)
|
||||
{
|
||||
var response = await requestHttpService.DeleteAsync($"/api/Doctors/{id}");
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public async Task<bool> DeletePatient(Guid id)
|
||||
{
|
||||
var response = await requestHttpService.DeleteAsync($"/api/Patients/{id}");
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using HealthcareManagerUiWebAssem.Entities;
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.AdminUserManagement;
|
||||
|
||||
public interface IAdminManagementService
|
||||
{
|
||||
Task<ApiResponse<List<Doctor>>> GetDoctors();
|
||||
Task<ApiResponse<List<Patient>>> GetPatients();
|
||||
Task<bool> DeleteDoctor(Guid id);
|
||||
Task<bool> DeletePatient(Guid id);
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@@ -1,104 +1,98 @@
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
using HealthcareManagerUiWebAssem.Services.Http;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using Blazored.LocalStorage;
|
||||
using HealthcareManagerUiWebAssem.Services.RequestHttp;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.Authentication;
|
||||
|
||||
public class AuthenticationService : IAuthenticationService
|
||||
public class AuthenticationService(
|
||||
IRequestHttpService requestHttpService,
|
||||
ILocalStorageService localStorageService,
|
||||
AuthenticationStateProvider authenticationStateProvider)
|
||||
: IAuthenticationService
|
||||
{
|
||||
private readonly IRequestHttpService _requestHttpService;
|
||||
|
||||
public AuthenticationService(IRequestHttpService requestHttpService)
|
||||
public async Task<ApiResponse<string>> Login(UserLoginModel loginModel)
|
||||
{
|
||||
_requestHttpService = requestHttpService;
|
||||
var response = await requestHttpService.PostAsync("/api/Authorization/login", loginModel);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var loginResponse = JsonSerializer.Deserialize<ApiResponse<string>>(responseContent,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
if (response.IsSuccessStatusCode) await localStorageService.SetItemAsync("authToken", loginResponse.Data);
|
||||
return loginResponse;
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> Login(UserLoginModel userLoginModel)
|
||||
public async Task<ApiResponse<string>> Register(UserRegisterModel registerModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _requestHttpService.PostAsync("/Authorization/login", userLoginModel);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
var response = await requestHttpService.PostAsync("/api/Authorization/register", registerModel);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var registerResponse = JsonSerializer.Deserialize<ApiResponse<string>>(responseContent,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
return registerResponse;
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> RegisterDoctor(UserRegisterModel userRegistrationModel)
|
||||
public async Task<ApiResponse<string>> ResetJwt(string token)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _requestHttpService.PostAsync("/Doctors/register", userRegistrationModel);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> RegisterPatient(UserRegisterModel userRegistrationModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _requestHttpService.PostAsync("/Patients/register", userRegistrationModel);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
var requestBody = new { token = token };
|
||||
var response = await requestHttpService.PostAsync("/api/Authorization/refresh_token", requestBody);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var resetJwtResponse = JsonSerializer.Deserialize<ApiResponse<string>>(responseContent,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
if (response.IsSuccessStatusCode) await localStorageService.SetItemAsync("authToken", resetJwtResponse.Data);
|
||||
return resetJwtResponse;
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> ResetPassword(UserLoginModel dto)
|
||||
public async Task<ApiResponse<object>> ResetPassword(ResetPasswordModel resetPasswordModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _requestHttpService.PostAsync("/Authorization/reset_password", dto);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
var requestBody = new { email = resetPasswordModel.Email, password = resetPasswordModel.Password };
|
||||
var response = await requestHttpService.PostAsync("/api/Authorization/reset_password", requestBody);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
|
||||
var resetPasswordResponse = JsonSerializer.Deserialize<ApiResponse<object>>(responseContent,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
return resetPasswordResponse;
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> RefreshToken(TokenRefreshModel dto)
|
||||
|
||||
public async Task<UserInformation?> GetUserInformation()
|
||||
{
|
||||
try
|
||||
var token = await GetAuthToken();
|
||||
if (string.IsNullOrEmpty(token)) return null;
|
||||
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jwtToken = handler.ReadJwtToken(token);
|
||||
|
||||
var name = jwtToken.Claims.FirstOrDefault(c => c.Type == "unique_name")?.Value;
|
||||
var id = jwtToken.Claims.FirstOrDefault(c => c.Type == "nameid")?.Value;
|
||||
var role = jwtToken.Claims.FirstOrDefault(c => c.Type == "role")?.Value;
|
||||
|
||||
return new UserInformation
|
||||
{
|
||||
var response = await _requestHttpService.PostAsync("/Authorization/refresh_token", dto);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
Name = name,
|
||||
Id = Guid.Parse(id),
|
||||
Role = role
|
||||
};
|
||||
}
|
||||
|
||||
public async Task Logout()
|
||||
{
|
||||
await localStorageService.RemoveItemAsync("authToken");
|
||||
((CustomAuthenticationStateProvider)authenticationStateProvider).NotifyUserLogout();
|
||||
}
|
||||
|
||||
public async Task<string> GetAuthToken()
|
||||
{
|
||||
return await localStorageService.GetItemAsync<string>("authToken");
|
||||
}
|
||||
|
||||
public async Task RemoveAuthToken()
|
||||
{
|
||||
await localStorageService.RemoveItemAsync("authToken");
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,12 @@ namespace HealthcareManagerUiWebAssem.Services.Authentication;
|
||||
|
||||
public interface IAuthenticationService
|
||||
{
|
||||
Task<BaseResponse> Login(UserLoginModel userLoginModel);
|
||||
Task<BaseResponse> RegisterPatient(UserRegisterModel userRegistrationModel);
|
||||
Task<BaseResponse> RegisterDoctor(UserRegisterModel userRegistrationModel);
|
||||
Task<BaseResponse> ResetPassword(UserLoginModel userResetPasswordModel);
|
||||
Task<BaseResponse> RefreshToken(TokenRefreshModel dto);
|
||||
Task<ApiResponse<string>> Login(UserLoginModel loginModel);
|
||||
Task<ApiResponse<string>> Register(UserRegisterModel registerModel);
|
||||
Task<ApiResponse<object>> ResetPassword(ResetPasswordModel resetPasswordModel);
|
||||
Task<ApiResponse<string>> ResetJwt(string token);
|
||||
Task Logout();
|
||||
Task<string> GetAuthToken();
|
||||
Task RemoveAuthToken();
|
||||
Task<UserInformation?> GetUserInformation();
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using System.Net.Http.Json;
|
||||
using HealthcareManagerUiWebAssem.Entities;
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
using HealthcareManagerUiWebAssem.Services.RequestHttp;
|
||||
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.DoctorManagement;
|
||||
|
||||
public class DoctorManagementService(IRequestHttpService requestHttpService) : IDoctorManagementService
|
||||
{
|
||||
public async Task<Doctor> GetDoctorProfileAsync(Guid doctorId)
|
||||
{
|
||||
var response = await requestHttpService.GetAsync($"/api/Doctors/{doctorId}");
|
||||
if (!response.IsSuccessStatusCode) return null;
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<Doctor>>();
|
||||
return apiResponse?.Data;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateDoctorProfileAsync(Doctor doctor)
|
||||
{
|
||||
var response = await requestHttpService.PutAsync("/api/Doctors", doctor);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteDoctorProfileAsync(Guid doctorId)
|
||||
{
|
||||
var response = await requestHttpService.DeleteAsync($"/api/Doctors/{doctorId}");
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public async Task<MedicalHistory?> GetMedicalHistoryAsync(Guid userId)
|
||||
{
|
||||
var response = await requestHttpService.GetAsync($"/api/MedicalHistory/user/{userId}");
|
||||
if (!response.IsSuccessStatusCode) return null;
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<MedicalHistory>>();
|
||||
return apiResponse.Data;
|
||||
}
|
||||
|
||||
public async Task<List<Patient>> GetAllPatientsAsync()
|
||||
{
|
||||
var response = await requestHttpService.GetAsync("/api/Patients");
|
||||
if (!response.IsSuccessStatusCode) return [];
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<List<Patient>>>();
|
||||
return apiResponse?.Data ?? [];
|
||||
}
|
||||
|
||||
public async Task<List<Appointment>> GetAppointmentsAsync(Guid doctorId)
|
||||
{
|
||||
var response = await requestHttpService.GetAsync($"/api/Appointments/doctor/{doctorId}");
|
||||
if (!response.IsSuccessStatusCode) return [];
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<List<Appointment>>>();
|
||||
return apiResponse?.Data ?? [];
|
||||
}
|
||||
|
||||
public async Task<bool> SendMessageAsync(Guid senderId, Guid receiverId, string message)
|
||||
{
|
||||
var requestBody = new { sender = senderId, receiver = receiverId, message = message };
|
||||
var response = await requestHttpService.PostAsync("/api/Chat/send_message", requestBody);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public async Task<bool> CancelAppointmentAsync(Guid doctorId, Guid patientId, DateTime appointmentDate)
|
||||
{
|
||||
var cancellationRequest = new AppointmentCancellationRequest
|
||||
{
|
||||
DoctorId = doctorId,
|
||||
PatientId = patientId,
|
||||
Appointment = appointmentDate
|
||||
};
|
||||
|
||||
var response = await requestHttpService.PutAsync("/api/Appointments", cancellationRequest);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public async Task<Chat> GetConversationAsync(Guid userId1, Guid userId2)
|
||||
{
|
||||
var requestBody = new { idUser1 = userId1, idUser2 = userId2 };
|
||||
var response = await requestHttpService.PostAsync("/api/Chat/get_conversation", requestBody);
|
||||
if (!response.IsSuccessStatusCode) return null;
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<Chat>>();
|
||||
return apiResponse?.Data;
|
||||
}
|
||||
|
||||
|
||||
public async Task<byte[]> DownloadMedicalHistoryAsync(Guid patientId)
|
||||
{
|
||||
var response = await requestHttpService.GetAsync($"/api/MedicalHistory/user/{patientId}");
|
||||
if (!response.IsSuccessStatusCode) return null;
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<MedicalHistory>>();
|
||||
return apiResponse.Data.Content;
|
||||
}
|
||||
|
||||
public async Task<bool> CheckForAccessAsync(Guid medicalRecordId, Guid doctorId)
|
||||
{
|
||||
var requestBody = new { MedicalRecordId = medicalRecordId, DoctorId = doctorId };
|
||||
var response = await requestHttpService.PostAsync("/api/MedicalHistory/check_access", requestBody);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<List<PredictionResult>>> GetSicknessPrediction(string text)
|
||||
{
|
||||
var requestBody = new { text };
|
||||
var response = await requestHttpService.PostAsync("/api/SicknessPrediction", requestBody);
|
||||
if (!response.IsSuccessStatusCode) return null;
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<List<PredictionResult>>>();
|
||||
return apiResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using HealthcareManagerUiWebAssem.Entities;
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.DoctorManagement;
|
||||
|
||||
public interface IDoctorManagementService
|
||||
{
|
||||
Task<Doctor> GetDoctorProfileAsync(Guid doctorId);
|
||||
Task<bool> UpdateDoctorProfileAsync(Doctor doctor);
|
||||
Task<bool> DeleteDoctorProfileAsync(Guid doctorId);
|
||||
|
||||
Task<List<Appointment>> GetAppointmentsAsync(Guid doctorId);
|
||||
Task<bool> CancelAppointmentAsync(Guid doctorId, Guid patientId, DateTime appointmentDate);
|
||||
Task<MedicalHistory?> GetMedicalHistoryAsync(Guid userId);
|
||||
Task<List<Patient>> GetAllPatientsAsync();
|
||||
Task<bool> SendMessageAsync(Guid senderId, Guid receiverId, string message);
|
||||
Task<Chat> GetConversationAsync(Guid userId1, Guid userId2);
|
||||
Task<byte[]> DownloadMedicalHistoryAsync(Guid patientId);
|
||||
|
||||
Task<bool> CheckForAccessAsync(Guid medicalRecordId, Guid doctorId);
|
||||
Task<ApiResponse<List<PredictionResult>>> GetSicknessPrediction(string text);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.Http;
|
||||
|
||||
public interface IRequestHttpService
|
||||
{
|
||||
Task<BaseResponse> GetAsync(string uri, IDictionary<string, string> headers = null);
|
||||
|
||||
Task<BaseResponse> GetByIdAsync(string uri, Guid id, IDictionary<string, string> headers = null);
|
||||
Task<BaseResponse> PostAsync(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> DeleteByFilterAsync(string uri, object data, IDictionary<string, string> headers = null);
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
using HealthcareManagerUiWebAssem.Services.UserSessionInformation;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.Http;
|
||||
|
||||
public class RequestHttpService : IRequestHttpService
|
||||
{
|
||||
private readonly string _apiEndpoint;
|
||||
private readonly string _apiKey;
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly IUserSessionInformation _userSessionInformation;
|
||||
|
||||
public RequestHttpService(IHttpClientFactory httpClientFactory, IUserSessionInformation userSessionInformation,
|
||||
IOptions<ApplicationSettings> settings)
|
||||
{
|
||||
_httpClient = httpClientFactory.CreateClient();
|
||||
_apiKey = settings.Value.ApiKey ?? "NoKey";
|
||||
_apiEndpoint = settings.Value.ApiEndpoint;
|
||||
_userSessionInformation = userSessionInformation;
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> GetByIdAsync(string uri, Guid id, IDictionary<string, string>? headers = null)
|
||||
{
|
||||
headers ??= new Dictionary<string, string>();
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, $"{_apiEndpoint}{uri}/{id}");
|
||||
AddHeaders(request, headers);
|
||||
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
Thread.Sleep(1000);
|
||||
return await HandleResponse(response, uri);
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> GetAsync(string uri, IDictionary<string, string>? headers = null)
|
||||
{
|
||||
headers ??= new Dictionary<string, string>();
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, $"{_apiEndpoint}{uri}");
|
||||
AddHeaders(request, headers);
|
||||
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
Thread.Sleep(1000);
|
||||
return await HandleResponse(response, uri);
|
||||
}
|
||||
|
||||
|
||||
public async Task<BaseResponse> PostAsync(string uri, object data, IDictionary<string, string>? headers = null)
|
||||
{
|
||||
headers ??= new Dictionary<string, string>();
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, $"{_apiEndpoint}{uri}"); // Prepend _apiEndpoint
|
||||
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);
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> PutAsync(string uri, object data, IDictionary<string, string>? headers = null)
|
||||
{
|
||||
headers ??= new Dictionary<string, string>();
|
||||
var request = new HttpRequestMessage(HttpMethod.Put, $"{_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);
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> DeleteAsync(string uri, Guid id, IDictionary<string, string>? headers = null)
|
||||
{
|
||||
headers ??= new Dictionary<string, string>();
|
||||
var request = new HttpRequestMessage(HttpMethod.Delete, $"{_apiEndpoint}{uri}/{id}");
|
||||
AddHeaders(request, headers);
|
||||
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
Thread.Sleep(1000);
|
||||
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)
|
||||
{
|
||||
request.Headers.Add("ApiKey", _apiKey);
|
||||
request.Headers.Add("Authorization", "Bearer " + await _userSessionInformation.GetTokenAsync());
|
||||
|
||||
if (headers == null) return;
|
||||
foreach (var header in headers) request.Headers.Add(header.Key, header.Value);
|
||||
}
|
||||
|
||||
private async Task<BaseResponse> HandleResponse(HttpResponseMessage? response, string uri)
|
||||
{
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
var responseJson = JsonSerializer.Deserialize<JsonResponse>(
|
||||
responseContent,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
|
||||
);
|
||||
|
||||
var baseResponse = new BaseResponse();
|
||||
|
||||
if (responseJson != null)
|
||||
{
|
||||
baseResponse.StatusCode = responseJson.StatusCode;
|
||||
baseResponse.Message = responseJson.Message;
|
||||
baseResponse.Data = responseJson.Data.ToString();
|
||||
baseResponse.Headers = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
foreach (var header in response.Headers.Concat(response.Content.Headers))
|
||||
{
|
||||
Console.WriteLine(header.Key);
|
||||
if (baseResponse.Headers.ContainsKey(header.Key))
|
||||
{
|
||||
baseResponse.Headers[header.Key] += ", " + string.Join(", ", header.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
baseResponse.Headers.Add(header.Key, string.Join(", ", header.Value));
|
||||
}
|
||||
}
|
||||
|
||||
return baseResponse;
|
||||
}
|
||||
|
||||
class JsonResponse
|
||||
{
|
||||
public int StatusCode { get; set; }
|
||||
public string Message { get; set; }
|
||||
public JsonElement Data { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using HealthcareManagerUiWebAssem.Entities;
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.PatientManagement;
|
||||
|
||||
public interface IPatientManagementService
|
||||
{
|
||||
Task<Patient> GetPatientProfileAsync(Guid patientId);
|
||||
Task<bool> UpdatePatientProfileAsync(Patient patient);
|
||||
Task<bool> DeletePatientProfileAsync(Guid patientId);
|
||||
|
||||
Task<List<Appointment>> GetAppointmentsAsync(Guid patientId);
|
||||
Task<ApiResponse<object>> BookAppointmentAsync(NewAppointmentModel newAppointment);
|
||||
Task<bool> CancelAppointmentAsync(Guid doctorId, Guid patientId, DateTime appointmentDate);
|
||||
|
||||
Task<MedicalHistory?> GetMedicalHistoryAsync(Guid userId);
|
||||
Task<ApiResponse<object>> UploadMedicalHistoryAsync(Guid userId, byte[] content);
|
||||
Task<bool> DeleteMedicalHistoryAsync(Guid fileId);
|
||||
Task<byte[]> DownloadMedicalHistoryAsync(Guid fileId);
|
||||
|
||||
|
||||
Task<List<Doctor>> GetAllDoctorsAsync();
|
||||
|
||||
Task<bool> CheckForAccessAsync(Guid medicalRecordId, Guid doctorId);
|
||||
Task<bool> GrantAccessAsync(Guid medicalRecordId, Guid doctorId);
|
||||
Task<bool> RevokeAccessAsync(Guid medicalRecordId, Guid doctorId);
|
||||
|
||||
Task<bool> SendMessageAsync(Guid senderId, Guid receiverId, string message);
|
||||
Task<Chat> GetConversationAsync(Guid userId1, Guid userId2);
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using System.Net.Http.Json;
|
||||
using HealthcareManagerUiWebAssem.Entities;
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
using HealthcareManagerUiWebAssem.Services.RequestHttp;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.PatientManagement;
|
||||
|
||||
public class PatientManagementService(IRequestHttpService requestHttpService) : IPatientManagementService
|
||||
{
|
||||
public async Task<Patient> GetPatientProfileAsync(Guid patientId)
|
||||
{
|
||||
var response = await requestHttpService.GetAsync($"/api/Patients/{patientId}");
|
||||
if (!response.IsSuccessStatusCode) return null;
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<Patient>>();
|
||||
return apiResponse?.Data;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdatePatientProfileAsync(Patient patient)
|
||||
{
|
||||
var response = await requestHttpService.PutAsync("/api/Patients", patient);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public async Task<bool> DeletePatientProfileAsync(Guid patientId)
|
||||
{
|
||||
var response = await requestHttpService.DeleteAsync($"/api/Patients/{patientId}");
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public async Task<List<Appointment>> GetAppointmentsAsync(Guid patientId)
|
||||
{
|
||||
var response = await requestHttpService.GetAsync($"/api/Appointments/patient/{patientId}");
|
||||
if (!response.IsSuccessStatusCode) return new List<Appointment>();
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<List<Appointment>>>();
|
||||
return apiResponse?.Data ?? [];
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<object>> BookAppointmentAsync(NewAppointmentModel newAppointment)
|
||||
{
|
||||
var response = await requestHttpService.PostAsync("/api/Appointments", newAppointment);
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<object>>();
|
||||
return apiResponse;
|
||||
}
|
||||
|
||||
public async Task<bool> CancelAppointmentAsync(Guid doctorId, Guid patientId, DateTime appointmentDate)
|
||||
{
|
||||
var cancellationRequest = new AppointmentCancellationRequest
|
||||
{
|
||||
DoctorId = doctorId,
|
||||
PatientId = patientId,
|
||||
Appointment = appointmentDate
|
||||
};
|
||||
|
||||
var response = await requestHttpService.PutAsync("/api/Appointments", cancellationRequest);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
// Medical history methods
|
||||
|
||||
public async Task<MedicalHistory?> GetMedicalHistoryAsync(Guid userId)
|
||||
{
|
||||
var response = await requestHttpService.GetAsync($"/api/MedicalHistory/user/{userId}");
|
||||
if (!response.IsSuccessStatusCode) return null;
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<MedicalHistory>>();
|
||||
return apiResponse.Data;
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<object>> UploadMedicalHistoryAsync(Guid userId, byte[] content)
|
||||
{
|
||||
var requestBody = new { UserId = userId, Content = content };
|
||||
var response = await requestHttpService.PostAsync("/api/MedicalHistory", requestBody);
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<object>>();
|
||||
return apiResponse;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteMedicalHistoryAsync(Guid fileId)
|
||||
{
|
||||
var response = await requestHttpService.DeleteAsync($"/api/MedicalHistory/{fileId}");
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public async Task<byte[]> DownloadMedicalHistoryAsync(Guid fileId)
|
||||
{
|
||||
var response = await requestHttpService.GetAsync($"/api/MedicalHistory/{fileId}");
|
||||
if (response.IsSuccessStatusCode) return await response.Content.ReadAsByteArrayAsync();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public async Task<List<Doctor>> GetAllDoctorsAsync()
|
||||
{
|
||||
var response = await requestHttpService.GetAsync("/api/Doctors");
|
||||
if (!response.IsSuccessStatusCode) return [];
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<List<Doctor>>>();
|
||||
return apiResponse?.Data ?? [];
|
||||
}
|
||||
|
||||
public async Task<bool> CheckForAccessAsync(Guid medicalRecordId, Guid doctorId)
|
||||
{
|
||||
var requestBody = new { MedicalRecordId = medicalRecordId, DoctorId = doctorId };
|
||||
var response = await requestHttpService.PostAsync("/api/MedicalHistory/check_access", requestBody);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public async Task<bool> GrantAccessAsync(Guid medicalRecordId, Guid doctorId)
|
||||
{
|
||||
var requestBody = new { MedicalRecordId = medicalRecordId, DoctorId = doctorId };
|
||||
var response = await requestHttpService.PutAsync("/api/MedicalHistory/grant_access", requestBody);
|
||||
if (!response.IsSuccessStatusCode) return false;
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<object>>();
|
||||
return apiResponse.StatusCode == HttpStatusCodes.OK;
|
||||
}
|
||||
|
||||
public async Task<bool> RevokeAccessAsync(Guid medicalRecordId, Guid doctorId)
|
||||
{
|
||||
var requestBody = new { MedicalRecordId = medicalRecordId, DoctorId = doctorId };
|
||||
var response = await requestHttpService.PutAsync("/api/MedicalHistory/revoke_access", requestBody);
|
||||
if (!response.IsSuccessStatusCode) return false;
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<object>>();
|
||||
return apiResponse.StatusCode == HttpStatusCodes.OK;
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> SendMessageAsync(Guid senderId, Guid receiverId, string message)
|
||||
{
|
||||
var requestBody = new { sender = senderId, receiver = receiverId, message = message };
|
||||
var response = await requestHttpService.PostAsync("/api/Chat/send_message", requestBody);
|
||||
return response.IsSuccessStatusCode;
|
||||
}
|
||||
|
||||
public async Task<Chat> GetConversationAsync(Guid userId1, Guid userId2)
|
||||
{
|
||||
var requestBody = new { idUser1 = userId1, idUser2 = userId2 };
|
||||
var response = await requestHttpService.PostAsync("/api/Chat/get_conversation", requestBody);
|
||||
if (!response.IsSuccessStatusCode) return null;
|
||||
var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<Chat>>();
|
||||
return apiResponse?.Data;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.Profile;
|
||||
|
||||
public interface IProfileService
|
||||
{
|
||||
Task<BaseResponse> GetDoctors();
|
||||
|
||||
Task<BaseResponse> GetPatients();
|
||||
|
||||
Task<BaseResponse> GetDoctorById(Guid id);
|
||||
|
||||
Task<BaseResponse> GetPatientById(Guid id);
|
||||
|
||||
Task<BaseResponse> UpdateDoctorProfile(UserUpdateProfileModel userUpdateProfileModel);
|
||||
|
||||
Task<BaseResponse> UpdatePatientProfile(UserUpdateProfileModel userUpdateProfileModel);
|
||||
|
||||
Task<BaseResponse> DeleteDoctorProfile(Guid id);
|
||||
|
||||
Task<BaseResponse> DeletePatientProfile(Guid id);
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
using HealthcareManagerUiWebAssem.Services.Http;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.Profile;
|
||||
|
||||
public class ProfileService : IProfileService
|
||||
{
|
||||
private readonly IRequestHttpService _requestHttpService;
|
||||
|
||||
public ProfileService(IRequestHttpService requestHttpService)
|
||||
{
|
||||
_requestHttpService = requestHttpService;
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> GetDoctors()
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _requestHttpService.GetAsync("/Doctors");
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> GetPatients()
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _requestHttpService.GetAsync("/Patients");
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> GetDoctorById(Guid id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _requestHttpService.GetByIdAsync("/Doctors", id);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> GetPatientById(Guid id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _requestHttpService.GetByIdAsync("/Patients", id);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<BaseResponse> UpdateDoctorProfile(
|
||||
UserUpdateProfileModel userUpdateProfileModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _requestHttpService.PutAsync("/Doctors", userUpdateProfileModel);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> UpdatePatientProfile(
|
||||
UserUpdateProfileModel userUpdateProfileModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _requestHttpService.PutAsync("/Patients", userUpdateProfileModel);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> DeleteDoctorProfile(Guid id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _requestHttpService.DeleteAsync("/Doctors", id);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> DeletePatientProfile(Guid id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _requestHttpService.DeleteAsync("/Patients", id);
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
StatusCode = HttpStatusCodes.InternalServerError,
|
||||
Data = null,
|
||||
Message = ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace HealthcareManagerUiWebAssem.Services.RequestHttp;
|
||||
|
||||
public interface IRequestHttpService
|
||||
{
|
||||
Task<HttpResponseMessage> GetAsync(string requestUri);
|
||||
Task<HttpResponseMessage> PostAsync<T>(string requestUri, T value);
|
||||
Task<HttpResponseMessage> PutAsync<T>(string requestUri, T value);
|
||||
Task<HttpResponseMessage> DeleteAsync(string requestUri);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.RequestHttp;
|
||||
|
||||
public class RequestHttpService : IRequestHttpService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public RequestHttpService(HttpClient httpClient, ApplicationSettings apiSettings)
|
||||
{
|
||||
httpClient.BaseAddress = new Uri(apiSettings.BaseAddress);
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> GetAsync(string requestUri)
|
||||
{
|
||||
return await _httpClient.GetAsync(requestUri);
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> PostAsync<T>(string requestUri, T value)
|
||||
{
|
||||
return await _httpClient.PostAsJsonAsync(requestUri, value);
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> PutAsync<T>(string requestUri, T value)
|
||||
{
|
||||
return await _httpClient.PutAsJsonAsync(requestUri, value);
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> DeleteAsync(string requestUri)
|
||||
{
|
||||
return await _httpClient.DeleteAsync(requestUri);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
using HealthcareManagerUiWebAssem.Services.Profile;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.User;
|
||||
|
||||
public class UserService
|
||||
{
|
||||
public Guid UserId { get; private set; }
|
||||
|
||||
public void SetUserId(Guid id)
|
||||
{
|
||||
UserId = id;
|
||||
}
|
||||
|
||||
public async Task<UserUpdateProfileModel?> InitializeProfile(string role, IProfileService profileService)
|
||||
{
|
||||
var response = role switch
|
||||
{
|
||||
"doctor" => await profileService.GetDoctorById(UserId),
|
||||
"patient" => await profileService.GetPatientById(UserId),
|
||||
_ => null
|
||||
};
|
||||
if (response.Data != null)
|
||||
{
|
||||
var jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
|
||||
|
||||
if (role.Equals("doctor", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var doctor = JsonSerializer.Deserialize<Doctor>(response.Data.ToString(), jsonOptions);
|
||||
return new UserUpdateProfileModel
|
||||
{
|
||||
Id = doctor.Id,
|
||||
Name = doctor.Name,
|
||||
Email = doctor.Email,
|
||||
Password = doctor.Password,
|
||||
Description = doctor.Description
|
||||
};
|
||||
}
|
||||
|
||||
if (role.Equals("patient", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var patient = JsonSerializer.Deserialize<Patient>(response.Data.ToString(), jsonOptions);
|
||||
return new UserUpdateProfileModel
|
||||
{
|
||||
Id = patient.Id,
|
||||
Name = patient.Name,
|
||||
Email = patient.Email,
|
||||
Password = patient.Password
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return new UserUpdateProfileModel();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
namespace HealthcareManagerUiWebAssem.Services.UserSessionInformation;
|
||||
|
||||
public interface IUserSessionInformation
|
||||
{
|
||||
Task SaveUserInformationAsync(Guid id, string role, string username, string email, string token);
|
||||
|
||||
Task SetIdAsync(Guid id);
|
||||
Task SetRoleAsync(string role);
|
||||
Task SetUsernameAsync(string username);
|
||||
Task SetEmailAsync(string email);
|
||||
Task SetTokenAsync(string token);
|
||||
|
||||
Task<Guid> GetIdAsync();
|
||||
Task<string> GetRoleAsync();
|
||||
Task<string> GetUsernameAsync();
|
||||
Task<string> GetEmailAsync();
|
||||
Task<string> GetTokenAsync();
|
||||
|
||||
Task RefreshTokenAsync();
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using Blazored.LocalStorage;
|
||||
using HealthcareManagerUiWebAssem.Models;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace HealthcareManagerUiWebAssem.Services.UserSessionInformation;
|
||||
|
||||
public class UserSessionInformation : IUserSessionInformation
|
||||
{
|
||||
private readonly ILocalStorageService _localStorageService;
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly string _apiEndpoint;
|
||||
private readonly string _apiKey;
|
||||
|
||||
public UserSessionInformation(ILocalStorageService localStorageService, IHttpClientFactory httpClientFactory, IOptions<ApplicationSettings> settings)
|
||||
{
|
||||
_localStorageService = localStorageService;
|
||||
_httpClient = httpClientFactory.CreateClient();
|
||||
_apiEndpoint = settings.Value.ApiEndpoint;
|
||||
_apiKey = settings.Value.ApiKey ?? "NoKey";
|
||||
}
|
||||
|
||||
public async Task SaveUserInformationAsync(Guid id, string role, string username, string email, string token)
|
||||
{
|
||||
await SetIdAsync(id);
|
||||
await SetRoleAsync(role);
|
||||
await SetUsernameAsync(username);
|
||||
await SetEmailAsync(email);
|
||||
await SetTokenAsync(token);
|
||||
}
|
||||
|
||||
public async Task SetIdAsync(Guid id)
|
||||
{
|
||||
await _localStorageService.SetItemAsync("id", id);
|
||||
}
|
||||
|
||||
public async Task SetRoleAsync(string role)
|
||||
{
|
||||
await _localStorageService.SetItemAsync("role", role);
|
||||
}
|
||||
|
||||
public async Task SetUsernameAsync(string username)
|
||||
{
|
||||
await _localStorageService.SetItemAsync("username", username);
|
||||
}
|
||||
|
||||
public async Task SetEmailAsync(string email)
|
||||
{
|
||||
await _localStorageService.SetItemAsync("email", email);
|
||||
}
|
||||
|
||||
public async Task SetTokenAsync(string token)
|
||||
{
|
||||
await _localStorageService.SetItemAsync("jwtToken", token);
|
||||
}
|
||||
|
||||
public async Task<Guid> GetIdAsync()
|
||||
{
|
||||
return await _localStorageService.GetItemAsync<Guid>("id");
|
||||
}
|
||||
|
||||
public async Task<string> GetRoleAsync()
|
||||
{
|
||||
return await _localStorageService.GetItemAsync<string>("role");
|
||||
}
|
||||
|
||||
public async Task<string> GetUsernameAsync()
|
||||
{
|
||||
return await _localStorageService.GetItemAsync<string>("username");
|
||||
}
|
||||
|
||||
public async Task<string> GetEmailAsync()
|
||||
{
|
||||
return await _localStorageService.GetItemAsync<string>("email");
|
||||
}
|
||||
|
||||
public async Task<string> GetTokenAsync()
|
||||
{
|
||||
return await _localStorageService.GetItemAsync<string>("jwtToken");
|
||||
}
|
||||
|
||||
public async Task RefreshTokenAsync()
|
||||
{
|
||||
var currentToken = await GetTokenAsync();
|
||||
if (string.IsNullOrWhiteSpace(currentToken)) return;
|
||||
|
||||
var tokenRequestModel = new TokenRefreshModel(currentToken);
|
||||
var requestUri = $"{_apiEndpoint}/refresh_token";
|
||||
|
||||
var jsonContent = JsonContent.Create(tokenRequestModel);
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, requestUri)
|
||||
{
|
||||
Content = jsonContent
|
||||
};
|
||||
|
||||
request.Headers.Add("ApiKey", _apiKey);
|
||||
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
if (!response.IsSuccessStatusCode) return;
|
||||
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
var baseResponse = JsonSerializer.Deserialize<BaseResponse>(
|
||||
responseContent,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
|
||||
if (baseResponse == null || baseResponse.StatusCode < HttpStatusCodes.BadRequest) return;
|
||||
|
||||
var tokenObj = JsonSerializer.Deserialize<TokenRefreshModel>(baseResponse.Data.ToString(),
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
|
||||
if (tokenObj != null) await SetTokenAsync(tokenObj.Token);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace HealthcareManagerUiWebAssem;
|
||||
|
||||
public static class UserRoles
|
||||
{
|
||||
public const string Admin = "Admin";
|
||||
public const string Doctor = "Doctor";
|
||||
public const string Patient = "Patient";
|
||||
}
|
||||
+22
-2
@@ -1,10 +1,30 @@
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.AspNetCore.Components.WebAssembly.Http
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
|
||||
@using HealthcareManagerUiWebAssem.Layout
|
||||
@using HealthcareManagerUiWebAssem.Components
|
||||
@using HealthcareManagerUiWebAssem.Models
|
||||
@using HealthcareManagerUiWebAssem.Pages
|
||||
@using HealthcareManagerUiWebAssem.Entities
|
||||
@using HealthcareManagerUiWebAssem.Pages.Errors
|
||||
|
||||
@using System.Security.Claims
|
||||
@using System.ComponentModel.DataAnnotations;
|
||||
@using System.IdentityModel.Tokens.Jwt
|
||||
@using Microsoft.JSInterop
|
||||
@using HealthcareManagerUiWebAssem
|
||||
@using HealthcareManagerUiWebAssem.Components.Layout
|
||||
@using iText.Kernel.Pdf
|
||||
@using iText.Kernel.Pdf.Canvas.Parser
|
||||
@using iText.Kernel.Pdf.Canvas.Parser.Listener
|
||||
@using System.Text
|
||||
|
||||
@using HealthcareManagerUiWebAssem.Services.AdminUserManagement
|
||||
@using HealthcareManagerUiWebAssem.Services.Authentication
|
||||
@using HealthcareManagerUiWebAssem.Services.PatientManagement
|
||||
@using HealthcareManagerUiWebAssem.Services.DoctorManagement
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user