using Microsoft.EntityFrameworkCore; namespace BudgetApp.Components.Pages; public partial class Settings { 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; private bool _confirmingDelete; 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; _passwordError = null; 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; var result = await UserManager.ChangePasswordAsync(user, _passwordForm.CurrentPassword, _passwordForm.NewPassword); if (result.Succeeded) { _passwordSuccess = true; _passwordForm = new ChangePasswordForm(); } else { _passwordError = string.Join(" ", result.Errors.Select(e => e.Description)); } } private async Task DeleteAccount() { _deleteError = null; 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; var passwordValid = await UserManager.CheckPasswordAsync(user, _deletePassword); if (!passwordValid) { _deleteError = "Incorrect password."; return; } // Delete all user data explicitly in correct dependency order 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(); // Delete identity user await UserManager.DeleteAsync(user); await SignInManager.SignOutAsync(); Navigation.NavigateTo("/login", forceLoad: true); } private void CancelDelete() { _confirmingDelete = false; _deletePassword = string.Empty; _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")] public string CurrentPassword { get; set; } = string.Empty; [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "New password is required")] [System.ComponentModel.DataAnnotations.MinLength(8, ErrorMessage = "Password must be at least 8 characters")] public string NewPassword { get; set; } = string.Empty; [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Please confirm your new password")] [System.ComponentModel.DataAnnotations.Compare(nameof(NewPassword), ErrorMessage = "Passwords do not match")] public string ConfirmNewPassword { get; set; } = string.Empty; } }