summary refs log tree commit diff
path: root/testFrontend/SafeNSound.Demo/Pages
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2025-06-11 07:38:05 +0200
committerRory& <root@rory.gay>2025-06-11 07:41:37 +0200
commitd6c791af681fdcc84c5a79a7b30d35db9231e404 (patch)
tree73816b7eda8ff52e7b8b28c9ab68960554d252d3 /testFrontend/SafeNSound.Demo/Pages
parentAPI docs (diff)
downloadnodejs-final-assignment-d6c791af681fdcc84c5a79a7b30d35db9231e404.tar.xz
Add initial demo
Diffstat (limited to 'testFrontend/SafeNSound.Demo/Pages')
-rw-r--r--testFrontend/SafeNSound.Demo/Pages/Admin.razor12
-rw-r--r--testFrontend/SafeNSound.Demo/Pages/Auth.razor239
-rw-r--r--testFrontend/SafeNSound.Demo/Pages/Devices.razor81
-rw-r--r--testFrontend/SafeNSound.Demo/Pages/Home.razor50
-rw-r--r--testFrontend/SafeNSound.Demo/Pages/Monitor.razor31
-rw-r--r--testFrontend/SafeNSound.Demo/Pages/User.razor83
6 files changed, 496 insertions, 0 deletions
diff --git a/testFrontend/SafeNSound.Demo/Pages/Admin.razor b/testFrontend/SafeNSound.Demo/Pages/Admin.razor
new file mode 100644

