@page "/salary" @attribute [Authorize] @inject SalaryService SalaryService @inject AccountService AccountService @inject AuthenticationStateProvider AuthStateProvider @rendermode InteractiveServer Salary

Salary

Define your regular income sources. Each salary generates recurring transactions at the set interval, forever.

Add Income Source
@if (accountsList is not null && accountsList.Count == 0) {
You need to create an account before adding a salary.
} else {
@if (accountsList is not null) { @foreach (var account in accountsList) { } }
}
@if (salaries is null) {

Loading...

} else if (salaries.Count == 0) {
No income sources yet. Add one above to start tracking your salary.
} else {
Income Sources
@foreach (var s in salaries) { }
Name Amount Account Schedule Next Payday
๐Ÿ’ฐ @s.Name +@s.Account.FormatAmount(s.Amount) @s.Account.Name ๐Ÿ” @s.GetPaydaySummary() @if (nextPaydates.TryGetValue(s.Id, out var next) && next.HasValue) { @next.Value.ToString("MMM dd, yyyy") } else { โ€” }
} @if (showDeleteModal && pendingDeleteSalary is not null) { } @code { private List? salaries; private List? accountsList; private Dictionary nextPaydates = []; private string? userId; private SalaryForm formModel = new(); protected override async Task OnInitializedAsync() { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); userId = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value; await LoadData(); } private async Task LoadData() { if (userId is null) return; var windowEnd = DateTime.Today.AddDays(90); await SalaryService.MaterializeSalaryTransactionsAsync(userId, windowEnd); salaries = await SalaryService.GetSalariesAsync(userId); accountsList = await AccountService.GetAccountsAsync(userId); nextPaydates.Clear(); foreach (var sal in salaries) { nextPaydates[sal.Id] = await SalaryService.GetNextPaydateAsync(sal.Id); } if (accountsList.Count > 0 && formModel.AccountId == 0) { formModel.AccountId = accountsList[0].Id; } } private async Task AddSalary() { if (userId is null) return; var entry = new BudgetApp.Data.Salary { Name = formModel.Name, Amount = formModel.Amount, AccountId = formModel.AccountId, RecurrenceType = formModel.RecurrenceType, StartDate = formModel.StartDate, UserId = userId }; await SalaryService.CreateSalaryAsync(entry, DateTime.Today.AddDays(90)); formModel = new SalaryForm { AccountId = accountsList?.FirstOrDefault()?.Id ?? 0 }; await LoadData(); } // Delete state private bool showDeleteModal; private BudgetApp.Data.Salary? pendingDeleteSalary; private void ConfirmDelete(BudgetApp.Data.Salary s) { pendingDeleteSalary = s; showDeleteModal = true; } private void CancelDelete() { showDeleteModal = false; pendingDeleteSalary = null; } private async Task ExecuteDelete() { if (userId is null || pendingDeleteSalary is null) return; showDeleteModal = false; await SalaryService.DeleteSalaryAsync(pendingDeleteSalary.Id, userId); pendingDeleteSalary = null; await LoadData(); } private sealed class SalaryForm : System.ComponentModel.DataAnnotations.IValidatableObject { [System.ComponentModel.DataAnnotations.Required(ErrorMessage = "Name is required")] [System.ComponentModel.DataAnnotations.MaxLength(200)] public string Name { get; set; } = string.Empty; [System.ComponentModel.DataAnnotations.Range(0.01, double.MaxValue, ErrorMessage = "Amount must be greater than zero")] public decimal Amount { get; set; } public int AccountId { get; set; } public RecurrenceType RecurrenceType { get; set; } = RecurrenceType.Monthly; public DateTime StartDate { get; set; } = DateTime.Today; public IEnumerable Validate( System.ComponentModel.DataAnnotations.ValidationContext validationContext) { yield break; } } }