Files
Budgeteer/BudgetApp/Components/Pages/Salary.razor
T
2026-03-23 15:05:57 +00:00

222 lines
10 KiB
Plaintext

@page "/salary"
@using BudgetApp.Data.Entities
@attribute [Authorize]
@inject ISalaryRepository SalaryService
@inject IAccountRepository AccountService
@inject AuthenticationStateProvider AuthStateProvider
@rendermode InteractiveServer
<PageTitle>Salary</PageTitle>
<h3 class="mb-2">Salary</h3>
<p class="text-muted mb-4">Define your regular income sources. Each salary generates recurring transactions at the set interval, forever.</p>
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">Add Income Source</h5>
</div>
<div class="card-body">
@if (_accountsList is not null && _accountsList.Count == 0)
{
<div class="alert alert-warning mb-0">
You need to <a href="/accounts">create an account</a> before adding a salary.
</div>
}
else
{
<EditForm Model="_formModel" OnValidSubmit="AddSalary" FormName="addSalary">
<ToastValidator />
<div class="row g-3">
<div class="col-md-3">
<label class="form-label">Name</label>
<InputText @bind-Value="_formModel.Name" class="form-control" placeholder="e.g. Main Job" />
</div>
<div class="col-md-2">
<label class="form-label">Amount</label>
<InputNumber @bind-Value="_formModel.Amount" class="form-control numeric-input" />
</div>
<div class="col-md-2">
<label class="form-label">Account</label>
<InputSelect @bind-Value="_formModel.AccountId" class="form-select">
@if (_accountsList is not null)
{
@foreach (var account in _accountsList)
{
<option value="@account.Id">@account.Name</option>
}
}
</InputSelect>
</div>
<div class="col-md-2">
<label class="form-label">Recurrence</label>
<InputSelect @bind-Value="_formModel.RecurrenceType" class="form-select">
<option value="@RecurrenceType.Weekly">Weekly</option>
<option value="@RecurrenceType.Monthly">Monthly</option>
<option value="@RecurrenceType.Yearly">Yearly</option>
</InputSelect>
</div>
<div class="col-md-3">
<label class="form-label">First Payday</label>
<InputDate @bind-Value="_formModel.StartDate" class="form-control" title="The date of the first payment — the interval repeats from here indefinitely" />
</div>
<div class="col-md-12 col-lg-auto d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">Add</button>
</div>
</div>
</EditForm>
}
</div>
</div>
@if (_salaries is null)
{
<p><em>Loading...</em></p>
}
else if (_salaries.Count == 0)
{
<div class="alert alert-info">No income sources yet. Add one above to start tracking your salary.</div>
}
else
{
<div class="card">
<div class="card-header">
<h5 class="mb-0">Income Sources</h5>
</div>
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Name</th>
<th>Amount</th>
<th>Account</th>
<th class="d-none d-md-table-cell">Schedule</th>
<th>Next Payday</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var s in _salaries)
{
<tr>
<td class="fw-semibold">💰 @s.Name</td>
<td class="text-success fw-semibold text-nowrap">+@s.Account.FormatAmount(s.Amount)</td>
<td>
<span class="badge" style="background-color: @s.Account.Color;">
@s.Account.Name
</span>
</td>
<td class="d-none d-md-table-cell">
<span class="badge bg-info text-nowrap">🔁 @s.GetPaydaySummary()</span>
</td>
<td class="text-nowrap">
@if (_nextPaydates.TryGetValue(s.Id, out var next) && next.HasValue)
{
<span>@next.Value.ToString("MMM dd, yyyy")</span>
}
else
{
<span class="text-muted">—</span>
}
</td>
<td class="text-end text-nowrap">
<button class="btn btn-sm btn-outline-secondary me-1" @onclick="() => OpenEditModal(s)">✏️ Edit</button>
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(s)">Delete</button>
</td>
</tr>
}
</tbody>
</table>
</div>
<div class="card-footer">
<small class="text-muted">@_salaries.Count income source@(_salaries.Count == 1 ? "" : "s") · Transactions are generated automatically and appear on the <a href="/transactions">Transactions</a> page.</small>
</div>
</div>
}
@if (_showDeleteModal && _pendingDeleteSalary is not null)
{
<div class="modal d-block" tabindex="-1" style="background: rgba(0,0,0,.5);">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Delete Income Source</h5>
<button type="button" class="btn-close" @onclick="CancelDelete"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete <strong>@_pendingDeleteSalary.Name</strong>?</p>
<p>All future unpaid salary transactions will be removed. Past transactions remain in your history.</p>
<p class="text-danger mb-0">This action cannot be undone.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @onclick="CancelDelete">Cancel</button>
<button type="button" class="btn btn-danger" @onclick="ExecuteDelete">Delete</button>
</div>
</div>
</div>
</div>
}
@if (_showEditModal)
{
<div class="modal d-block" tabindex="-1" style="background: rgba(0,0,0,.5);">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit Income Source</h5>
<button type="button" class="btn-close" @onclick="CancelEdit"></button>
</div>
<EditForm Model="_editFormModel" OnValidSubmit="SaveEdit" FormName="editSalary">
<ToastValidator />
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Name</label>
<InputText @bind-Value="_editFormModel.Name" class="form-control" />
</div>
<div class="mb-3">
<label class="form-label">Amount</label>
<InputNumber @bind-Value="_editFormModel.Amount" class="form-control numeric-input" />
</div>
<div class="mb-3">
<label class="form-label">Account</label>
<InputSelect @bind-Value="_editFormModel.AccountId" class="form-select">
@if (_accountsList is not null)
{
@foreach (var account in _accountsList)
{
<option value="@account.Id">@account.Name</option>
}
}
</InputSelect>
</div>
<div class="mb-3">
<label class="form-label">Recurrence</label>
<InputSelect @bind-Value="_editFormModel.RecurrenceType" class="form-select">
<option value="@RecurrenceType.Weekly">Weekly</option>
<option value="@RecurrenceType.Monthly">Monthly</option>
<option value="@RecurrenceType.Yearly">Yearly</option>
</InputSelect>
</div>
<div class="mb-3">
<label class="form-label">First Payday</label>
<InputDate @bind-Value="_editFormModel.StartDate" class="form-control" />
</div>
<p class="text-muted mb-0" style="font-size: 0.85rem;">
Saving will delete all future unpaid salary transactions and regenerate them with the updated settings.
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" @onclick="CancelEdit">Cancel</button>
<button type="submit" class="btn btn-primary">Save Changes</button>
</div>
</EditForm>
</div>
</div>
</div>
}
@code {
// Delete state
}