From 8313d8e85a0d8d294dbac2a4f53719a06510318f Mon Sep 17 00:00:00 2001 From: Ross Miller Date: Mon, 2 Mar 2026 16:00:18 +0000 Subject: [PATCH] WIP --- BudgetApp/BudgetApp.csproj | 1 + BudgetApp/Components/Layout/NavMenu.razor | 2 +- BudgetApp/Components/Pages/About.razor | 4 +- BudgetApp/Components/Pages/Help.razor | 1 + BudgetApp/Components/Pages/Login.razor | 8 +- BudgetApp/Components/Pages/Register.razor | 2 +- BudgetApp/Components/Pages/Settings.razor | 111 +++++++++++++++++++++- BudgetApp/budgetapp.db-shm | Bin 32768 -> 32768 bytes BudgetApp/budgetapp.db-wal | Bin 1491472 -> 1569752 bytes 9 files changed, 122 insertions(+), 7 deletions(-) diff --git a/BudgetApp/BudgetApp.csproj b/BudgetApp/BudgetApp.csproj index 8133746..f2e15ab 100644 --- a/BudgetApp/BudgetApp.csproj +++ b/BudgetApp/BudgetApp.csproj @@ -5,6 +5,7 @@ enable enable true + 0.1.0 diff --git a/BudgetApp/Components/Layout/NavMenu.razor b/BudgetApp/Components/Layout/NavMenu.razor index 12ae9b9..7a17eb9 100644 --- a/BudgetApp/Components/Layout/NavMenu.razor +++ b/BudgetApp/Components/Layout/NavMenu.razor @@ -7,7 +7,7 @@ diff --git a/BudgetApp/Components/Pages/About.razor b/BudgetApp/Components/Pages/About.razor index 9d5dacf..2f4bc8e 100644 --- a/BudgetApp/Components/Pages/About.razor +++ b/BudgetApp/Components/Pages/About.razor @@ -2,9 +2,9 @@ @layout AuthLayout @inject IConfiguration Configuration -Budgeteer — About +Budgeteer Beta — About -

💰 Budgeteer

+

💰 Budgeteer beta

A personal finance dashboard built with Blazor Server. diff --git a/BudgetApp/Components/Pages/Help.razor b/BudgetApp/Components/Pages/Help.razor index bfb7d6c..1be8e61 100644 --- a/BudgetApp/Components/Pages/Help.razor +++ b/BudgetApp/Components/Pages/Help.razor @@ -133,6 +133,7 @@

The Settings page provides account management options:

diff --git a/BudgetApp/Components/Pages/Login.razor b/BudgetApp/Components/Pages/Login.razor index d30c84f..6b94e9a 100644 --- a/BudgetApp/Components/Pages/Login.razor +++ b/BudgetApp/Components/Pages/Login.razor @@ -6,9 +6,13 @@ @inject NavigationManager Navigation @inject IAuthenticationSchemeProvider SchemeProvider -Budgeteer — Sign In +Budgeteer Beta — Sign In -

Sign In

+
+ 💰 +

Budgeteer beta

+

Sign in to your account

+
diff --git a/BudgetApp/Components/Pages/Register.razor b/BudgetApp/Components/Pages/Register.razor index a6dc449..77f19a5 100644 --- a/BudgetApp/Components/Pages/Register.razor +++ b/BudgetApp/Components/Pages/Register.razor @@ -5,7 +5,7 @@ @inject SignInManager SignInManager @inject NavigationManager Navigation -Budgeteer — Register +Budgeteer Beta — Register

Create Account

diff --git a/BudgetApp/Components/Pages/Settings.razor b/BudgetApp/Components/Pages/Settings.razor index 9f60069..1e7eee9 100644 --- a/BudgetApp/Components/Pages/Settings.razor +++ b/BudgetApp/Components/Pages/Settings.razor @@ -7,11 +7,15 @@ @inject AuthenticationStateProvider AuthStateProvider @inject ApplicationDbContext DbContext @inject NavigationManager Navigation +@inject AppState AppState @rendermode InteractiveServer Settings -

