summary refs log tree commit diff
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2025-06-11 10:47:15 +0200
committerRory& <root@rory.gay>2025-06-11 10:47:15 +0200
commit6a830e02b1c1873c77907e7a88c2143a351adea4 (patch)
tree10761b7d0dccb5c8859ee13c4aafae385218345c
parentAdd initial demo (diff)
downloadnodejs-final-assignment-demo.tar.xz
Finish demo demo
-rw-r--r--flake.nix4
-rw-r--r--testFrontend/SafeNSound.Demo/Layout/NavMenu.razor5
-rw-r--r--testFrontend/SafeNSound.Demo/Pages/Admin.razor20
-rw-r--r--testFrontend/SafeNSound.Demo/Pages/Devices.razor5
-rw-r--r--testFrontend/SafeNSound.Demo/Pages/Monitor.razor59
-rw-r--r--testFrontend/SafeNSound.Demo/Pages/Tools.razor18
-rw-r--r--testFrontend/SafeNSound.Demo/Pages/User.razor46
-rw-r--r--testFrontend/SafeNSound.Sdk/SafeNSoundClient.cs23
8 files changed, 163 insertions, 17 deletions
diff --git a/flake.nix b/flake.nix

index 86ddc58..4245b6d 100644 --- a/flake.nix +++ b/flake.nix
@@ -80,7 +80,9 @@ devShell = pkgs.mkShell { buildInputs = with pkgs; [ mongodb-compass -# jetbrains.webstorm + # jetbrains.webstorm + # jetbrains.rider + # dotnet-sdk_9 nodejs nodePackages.prettier ]; diff --git a/testFrontend/SafeNSound.Demo/Layout/NavMenu.razor b/testFrontend/SafeNSound.Demo/Layout/NavMenu.razor
index 36501b1..a4d4522 100644 --- a/testFrontend/SafeNSound.Demo/Layout/NavMenu.razor +++ b/testFrontend/SafeNSound.Demo/Layout/NavMenu.razor
@@ -24,6 +24,11 @@ <span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Admin </NavLink> </div> + <div class="nav-item px-3"> + <NavLink class="nav-link" href="/Tools" Match="NavLinkMatch.All"> + <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Tools + </NavLink> + </div> </nav> </div> diff --git a/testFrontend/SafeNSound.Demo/Pages/Admin.razor b/testFrontend/SafeNSound.Demo/Pages/Admin.razor
index 0dcb1a3..952910d 100644 --- a/testFrontend/SafeNSound.Demo/Pages/Admin.razor +++ b/testFrontend/SafeNSound.Demo/Pages/Admin.razor
@@ -5,8 +5,24 @@ return Task.CompletedTask; })">Manage devices </LinkButton> +<LinkButton OnClick="@GetUserList">Get all users</LinkButton> + +<pre> + @foreach (var user in Enumerable.Reverse(UserList)) { + @(user + "\n") + } +</pre> @code { - - + + List<string> UserList { get; set; } = []; + + private async Task GetUserList() { + UserList.Clear(); + await foreach (var userId in App.AdminClient.GetAllUserIdsEnumerable()) { + UserList.Add(userId); + StateHasChanged(); + } + } + } \ No newline at end of file diff --git a/testFrontend/SafeNSound.Demo/Pages/Devices.razor b/testFrontend/SafeNSound.Demo/Pages/Devices.razor
index 5f90132..695c138 100644 --- a/testFrontend/SafeNSound.Demo/Pages/Devices.razor +++ b/testFrontend/SafeNSound.Demo/Pages/Devices.razor
@@ -5,6 +5,7 @@ <hr/> <LinkButton OnClick="@CreateDevice">Create new device</LinkButton> <LinkButton OnClick="@ReloadDevices">Reload Devices</LinkButton> + <LinkButton OnClick="@Logout">Log out</LinkButton> <LinkButton OnClick="@DeleteAccount">Delete account</LinkButton> @foreach (var device in CurrentDevices) { <div class="device-card"> @@ -77,5 +78,9 @@ private async Task DeleteAccount() { await Client.DeleteAccount(Auth); } + + private async Task Logout() { + await Client.LogOut(); + } } \ No newline at end of file diff --git a/testFrontend/SafeNSound.Demo/Pages/Monitor.razor b/testFrontend/SafeNSound.Demo/Pages/Monitor.razor
index d78d60a..f948b69 100644 --- a/testFrontend/SafeNSound.Demo/Pages/Monitor.razor +++ b/testFrontend/SafeNSound.Demo/Pages/Monitor.razor
@@ -6,16 +6,44 @@ return Task.CompletedTask; })">Manage devices </LinkButton> - + @foreach (var user in AssignedUsers) { - <p>Assigned user @user + <p> + <span>Assigned user @user</span> @if (Alarms.ContainsKey(user)) { - <pre>@Alarms[user].ToJson(indent: false)</pre> + <span>&#x1F514;</span> + <br/> + <i>@Alarms[user].Reason (@Alarms[user].CreatedAt)</i> + } + else { + <span>&#x1F515;</span> } - </p> + </p> + <LinkButton OnClick="() => { _ = ManageUser(user); return Task.CompletedTask; }">Manage</LinkButton> } } +@if (!string.IsNullOrWhiteSpace(manageUserId)) { + <ModalWindow Title="Manage user"> + <span>ID: @manageUserId</span><br/> + @if (Alarms.ContainsKey(manageUserId)) { + <span><b>User has alarm!</b></span> + <br/> + <i>@Alarms[manageUserId].Reason at @Alarms[manageUserId].CreatedAt.ToLocalTime()</i> + <LinkButton>Clear</LinkButton> + <br/> + } + <span>Budget: @manageUserBudget?.Amount EUR</span><br/> + <span>Add money: </span> + <FancyTextBox Value="@manageUserNewBudget?.Amount.ToString()" + ValueChanged="s => manageUserNewBudget.Amount = double.Parse(s)"/> + <span>, reason:</span> + <FancyTextBox @bind-Value="@manageUserNewBudget.Reason"/> + <LinkButton OnClick="@UpdateUserBudget">Add budget + </LinkButton> + </ModalWindow> +} + @code { bool _isInitialized = false; @@ -28,4 +56,27 @@ _isInitialized = true; } + string? manageUserId { get; set; } + BudgetWithHistory? manageUserBudget { get; set; } + BudgetHistoryEntry manageUserNewBudget { get; set; } + + private async Task ManageUser(string user) { + manageUserId = user; + manageUserBudget = await App.MonitorClient.GetBudget(manageUserId); + manageUserBudget ??= new BudgetWithHistory { + Amount = 0, + History = [] + }; + manageUserNewBudget = new() { + Venue = App.MonitorAuth.Username + }; + + StateHasChanged(); + } + + private async Task UpdateUserBudget() { + await App.MonitorClient.AddBudget(manageUserId, manageUserNewBudget); + await ManageUser(manageUserId); + } + } \ No newline at end of file diff --git a/testFrontend/SafeNSound.Demo/Pages/Tools.razor b/testFrontend/SafeNSound.Demo/Pages/Tools.razor new file mode 100644
index 0000000..f67f7ef --- /dev/null +++ b/testFrontend/SafeNSound.Demo/Pages/Tools.razor
@@ -0,0 +1,18 @@ +@page "/Tools" +<h3>Tools</h3> +<LinkButton OnClick="@MakeSuperAdmin">Assign more users to monitor</LinkButton> + +@code { + + private async Task MakeSuperAdmin() { + await foreach (var user in App.AdminClient.GetAllUserIdsEnumerable()) { + try { + await App.MonitorClient.AddAssignedUser(user); + } + catch (Exception ex) { + Console.WriteLine($"Failed to assign user {user}: {ex.Message}"); + } + } + } + +} \ No newline at end of file diff --git a/testFrontend/SafeNSound.Demo/Pages/User.razor b/testFrontend/SafeNSound.Demo/Pages/User.razor
index cdffbe3..cfbfa53 100644 --- a/testFrontend/SafeNSound.Demo/Pages/User.razor +++ b/testFrontend/SafeNSound.Demo/Pages/User.razor
@@ -16,6 +16,30 @@ <LinkButton Color="#FF0000" OnClick="@(() => RaiseAlarm(null))">Clear</LinkButton> </span> <br/> + + <span>Budget: @Math.Round(Budget.Amount, 2)</span><br/> + + <span> + Spend + <FancyTextBox Value="@ToSpend.Amount.ToString()" ValueChanged="s => ToSpend.Amount = double.Parse(s)"/> + EUR at + <FancyTextBox @bind-Value="@ToSpend.Venue"/> + for + <FancyTextBox @bind-Value="@ToSpend.Reason"/> + <LinkButton OnClick="@(() => SpendBudget(ToSpend))">Spend</LinkButton> + </span><br/> + + <i>Spend history is comming soon!</i> + @* oops, forgot to use the DTO for getting own budget... *@ + @* @foreach(var history in Budget.History) { *@ + @* <div> *@ + @* <span>@history.CreatedAt!.Value.ToLocalTime()</span> - *@ + @* <span>@history.Amount EUR</span> - *@ + @* <span>@history.Reason</span> - *@ + @* <span>@history.Venue</span> *@ + @* </div> *@ + @* } *@ + } @if (Alarm != null) { @@ -38,16 +62,22 @@ private WhoAmI WhoAmI { get; set; } = null!; private AlarmDto? Alarm { get; set; } = null!; + private BudgetHistoryEntry ToSpend { get; set; } = new() { + Amount = 0, + Reason = string.Empty, + Venue = string.Empty + }; protected override async Task OnInitializedAsync() { WhoAmI = await App.UserClient.WhoAmI(); - _isInitialized = true; _ = PollAlarm(); + Budget = await App.UserClient.GetBudget(); NavigationManager.LocationChanged += (sender, args) => { if (args.Location != NavigationManager.Uri) { - _running = false; // Stop polling when navigating away + _running = false; } }; + _isInitialized = true; } private async Task PollAlarm() { @@ -57,11 +87,19 @@ Alarm = newAlarm; StateHasChanged(); } + + var newBudget = await App.UserClient.GetBudget(); + if (Math.Abs(Budget.Amount - newBudget.Amount) > 0.01) { + Budget = newBudget; + StateHasChanged(); + } await Task.Delay(1000); } } + public CurrentBalance Budget { get; set; } + private async Task RaiseAlarm(string? reason) { if (string.IsNullOrWhiteSpace(reason)) await App.UserClient.DeleteAlarm(); @@ -80,4 +118,8 @@ ~User() => ReleaseUnmanagedResources(); + private async Task SpendBudget(BudgetHistoryEntry toSpend) { + await App.UserClient.SpendBudget(toSpend); + } + } \ No newline at end of file diff --git a/testFrontend/SafeNSound.Sdk/SafeNSoundClient.cs b/testFrontend/SafeNSound.Sdk/SafeNSoundClient.cs
index 3cd5d5c..b5bca12 100644 --- a/testFrontend/SafeNSound.Sdk/SafeNSoundClient.cs +++ b/testFrontend/SafeNSound.Sdk/SafeNSoundClient.cs
@@ -66,12 +66,14 @@ public class SafeNSoundClient(SafeNSoundConfiguration config, string accessToken res.EnsureSuccessStatusCode(); } - public async Task<BudgetWithHistory> GetBudget(string userId = "@me") { - var res = await HttpClient.GetAsync( - userId == "@me" - ? $"/budget/@me" - : $"/user/{userId}/budget" - ); + public async Task<CurrentBalance> GetBudget() { + var res = await HttpClient.GetAsync($"/budget/@me"); + res.EnsureSuccessStatusCode(); + return (await res.Content.ReadFromJsonAsync<CurrentBalance>())!; + } + + public async Task<BudgetWithHistory> GetBudget(string userId) { + var res = await HttpClient.GetAsync($"/user/{userId}/budget"); res.EnsureSuccessStatusCode(); return (await res.Content.ReadFromJsonAsync<BudgetWithHistory>())!; } @@ -143,7 +145,7 @@ public class SafeNSoundClient(SafeNSoundConfiguration config, string accessToken public class AlarmDto { [JsonPropertyName("_id")] public string? Id { get; set; } - + [JsonPropertyName("reason")] public required string Reason { get; set; } @@ -165,8 +167,13 @@ public class DeviceDto { public DateTime LastSeen { get; set; } } +public class CurrentBalance { + [JsonPropertyName("currentBalance")] + public double Amount { get; set; } +} + public class BudgetWithHistory { - [JsonPropertyName("budget")] + [JsonPropertyName("balance")] public double Amount { get; set; } [JsonPropertyName("history")]