Updates
This commit is contained in:
1 parent
9faa49daa1
commit
08eefe2973
20 files changed
+572
-142
No files matched your search
@@ -2,7 +2,6 @@
|
||||
@attribute [Authorize]
|
||||
@inject DashboardService DashboardService
|
||||
@inject TransactionService TransactionService
|
||||
@inject AccountService AccountService
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
@inject IJSRuntime JS
|
||||
@implements IAsyncDisposable
|
||||
@@ -25,53 +24,25 @@ else if (data.Accounts.Count == 0)
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="d-flex flex-wrap gap-2 mb-3" style="overflow-x: auto;">
|
||||
@foreach (var account in data.Accounts)
|
||||
{
|
||||
<div class="border rounded px-3 py-2 balance-chip" style="border-left: 3px solid @account.Color !important; min-width: 180px;">
|
||||
<small class="text-muted">@account.Name</small>
|
||||
@if (editingBalanceAccountId == account.Id)
|
||||
{
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
<span class="text-muted" style="font-size: 0.85rem;">@account.GetSymbol()</span>
|
||||
<input type="number" class="form-control form-control-sm numeric-input" step="0.01"
|
||||
style="width: 90px;" @bind="editingBalanceValue" />
|
||||
<button class="btn btn-success btn-sm px-1" @onclick="() => SaveBalance(account.Id)" title="Save">✓</button>
|
||||
<button class="btn btn-outline-secondary btn-sm px-1" @onclick="CancelBalanceEdit" title="Cancel">✕</button>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div style="cursor: pointer;" title="Click to edit balance"
|
||||
@onclick="() => StartBalanceEdit(account)">
|
||||
<span class="fw-semibold @(account.CurrentBalance >= 0 ? "text-success" : "text-danger")">
|
||||
@account.FormatAmount(account.CurrentBalance)
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap align-items-center gap-2 mb-3">
|
||||
<small class="text-muted fw-semibold">Window:</small>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn @(windowPreset == "7d" ? "btn-primary" : "btn-outline-secondary")" @onclick="() => SetWindow(7)">7d</button>
|
||||
<button class="btn @(windowPreset == "30d" ? "btn-primary" : "btn-outline-secondary")" @onclick="() => SetWindow(30)">30d</button>
|
||||
<button class="btn @(windowPreset == "90d" ? "btn-primary" : "btn-outline-secondary")" @onclick="() => SetWindow(90)">90d</button>
|
||||
<button class="btn @(windowPreset == "1y" ? "btn-primary" : "btn-outline-secondary")" @onclick="() => SetWindow(365)">1y</button>
|
||||
</div>
|
||||
<input type="date" class="form-control form-control-sm" style="width: auto;"
|
||||
@bind="windowStart" @bind:after="OnWindowDateChanged" />
|
||||
<span class="text-muted">to</span>
|
||||
<input type="date" class="form-control form-control-sm" style="width: auto;"
|
||||
@bind="windowEnd" @bind:after="OnWindowDateChanged" />
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<h5 class="mb-0">Balance Trend</h5>
|
||||
<div class="d-flex flex-wrap align-items-center gap-2">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn @(windowPreset == "7d" ? "btn-primary" : "btn-outline-secondary")" @onclick="() => SetWindow(7)">7d</button>
|
||||
<button class="btn @(windowPreset == "30d" ? "btn-primary" : "btn-outline-secondary")" @onclick="() => SetWindow(30)">30d</button>
|
||||
<button class="btn @(windowPreset == "90d" ? "btn-primary" : "btn-outline-secondary")" @onclick="() => SetWindow(90)">90d</button>
|
||||
<button class="btn @(windowPreset == "1y" ? "btn-primary" : "btn-outline-secondary")" @onclick="() => SetWindow(365)">1y</button>
|
||||
</div>
|
||||
<input type="date" class="form-control form-control-sm date-picker"
|
||||
@bind="windowStart" @bind:after="OnWindowDateChanged" />
|
||||
<span class="text-muted">to</span>
|
||||
<input type="date" class="form-control form-control-sm date-picker"
|
||||
@bind="windowEnd" @bind:after="OnWindowDateChanged" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Balance Trend</h5>
|
||||
<div style="height: clamp(200px, 40vw, 350px); position: relative;">
|
||||
<div class="chart-container">
|
||||
<canvas id="balanceChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
@@ -88,12 +59,11 @@ else
|
||||
<button type="button" class="btn-close" @onclick="CloseModal"></button>
|
||||
</div>
|
||||
<EditForm Model="modalForm" OnValidSubmit="SaveModal" FormName="dashboardModal">
|
||||
<DataAnnotationsValidator />
|
||||
<ToastValidator />
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Description</label>
|
||||
<InputText @bind-Value="modalForm.Description" class="form-control" />
|
||||
<ValidationMessage For="() => modalForm.Description" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Amount</label>
|
||||
@@ -129,22 +99,30 @@ else
|
||||
}
|
||||
</InputSelect>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Recurrence</label>
|
||||
<InputSelect @bind-Value="modalForm.RecurrenceType" class="form-select">
|
||||
<option value="@RecurrenceType.None">None</option>
|
||||
<option value="@RecurrenceType.Daily">Daily</option>
|
||||
<option value="@RecurrenceType.Weekly">Weekly</option>
|
||||
<option value="@RecurrenceType.Monthly">Monthly</option>
|
||||
<option value="@RecurrenceType.Yearly">Yearly</option>
|
||||
</InputSelect>
|
||||
</div>
|
||||
@if (modalForm.RecurrenceType != RecurrenceType.None)
|
||||
@if (editingTransactionId is null)
|
||||
{
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Recurrence End Date <small class="text-muted">(max 1 year, defaults to 1 year if blank)</small></label>
|
||||
<InputDate @bind-Value="modalForm.RecurrenceEndDate" class="form-control" max="@modalForm.Date.AddYears(1).ToString("yyyy-MM-dd")" />
|
||||
<ValidationMessage For="() => modalForm.RecurrenceEndDate" />
|
||||
<label class="form-label">Recurrence</label>
|
||||
<InputSelect @bind-Value="modalForm.RecurrenceType" class="form-select">
|
||||
<option value="@RecurrenceType.None">None</option>
|
||||
<option value="@RecurrenceType.Daily">Daily</option>
|
||||
<option value="@RecurrenceType.Weekly">Weekly</option>
|
||||
<option value="@RecurrenceType.Monthly">Monthly</option>
|
||||
<option value="@RecurrenceType.Yearly">Yearly</option>
|
||||
</InputSelect>
|
||||
</div>
|
||||
@if (modalForm.RecurrenceType != RecurrenceType.None)
|
||||
{
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Recurrence End Date <small class="text-muted">(max 1 year, defaults to 1 year if blank)</small></label>
|
||||
<InputDate @bind-Value="modalForm.RecurrenceEndDate" class="form-control" max="@modalForm.Date.AddYears(1).ToString("yyyy-MM-dd")" />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else if (editIsPartOfSeries)
|
||||
{
|
||||
<div class="alert alert-info mb-0">
|
||||
<i class="bi bi-info-circle"></i> Changes will apply to this and all future occurrences in the series.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -341,6 +319,7 @@ else
|
||||
// Modal state
|
||||
private bool showModal;
|
||||
private int? editingTransactionId;
|
||||
private bool editIsPartOfSeries;
|
||||
private ModalTransactionForm modalForm = new();
|
||||
|
||||
// Chart redraw tracking
|
||||
@@ -422,30 +401,6 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
// Balance editing state
|
||||
private int? editingBalanceAccountId;
|
||||
private decimal editingBalanceValue;
|
||||
|
||||
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 RefreshData();
|
||||
}
|
||||
|
||||
private void OpenAddModal()
|
||||
{
|
||||
editingTransactionId = null;
|
||||
@@ -459,6 +414,7 @@ else
|
||||
private void OpenEditModal(Transaction t)
|
||||
{
|
||||
editingTransactionId = t.Id;
|
||||
editIsPartOfSeries = t.RecurrenceType != RecurrenceType.None || t.ParentTransactionId is not null;
|
||||
modalForm = new ModalTransactionForm
|
||||
{
|
||||
Description = t.Description,
|
||||
@@ -493,6 +449,18 @@ else
|
||||
RecurrenceEndDate = modalForm.RecurrenceEndDate
|
||||
};
|
||||
await TransactionService.CreateTransactionAsync(transaction);
|
||||
await RefreshData();
|
||||
|
||||
// Keep modal open and reset form for the next entry
|
||||
var previousAccountId = modalForm.AccountId;
|
||||
var previousCategoryId = modalForm.CategoryId;
|
||||
var previousType = modalForm.Type;
|
||||
modalForm = new ModalTransactionForm
|
||||
{
|
||||
AccountId = previousAccountId,
|
||||
CategoryId = previousCategoryId,
|
||||
Type = previousType
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -501,10 +469,10 @@ else
|
||||
modalForm.Description, modalForm.Amount, modalForm.Date,
|
||||
modalForm.Type, modalForm.AccountId, modalForm.CategoryId,
|
||||
modalForm.RecurrenceType, modalForm.RecurrenceEndDate);
|
||||
}
|
||||
|
||||
showModal = false;
|
||||
await RefreshData();
|
||||
showModal = false;
|
||||
await RefreshData();
|
||||
}
|
||||
}
|
||||
|
||||
// Delete confirmation state
|
||||
|
||||
Reference in new issue
Block a user