Settings

+

+ Settings + beta +

@* ── Change Password ── *@
@@ -52,6 +56,49 @@
+@* ── Reset Data ── *@ +
+
+
Reset Data
+
+
+

+ This will permanently delete all your accounts, transactions and categories, giving you a clean slate. Your user account and password will be kept. +

+ + @if (resetSuccess) + { +
All data has been reset successfully.
+ } + + @if (!string.IsNullOrEmpty(resetError)) + { +
@resetError
+ } + + @if (!confirmingReset) + { + + } + else + { +
+ Are you sure? This cannot be undone. Type I UNDERSTAND and enter your password to confirm. +
+
+ + +
+ + +
+
+ } +
+
+ @* ── Delete Account ── *@
@@ -88,6 +135,9 @@
@code { + private static readonly string appVersion = + typeof(Program).Assembly.GetName().Version?.ToString(3) ?? "0.0.0"; + private ChangePasswordForm passwordForm = new(); private bool passwordSuccess; private string? passwordError; @@ -96,6 +146,12 @@ private string deletePassword = string.Empty; private string? deleteError; + private bool confirmingReset; + private string resetConfirmation = string.Empty; + private string resetPassword = string.Empty; + private string? resetError; + private bool resetSuccess; + private async Task ChangePassword() { passwordSuccess = false; @@ -166,6 +222,59 @@ deleteError = null; } + private async Task ResetData() + { + resetError = null; + resetSuccess = false; + + var authState = await AuthStateProvider.GetAuthenticationStateAsync(); + var userIdClaim = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value; + if (userIdClaim is null) return; + + var user = await UserManager.FindByIdAsync(userIdClaim); + if (user is null) return; + + if (!string.Equals(resetConfirmation?.Trim(), "I UNDERSTAND", StringComparison.Ordinal)) + { + resetError = "Please type 'I UNDERSTAND' exactly to confirm."; + return; + } + + var passwordValid = await UserManager.CheckPasswordAsync(user, resetPassword); + if (!passwordValid) + { + resetError = "Incorrect password."; + return; + } + + await DbContext.Transactions + .Where(t => t.Account.UserId == userIdClaim) + .ExecuteDeleteAsync(); + + await DbContext.Accounts + .Where(a => a.UserId == userIdClaim) + .ExecuteDeleteAsync(); + + await DbContext.Categories + .Where(c => c.UserId == userIdClaim) + .ExecuteDeleteAsync(); + + confirmingReset = false; + resetConfirmation = string.Empty; + resetPassword = string.Empty; + resetSuccess = true; + + AppState.NotifyAccountsChanged(); + } + + private void CancelReset() + { + confirmingReset = false; + resetConfirmation = string.Empty; + resetPassword = string.Empty; + resetError = null; + } + private sealed class ChangePasswordForm { [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Current password is required")] diff --git a/BudgetApp/budgetapp.db-shm b/BudgetApp/budgetapp.db-shm index 4b1e3faef8a2fe7ff1af377be924fb7f5845168a..7dd160e14d0a9fa1bb0c12f2442099815ae1c2f6 100644 GIT binary patch delta 334 zcmZo@U}|V!s+V}A%K!pQK+MR%AW#b=l!0vd8!U$xyi@3D*)WSo2;zKyY9^2db5oI4l}Fq~(QVJrqpoMxQ7 lFR6mD5Xd~s2-LofdGfA|9>yXV|Bx`NJ7Xzh1(2`G2mtw*Za4q{ delta 247 zcmZo@U}|V!s+V}A%K!pQK+MR%Adm$ll!5Fjo8GH?m)h@d*E)wzZ<(rz zq^bv*4FVu@|04m2DA3xBjg9MBH`lO5SWlk7<}>++5cJuf5&97D`(vU>{Ph?cMcyw$x(fRiPYE1VrH|)Rtg-&{C9_ z&@6<|>$N;&K;ckO6BDd7U=D(R@K7KT113hWi3W&9peO+pgaq+7GhE>UqMh7*-_HEz z_xkcq|3TRPt=oitFGwke$8?e zZ=EMu;!Y)&jhl7scx5=ll+-H^b9x*ukE0;3pvdWT+Y58;c8}f8aU2hyO#P65$?C%~ z<;PB5_f}roa5_A@Q~gcO{U0-Ga?bB8Y1y0@?tDkMEDtZp$ai|21)luEJcrX^FK|9t zX9vD|iobm-J8bu>S7kA33O>@r+s2%!uDGz;kl;^%@;D2}$$S2#mB>$RTS~13;k);h zZ>6^(kzYX58BAmFGJ~lMsu@gSFqy$523`ge8B{Tt08lTW@c?rK^b&w1pvo8l@dBCw zPve>1C;?65K)*~tM9?iDBA72ABA6o}BA6*4BA6;5BB%<82tqa@7%Lzmm;oOyvQ^7^ ze%X5`?}e`aFzAlFUfxQZjlpBz;q$V3Tf+A{H7l{Go~IT85Q_jv#Q>yY08%jksThD% z3^0k*TF28`8)Im#0K}X+iJIe!r{;+9hJQbMci2Yz`i}gH4(pJ*jL3 zoAeqEK>(RF6Zh=UiV2&JZ6VDK3llaxI;pR)WD-=3XRXx65f+QTDN(b5@jZCy2%46= zj6WHnWt)mxXGGS9(ROXP34$sfEEbnS2P}tAc+l~1IoZ&S&O~?nJ!&>gM_VG(ud{q; zW~5`-4w?@Qik=87CHc5Z(cR@kW34#2Len5A*A>Zef#u+uql10RU@;KqiNvLawBduH zgLL-2O}m7ze`wry=Gn-(kXz-}Zf}c>NA@I^gFjp;t`fwt*0g23XWsnE5BuJ_E;;Ny zE;yy`*$oJnb(^w`Bsv@W1aWY=091ufpUf3cz+1aNv?(HE9{=>#?HyvqYMbJLq{3Tx za+~5dh_|?@xT8&33w``PoDxt930saQ1{4Qj6?k1hae)M%(7#%p>}#oScpdwK$}=$? zAHyzuhyMyF`4EV9;$iJd5n*$2UAy8WEEj*!4kA$VHT>fP1|mU)951c~PYqHLZUhbn zm2viapKjRuRm;I!5kJI*PjB7cu_Chx&M03?Z8FM0OBt1bDvZJcP%ooqphp=c=V%+F zpo#p9#seMAXdKWqMq{DkKN*bydWuma(A~H^qzoY|TFN@fPI(z6mxU}!@%fNqiOcyi zuW2gktqA+CDL3Q^=`bwDm)F!ZE^cUUf`_OLw(mY-&ivj?&yj5I;^dR?ziE=(Px?vP zCA}dPi1)-3Vw>m{Q_L65UFPwRZ*ZomAl=B>s@2i;was+f48hgTG;D)CMFa4JfKr4~ z@!Ejm<~{wf7tRLn=!Xvn6emi-x8Mk%WSrZs6pMv6BbQRGj+y6M&|HU6yE4?!@sUTv zJKL4#EUJ+kSl$0cUo)JIjH<=WbR1iPiqYayj9hxPI%Scsp~>I4hz%Lu#V#wJ5(M!i z90)2Uyekp6w<*P?#E-t8SMEeZ<;~4ELgYBe;x-K`H}SCK>C;Aca9P0~-U4fyzK(U}ca*A%*;q zL|p_go*wEw$FKNWVTrE GEj&N^&=F+-