Refactor. Add data layer
This commit is contained in:
1 parent
3b7e4e6ec5
commit
b6b794f0b4
82 files changed
+2930
-2811
No files matched your search
@@ -1,6 +1,7 @@
|
||||
@page "/accounts"
|
||||
@using BudgetApp.Data.Entities
|
||||
@attribute [Authorize]
|
||||
@inject AccountService AccountService
|
||||
@inject IAccountRepository AccountService
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
@inject AppState AppState
|
||||
@rendermode InteractiveServer
|
||||
@@ -15,20 +16,20 @@
|
||||
<h5 class="mb-0">Add Account</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<EditForm Model="formModel" OnValidSubmit="AddAccount" FormName="addAccount">
|
||||
<EditForm Model="_formModel" OnValidSubmit="AddAccount" FormName="addAccount">
|
||||
<ToastValidator />
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Account Name</label>
|
||||
<InputText @bind-Value="formModel.Name" class="form-control" placeholder="e.g. Current Account" title="A friendly name for this account" />
|
||||
<InputText @bind-Value="_formModel.Name" class="form-control" placeholder="e.g. Current Account" title="A friendly name for this account" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Current Balance</label>
|
||||
<InputNumber @bind-Value="formModel.CurrentBalance" class="form-control numeric-input" title="The opening balance for this account" />
|
||||
<InputNumber @bind-Value="_formModel.CurrentBalance" class="form-control numeric-input" title="The opening balance for this account" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Currency</label>
|
||||
<InputSelect @bind-Value="formModel.CurrencyCode" class="form-select" title="Currency used for this account">
|
||||
<InputSelect @bind-Value="_formModel.CurrencyCode" class="form-select" title="Currency used for this account">
|
||||
@foreach (var kvp in Account.CurrencySymbols)
|
||||
{
|
||||
<option value="@kvp.Key">@kvp.Key (@kvp.Value)</option>
|
||||
@@ -37,7 +38,7 @@
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Colour</label>
|
||||
<InputText @bind-Value="formModel.Color" type="color" class="form-control form-control-color" title="Colour used to identify this account in charts and lists" />
|
||||
<InputText @bind-Value="_formModel.Color" type="color" class="form-control form-control-color" title="Colour used to identify this account in charts and lists" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary w-100">Add</button>
|
||||
@@ -47,31 +48,31 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (accounts is null)
|
||||
@if (_accounts is null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else if (accounts.Count == 0)
|
||||
else if (_accounts.Count == 0)
|
||||
{
|
||||
<div class="alert alert-info">No accounts yet. Add one above to get started.</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
@foreach (var account in accounts)
|
||||
@foreach (var account in _accounts)
|
||||
{
|
||||
<div class="col-md-4 col-sm-6 mb-3">
|
||||
<div class="card h-100" style="border-left: 4px solid @account.Color;">
|
||||
<div class="card-body">
|
||||
@if (editingAccountId == account.Id)
|
||||
@if (_editingAccountId == account.Id)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label form-label-sm mb-1">Name</label>
|
||||
<input type="text" class="form-control form-control-sm" @bind="editName" />
|
||||
<input type="text" class="form-control form-control-sm" @bind="_editName" />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label form-label-sm mb-1">Currency</label>
|
||||
<select class="form-select form-select-sm" @bind="editCurrencyCode">
|
||||
<select class="form-select form-select-sm" @bind="_editCurrencyCode">
|
||||
@foreach (var kvp in Account.CurrencySymbols)
|
||||
{
|
||||
<option value="@kvp.Key">@kvp.Key (@kvp.Value)</option>
|
||||
@@ -80,7 +81,7 @@ else
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label form-label-sm mb-1">Colour</label>
|
||||
<input type="color" class="form-control form-control-color form-control-sm" @bind="editColor" />
|
||||
<input type="color" class="form-control form-control-color form-control-sm" @bind="_editColor" />
|
||||
</div>
|
||||
<div class="d-flex gap-1">
|
||||
<button class="btn btn-success btn-sm" @onclick="() => SaveAccountDetails(account.Id)">Save</button>
|
||||
@@ -100,13 +101,13 @@ else
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (editingBalanceAccountId == account.Id)
|
||||
@if (_editingBalanceAccountId == account.Id)
|
||||
{
|
||||
<div class="mt-2">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">@account.GetSymbol()</span>
|
||||
<input type="number" class="form-control numeric-input" step="0.01"
|
||||
@bind="editingBalanceValue" />
|
||||
@bind="_editingBalanceValue" />
|
||||
</div>
|
||||
<div class="d-flex gap-1 mt-2">
|
||||
<button class="btn btn-success btn-sm flex-fill" @onclick="() => SaveBalance(account.Id)">Save</button>
|
||||
@@ -131,7 +132,7 @@ else
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (showDeleteModal && pendingDeleteAccount is not null)
|
||||
@if (_showDeleteModal && _pendingDeleteAccount is not null)
|
||||
{
|
||||
<div class="modal d-block" tabindex="-1" style="background: rgba(0,0,0,.5);">
|
||||
<div class="modal-dialog">
|
||||
@@ -141,7 +142,7 @@ else
|
||||
<button type="button" class="btn-close" @onclick="CancelDeleteAccount"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to delete <strong>@pendingDeleteAccount.Name</strong>?</p>
|
||||
<p>Are you sure you want to delete <strong>@_pendingDeleteAccount.Name</strong>?</p>
|
||||
<p class="text-danger mb-0">This will permanently remove the account and all its transactions.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@@ -154,138 +155,11 @@ else
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Account>? accounts;
|
||||
private AccountForm formModel = new();
|
||||
private string? userId;
|
||||
|
||||
// Balance editing state
|
||||
private int? editingBalanceAccountId;
|
||||
private decimal editingBalanceValue;
|
||||
|
||||
// Account details editing state
|
||||
private int? editingAccountId;
|
||||
private string editName = string.Empty;
|
||||
private string editColor = "#1b6ec2";
|
||||
private string editCurrencyCode = "GBP";
|
||||
|
||||
// Delete confirmation state
|
||||
private bool showDeleteModal;
|
||||
private Account? pendingDeleteAccount;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
userId = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
||||
await LoadAccounts();
|
||||
}
|
||||
|
||||
private async Task LoadAccounts()
|
||||
{
|
||||
if (userId is not null)
|
||||
{
|
||||
accounts = await AccountService.GetAccountsAsync(userId);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AddAccount()
|
||||
{
|
||||
if (userId is null) return;
|
||||
|
||||
var account = new Account
|
||||
{
|
||||
Name = formModel.Name,
|
||||
CurrentBalance = formModel.CurrentBalance,
|
||||
Color = formModel.Color,
|
||||
CurrencyCode = formModel.CurrencyCode,
|
||||
UserId = userId
|
||||
};
|
||||
await AccountService.CreateAccountAsync(account);
|
||||
formModel = new AccountForm();
|
||||
await LoadAccounts();
|
||||
AppState.NotifyAccountsChanged();
|
||||
}
|
||||
|
||||
private void ConfirmDeleteAccount(Account account)
|
||||
{
|
||||
pendingDeleteAccount = account;
|
||||
showDeleteModal = true;
|
||||
}
|
||||
|
||||
private async Task DeleteAccountConfirmed()
|
||||
{
|
||||
if (userId is null || pendingDeleteAccount is null) return;
|
||||
|
||||
showDeleteModal = false;
|
||||
await AccountService.DeleteAccountAsync(pendingDeleteAccount.Id, userId);
|
||||
pendingDeleteAccount = null;
|
||||
await LoadAccounts();
|
||||
AppState.NotifyAccountsChanged();
|
||||
}
|
||||
|
||||
private void CancelDeleteAccount()
|
||||
{
|
||||
showDeleteModal = false;
|
||||
pendingDeleteAccount = null;
|
||||
}
|
||||
|
||||
private void StartBalanceEdit(Account account)
|
||||
{
|
||||
editingBalanceAccountId = account.Id;
|
||||
editingBalanceValue = account.CurrentBalance;
|
||||
}
|
||||
|
||||
private void CancelBalanceEdit()
|
||||
{
|
||||
editingBalanceAccountId = null;
|
||||
}
|
||||
|
||||
private async Task SaveBalance(int accountId)
|
||||
{
|
||||
if (userId is null) return;
|
||||
|
||||
await AccountService.UpdateBalanceAsync(accountId, userId, editingBalanceValue);
|
||||
editingBalanceAccountId = null;
|
||||
await LoadAccounts();
|
||||
AppState.NotifyAccountsChanged();
|
||||
}
|
||||
|
||||
private void StartAccountEdit(Account account)
|
||||
{
|
||||
editingAccountId = account.Id;
|
||||
editName = account.Name;
|
||||
editColor = account.Color;
|
||||
editCurrencyCode = account.CurrencyCode;
|
||||
}
|
||||
|
||||
private void CancelAccountEdit()
|
||||
{
|
||||
editingAccountId = null;
|
||||
}
|
||||
|
||||
private async Task SaveAccountDetails(int accountId)
|
||||
{
|
||||
if (userId is null) return;
|
||||
|
||||
await AccountService.UpdateAccountDetailsAsync(accountId, userId, editName, editColor, editCurrencyCode);
|
||||
editingAccountId = null;
|
||||
await LoadAccounts();
|
||||
AppState.NotifyAccountsChanged();
|
||||
}
|
||||
|
||||
private sealed class AccountForm
|
||||
{
|
||||
[System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Account name is required")]
|
||||
[System.ComponentModel.DataAnnotations.MaxLength(100)]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public decimal CurrentBalance { get; set; }
|
||||
|
||||
[System.ComponentModel.DataAnnotations.Required]
|
||||
[System.ComponentModel.DataAnnotations.MaxLength(7)]
|
||||
public string Color { get; set; } = "#1b6ec2";
|
||||
|
||||
[System.ComponentModel.DataAnnotations.Required]
|
||||
[System.ComponentModel.DataAnnotations.MaxLength(3)]
|
||||
public string CurrencyCode { get; set; } = "GBP";
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user