index 0000000..0dcb1a3 --- /dev/null +++ b/testFrontend/SafeNSound.Demo/Pages/Admin.razor
@@ -0,0 +1,12 @@ +@page "/Admin" +<h3>Admin</h3> +<LinkButton OnClick="@(() => { + NavigationManager.NavigateTo("/Admin/Devices"); + return Task.CompletedTask; + })">Manage devices +</LinkButton> + +@code { + + +} \ No newline at end of file diff --git a/testFrontend/SafeNSound.Demo/Pages/Auth.razor b/testFrontend/SafeNSound.Demo/Pages/Auth.razor new file mode 100644
index 0000000..58fe6d6 --- /dev/null +++ b/testFrontend/SafeNSound.Demo/Pages/Auth.razor
@@ -0,0 +1,239 @@ +@* @page "/Auth" *@ +@* *@ +@* <h1>Auth</h1> *@ +@* <u>User:</u><br/> *@ +@* <span>Username (L?, R): </span> *@ +@* <FancyTextBox @bind-Value="@AuthData.Username"/><br/> *@ +@* <span>Email (L? R): </span> *@ +@* <FancyTextBox @bind-Value="@AuthData.Email"/><br/> *@ +@* <span>Password (L, R): </span> *@ +@* <FancyTextBox @bind-Value="@AuthData.Password" IsPassword="true"/><br/> *@ +@* <span>Type (R): </span> *@ +@* <FancyTextBox @bind-Value="@AuthData.UserType"/><span> (one of user|monitor|admin)</span><br/> *@ +@* <LinkButton OnClick="@Randomise">Randomise</LinkButton> *@ +@* <LinkButton OnClick="@Register">Register</LinkButton> *@ +@* <LinkButton OnClick="@Login">Login</LinkButton> *@ +@* <LinkButton OnClick="@WhoAmI">Who Am I</LinkButton> *@ +@* <LinkButton OnClick="@Delete">Delete</LinkButton> *@ +@* <LinkButton OnClick="@MakeFullAdmin">Register superadmin</LinkButton> *@ +@* <br/><br/> *@ +@* *@ +@* <u>Monitor:</u><br/> *@ +@* <span>User ID: </span> *@ +@* <FancyTextBox @bind-Value="@TargetUserId"/><br/> *@ +@* <LinkButton OnClick="@GetAssignedUsers">Get</LinkButton> *@ +@* <LinkButton OnClick="@AddAssignedUser">Add</LinkButton> *@ +@* <LinkButton OnClick="@RemoveAssignedUser">Remove</LinkButton> *@ +@* <br/><br/> *@ +@* *@ +@* <u>Devices:</u><br/> *@ +@* @if (CurrentDevice is not null) { *@ +@* <span>Device ID: @CurrentDevice.Id</span><br/> *@ +@* <span>Log in date: @CurrentDevice.CreatedAt</span><br/> *@ +@* <span>Last seen: @CurrentDevice.LastSeen</span><br/> *@ +@* <span>Device name: </span> *@ +@* <FancyTextBox @bind-Value="@CurrentDevice.Name"/><br/> *@ +@* <LinkButton OnClick="@GetDevice">Get</LinkButton> *@ +@* <LinkButton OnClick="@UpdateDevice">Update</LinkButton> *@ +@* <LinkButton OnClick="@DeleteDevice">Delete</LinkButton> *@ +@* <LinkButton OnClick="@GetAllDevices">Get all</LinkButton> *@ +@* } *@ +@* *@ +@* @if (Exception != null) { *@ +@* <div class="alert alert-danger"> *@ +@* <strong>Error:</strong><br/> *@ +@* <pre> *@ +@* @Exception *@ +@* </pre> *@ +@* </div> *@ +@* } *@ +@* *@ +@* @if (Result != null) { *@ +@* <div class="alert alert-success"> *@ +@* <strong>Result:</strong><br/> *@ +@* <pre> *@ +@* @Result.ToJson(indent: true) *@ +@* </pre> *@ +@* </div> *@ +@* } *@ +@* *@ +@* @code { *@ +@* *@ +@* private RegisterDto AuthData { get; set; } = new() { *@ +@* Username = string.Empty, *@ +@* UserType = string.Empty, *@ +@* Email = String.Empty, *@ +@* Password = string.Empty *@ +@* }; *@ +@* *@ +@* private DeviceDto? CurrentDevice { get; set; } *@ +@* *@ +@* private string TargetUserId { get; set; } = string.Empty; *@ +@* *@ +@* private Exception? Exception { get; set; } *@ +@* private object? Result { get; set; } *@ +@* *@ +@* private async Task Randomise() { *@ +@* AuthData.Username = Guid.NewGuid().ToString(); *@ +@* AuthData.Email = Guid.NewGuid() + "@example.com"; *@ +@* AuthData.Password = Guid.NewGuid().ToString(); *@ +@* AuthData.UserType = Random.Shared.GetItems(["user", "monitor", "admin"], 1)[0]; *@ +@* StateHasChanged(); *@ +@* } *@ +@* *@ +@* private async Task Register() { *@ +@* Result = null; *@ +@* Exception = null; *@ +@* try { *@ +@* await Authentication.Register(new() { *@ +@* Username = AuthData.Username, *@ +@* Password = AuthData.Password, *@ +@* Email = AuthData.Email, *@ +@* UserType = AuthData.UserType *@ +@* }); *@ +@* } *@ +@* catch (Exception ex) { *@ +@* Exception = ex; *@ +@* } *@ +@* *@ +@* StateHasChanged(); *@ +@* } *@ +@* *@ +@* private async Task Login() { *@ +@* Result = null; *@ +@* Exception = null; *@ +@* try { *@ +@* AuthResult result; *@ +@* Result = result = await Authentication.Login(new() { *@ +@* Username = AuthData.Username, *@ +@* Password = AuthData.Password, *@ +@* Email = AuthData.Email *@ +@* }); *@ +@* App.Client = new SafeNSoundClient(Config, result.AccessToken); *@ +@* CurrentDevice = await App.Client.GetDevice( *@ +@* (await App.Client.WhoAmI()).DeviceId *@ +@* ); *@ +@* } *@ +@* catch (Exception ex) { *@ +@* Exception = ex; *@ +@* } *@ +@* *@ +@* StateHasChanged(); *@ +@* } *@ +@* *@ +@* private async Task Delete() { *@ +@* Result = null; *@ +@* Exception = null; *@ +@* try { *@ +@* await Authentication.Delete(new() { *@ +@* Username = AuthData.Username, *@ +@* Password = AuthData.Password, *@ +@* Email = AuthData.Email *@ +@* }); *@ +@* } *@ +@* catch (Exception ex) { *@ +@* Exception = ex; *@ +@* } *@ +@* *@ +@* StateHasChanged(); *@ +@* } *@ +@* *@ +@* private async Task WhoAmI() { *@ +@* Result = null; *@ +@* Exception = null; *@ +@* try { *@ +@* Result = await App.Client!.WhoAmI(); *@ +@* } *@ +@* catch (Exception ex) { *@ +@* Exception = ex; *@ +@* } *@ +@* *@ +@* StateHasChanged(); *@ +@* } *@ +@* *@ +@* private async Task GetAssignedUsers() { *@ +@* Result = null; *@ +@* Exception = null; *@ +@* try { *@ +@* Result = await App.Client!.GetAssignedUsers(); *@ +@* } *@ +@* catch (Exception ex) { *@ +@* Exception = ex; *@ +@* } *@ +@* *@ +@* StateHasChanged(); *@ +@* } *@ +@* *@ +@* private async Task AddAssignedUser() { *@ +@* Result = null; *@ +@* Exception = null; *@ +@* try { *@ +@* await App.Client!.AddAssignedUser(TargetUserId); *@ +@* await GetAssignedUsers(); *@ +@* } *@ +@* catch (Exception ex) { *@ +@* Exception = ex; *@ +@* } *@ +@* *@ +@* StateHasChanged(); *@ +@* } *@ +@* *@ +@* private async Task RemoveAssignedUser() { *@ +@* Result = null; *@ +@* Exception = null; *@ +@* try { *@ +@* await App.Client!.RemoveAssignedUser(TargetUserId); *@ +@* await GetAssignedUsers(); *@ +@* } *@ +@* catch (Exception ex) { *@ +@* Exception = ex; *@ +@* } *@ +@* *@ +@* StateHasChanged(); *@ +@* } *@ +@* *@ +@* private async Task MakeFullAdmin() { *@ +@* Result = null; *@ +@* Exception = null; *@ +@* try { *@ +@* AuthResult result; *@ +@* RegisterDto auth = new() { *@ +@* Username = Guid.NewGuid().ToString(), *@ +@* Password = Guid.NewGuid().ToString(), *@ +@* Email = Guid.NewGuid() + "@example.com", *@ +@* UserType = "admin" *@ +@* }; *@ +@* await Authentication.Register(auth); *@ +@* Result = result = await Authentication.Login(auth); *@ +@* App.Client = new SafeNSoundClient(Config, result.AccessToken); *@ +@* await App.Client.MonitorAllUsers(); *@ +@* } *@ +@* catch (Exception ex) { *@ +@* Exception = ex; *@ +@* } *@ +@* *@ +@* StateHasChanged(); *@ +@* } *@ +@* *@ +@* private async Task GetDevice() { *@ +@* Result = CurrentDevice = await App.Client!.GetDevice(CurrentDevice!.Id!); *@ +@* StateHasChanged(); *@ +@* } *@ +@* *@ +@* private async Task UpdateDevice() { *@ +@* await App.Client!.UpdateDevice(CurrentDevice!.Id!, new() { *@ +@* Name = CurrentDevice.Name *@ +@* }); *@ +@* await GetDevice(); *@ +@* } *@ +@* *@ +@* private async Task DeleteDevice() { *@ +@* await App.Client!.DeleteDevice(CurrentDevice!.Id!); *@ +@* } *@ +@* *@ +@* private async Task GetAllDevices() { *@ +@* Result = await App.Client!.GetDevices(); *@ +@* StateHasChanged(); *@ +@* } *@ +@* *@ +@* } *@ \ No newline at end of file diff --git a/testFrontend/SafeNSound.Demo/Pages/Devices.razor b/testFrontend/SafeNSound.Demo/Pages/Devices.razor new file mode 100644
index 0000000..5f90132 --- /dev/null +++ b/testFrontend/SafeNSound.Demo/Pages/Devices.razor
@@ -0,0 +1,81 @@ +@page "/{UserType}/Devices" + +@if (_isInitialized) { + <h3>Devices (@WhoAmI.UserId#@WhoAmI.DeviceId[..5])</h3> + <hr/> + <LinkButton OnClick="@CreateDevice">Create new device</LinkButton> + <LinkButton OnClick="@ReloadDevices">Reload Devices</LinkButton> + <LinkButton OnClick="@DeleteAccount">Delete account</LinkButton> + @foreach (var device in CurrentDevices) { + <div class="device-card"> + <h4>ID: @device.Id + @if (device.Id == WhoAmI.DeviceId) { + <span> (current)</span> + } + </h4> + <span>Name: @device.Name</span><br/> + <span>First seen: @device.CreatedAt.ToLocalTime()</span><br/> + <span>Last seen: @device.LastSeen.ToLocalTime()</span><br/> + <LinkButton OnClick="@(() => DeleteDevice(device))">Log out</LinkButton> + </div> + } +} + +@code { + private bool _isInitialized; + + [Parameter] + public string UserType { get; set; } = string.Empty; + + private SafeNSoundClient Client { get; set; } = null!; + private RegisterDto Auth { get; set; } = null!; + private WhoAmI WhoAmI { get; set; } = null!; + private List<DeviceDto> CurrentDevices { get; set; } = null!; + + protected override async Task OnInitializedAsync() { + if (UserType is not "User" and not "Monitor" and not "Admin") { + NavigationManager.NavigateTo("/"); + return; + } + + Client = UserType switch { + "User" => App.UserClient, + "Monitor" => App.MonitorClient, + "Admin" => App.AdminClient, + _ => throw new InvalidOperationException("Invalid UserType") + }; + + Auth = UserType switch { + "User" => App.UserAuth, + "Monitor" => App.MonitorAuth, + "Admin" => App.AdminAuth, + _ => throw new InvalidOperationException("Invalid UserType") + }; + + WhoAmI = await Client.WhoAmI(); + + await ReloadDevices(); + + _isInitialized = true; + } + + private async Task CreateDevice() { + await Authentication.Login(Auth); + await ReloadDevices(); + } + + private async Task DeleteDevice(DeviceDto device) { + await Client.DeleteDevice(device.Id!); + await ReloadDevices(); + } + + private async Task ReloadDevices() { + CurrentDevices = await Client.GetDevices(); + StateHasChanged(); + } + + private async Task DeleteAccount() { + await Client.DeleteAccount(Auth); + } + +} \ No newline at end of file diff --git a/testFrontend/SafeNSound.Demo/Pages/Home.razor b/testFrontend/SafeNSound.Demo/Pages/Home.razor new file mode 100644
index 0000000..ae35214 --- /dev/null +++ b/testFrontend/SafeNSound.Demo/Pages/Home.razor
@@ -0,0 +1,50 @@ +@page "/" +@using System.Text.Json.Nodes +@implements IDisposable + +<PageTitle>Home</PageTitle> + +<h1>SafeNSound</h1> + +<p>Welcome to SafeNSound! This application is purely a demonstration of the basic concepts in SafeNSound!</p> +<p>Here be dragons!</p> + +<pre> + @serverStatus?.ToJson(indent: true) +</pre> + +@code { + + private bool running = true; + private JsonObject? serverStatus { get; set; } + + protected override async Task OnInitializedAsync() { + _ = PollServerStatus(); + } + + private async Task PollServerStatus() { + while (running) { + try { + serverStatus = await App.UserClient.HttpClient.GetFromJsonAsync<JsonObject>("/status"); + } + catch (Exception ex) { + serverStatus = new JsonObject { + ["error"] = ex.Message + }; + } + StateHasChanged(); + await Task.Delay(1000); + } + } + + private void ReleaseUnmanagedResources() { + running = false; + } + + public void Dispose() { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + ~Home() => ReleaseUnmanagedResources(); +} \ No newline at end of file diff --git a/testFrontend/SafeNSound.Demo/Pages/Monitor.razor b/testFrontend/SafeNSound.Demo/Pages/Monitor.razor new file mode 100644
index 0000000..d78d60a --- /dev/null +++ b/testFrontend/SafeNSound.Demo/Pages/Monitor.razor
@@ -0,0 +1,31 @@ +@page "/Monitor" +@if (_isInitialized) { + <h3>User</h3> + <LinkButton OnClick="@(() => { + NavigationManager.NavigateTo("/User/Devices"); + return Task.CompletedTask; + })">Manage devices + </LinkButton> + + @foreach (var user in AssignedUsers) { + <p>Assigned user @user + @if (Alarms.ContainsKey(user)) { + <pre>@Alarms[user].ToJson(indent: false)</pre> + } + </p> + } +} + +@code { + + bool _isInitialized = false; + Dictionary<string, AlarmDto> Alarms { get; set; } + List<string> AssignedUsers { get; set; } + + protected override async Task OnInitializedAsync() { + Alarms = await App.MonitorClient.GetAllAlarms(); + AssignedUsers = await App.MonitorClient.GetAssignedUsers(); + _isInitialized = true; + } + +} \ No newline at end of file diff --git a/testFrontend/SafeNSound.Demo/Pages/User.razor b/testFrontend/SafeNSound.Demo/Pages/User.razor new file mode 100644
index 0000000..cdffbe3 --- /dev/null +++ b/testFrontend/SafeNSound.Demo/Pages/User.razor
@@ -0,0 +1,83 @@ +@page "/User" +@implements IDisposable +@if (_isInitialized) { + <h3>User (@WhoAmI.UserId#@WhoAmI.DeviceId[..5])</h3> + <LinkButton OnClick="@(() => { + NavigationManager.NavigateTo("/User/Devices"); + return Task.CompletedTask; + })">Manage devices + </LinkButton> + <hr/> + + <span> + Raise alarm: + <LinkButton OnClick="@(() => RaiseAlarm("fall"))">Fall</LinkButton> + <LinkButton OnClick="@(() => RaiseAlarm("toilet"))">Toilet</LinkButton> + <LinkButton Color="#FF0000" OnClick="@(() => RaiseAlarm(null))">Clear</LinkButton> + </span> + <br/> +} + +@if (Alarm != null) { + <ModalWindow Title="User alarm"> + <div class="modal-body"> + <span><strong>Alarm ID:</strong> @Alarm.Id</span><br/> + <span><strong>Reason:</strong> @Alarm.Reason</span><br/> + <span><strong>Raised at:</strong> @Alarm.CreatedAt.ToLocalTime()</span><br/> + </div> + <div class="modal-footer"> + <LinkButton Color="#FF0000" OnClick="@(() => RaiseAlarm(null))">Clear Alarm</LinkButton> + </div> + </ModalWindow> +} + +@code { + + bool _isInitialized = false; + bool _running = true; + + private WhoAmI WhoAmI { get; set; } = null!; + private AlarmDto? Alarm { get; set; } = null!; + + protected override async Task OnInitializedAsync() { + WhoAmI = await App.UserClient.WhoAmI(); + _isInitialized = true; + _ = PollAlarm(); + NavigationManager.LocationChanged += (sender, args) => { + if (args.Location != NavigationManager.Uri) { + _running = false; // Stop polling when navigating away + } + }; + } + + private async Task PollAlarm() { + while (_running) { + var newAlarm = await App.UserClient.GetAlarm(); + if (Alarm?.Id != newAlarm?.Id) { + Alarm = newAlarm; + StateHasChanged(); + } + + await Task.Delay(1000); + } + } + + private async Task RaiseAlarm(string? reason) { + if (string.IsNullOrWhiteSpace(reason)) + await App.UserClient.DeleteAlarm(); + else + await App.UserClient.SetAlarm(new() { Reason = reason }); + } + + private void ReleaseUnmanagedResources() { + _running = false; + } + + public void Dispose() { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + ~User() => ReleaseUnmanagedResources(); + +} \ No newline at end of file