diff --git a/BudgetApp.Data/Entities/RecurrenceType.cs b/BudgetApp.Data/Entities/RecurrenceType.cs index 45a7990..b2ae989 100644 --- a/BudgetApp.Data/Entities/RecurrenceType.cs +++ b/BudgetApp.Data/Entities/RecurrenceType.cs @@ -6,5 +6,6 @@ public enum RecurrenceType Daily, Weekly, Monthly, - Yearly + Yearly, + FourWeekly } \ No newline at end of file diff --git a/BudgetApp.Data/Entities/Salary.cs b/BudgetApp.Data/Entities/Salary.cs index bb8fca9..7c8e76e 100644 --- a/BudgetApp.Data/Entities/Salary.cs +++ b/BudgetApp.Data/Entities/Salary.cs @@ -27,10 +27,11 @@ public class Salary public string GetPaydaySummary() => RecurrenceType switch { - RecurrenceType.Weekly => $"Every week on {StartDate:dddd}", - RecurrenceType.Monthly => $"Monthly on the {GetOrdinal(StartDate.Day)}", - RecurrenceType.Yearly => $"Annually on {StartDate:MMMM d}", - _ => RecurrenceType.ToString() + RecurrenceType.Weekly => $"Every week on {StartDate:dddd}", + RecurrenceType.FourWeekly => $"Every 4 weeks on {StartDate:dddd}", + RecurrenceType.Monthly => $"Monthly on the {GetOrdinal(StartDate.Day)}", + RecurrenceType.Yearly => $"Annually on {StartDate:MMMM d}", + _ => RecurrenceType.ToString() }; private static string GetOrdinal(int day) => day switch diff --git a/BudgetApp.Data/Repositories/SalaryRepository.cs b/BudgetApp.Data/Repositories/SalaryRepository.cs index eb81fb7..fb4ad33 100644 --- a/BudgetApp.Data/Repositories/SalaryRepository.cs +++ b/BudgetApp.Data/Repositories/SalaryRepository.cs @@ -129,9 +129,10 @@ public class SalaryRepository(ApplicationDbContext db) : ISalaryRepository private static DateTime GetNextDate(DateTime from, Salary salary) => salary.RecurrenceType switch { - RecurrenceType.Weekly => from.AddDays(7), - RecurrenceType.Monthly => from.AddMonths(1), - RecurrenceType.Yearly => from.AddYears(1), - _ => from.AddMonths(1) + RecurrenceType.Weekly => from.AddDays(7), + RecurrenceType.FourWeekly => from.AddDays(28), + RecurrenceType.Monthly => from.AddMonths(1), + RecurrenceType.Yearly => from.AddYears(1), + _ => from.AddMonths(1) }; } diff --git a/BudgetApp.Data/Repositories/TransactionRepository.cs b/BudgetApp.Data/Repositories/TransactionRepository.cs index 01677dc..f653de3 100644 --- a/BudgetApp.Data/Repositories/TransactionRepository.cs +++ b/BudgetApp.Data/Repositories/TransactionRepository.cs @@ -278,10 +278,11 @@ public class TransactionRepository(ApplicationDbContext db) : ITransactionReposi internal static DateTime GetNextOccurrence(DateTime current, RecurrenceType recurrence) => recurrence switch { - RecurrenceType.Daily => current.AddDays(1), - RecurrenceType.Weekly => current.AddDays(7), - RecurrenceType.Monthly => current.AddMonths(1), - RecurrenceType.Yearly => current.AddYears(1), - _ => DateTime.MaxValue + RecurrenceType.Daily => current.AddDays(1), + RecurrenceType.Weekly => current.AddDays(7), + RecurrenceType.FourWeekly => current.AddDays(28), + RecurrenceType.Monthly => current.AddMonths(1), + RecurrenceType.Yearly => current.AddYears(1), + _ => DateTime.MaxValue }; } diff --git a/BudgetApp/BudgetApp.csproj b/BudgetApp/BudgetApp.csproj index c3894b4..850f5c7 100644 --- a/BudgetApp/BudgetApp.csproj +++ b/BudgetApp/BudgetApp.csproj @@ -24,4 +24,10 @@ + + + PreserveNewest + + + diff --git a/BudgetApp/Components/Pages/Home.razor b/BudgetApp/Components/Pages/Home.razor index 14074b3..44b9a6d 100644 --- a/BudgetApp/Components/Pages/Home.razor +++ b/BudgetApp/Components/Pages/Home.razor @@ -110,6 +110,7 @@ else + diff --git a/BudgetApp/Components/Pages/Home.razor.cs b/BudgetApp/Components/Pages/Home.razor.cs index d1cf668..811bb27 100644 --- a/BudgetApp/Components/Pages/Home.razor.cs +++ b/BudgetApp/Components/Pages/Home.razor.cs @@ -287,9 +287,14 @@ public partial class Home private static string GetRecurrenceLabel(Transaction t) { - if (t.RecurrenceType != RecurrenceType.None) - return t.RecurrenceType.ToString(); - return t.ParentTransaction?.RecurrenceType.ToString() ?? "Recurring"; + var type = t.RecurrenceType != RecurrenceType.None + ? t.RecurrenceType + : (t.ParentTransaction?.RecurrenceType ?? RecurrenceType.None); + return type switch + { + RecurrenceType.FourWeekly => "4 Weekly", + _ => type.ToString() + }; } private void ComputeProjectedBalances() @@ -342,7 +347,7 @@ public partial class Home if (_nextPayday is null) return; var txnsToPayday = await TransactionService.GetTransactionsInRangeAsync( - _userId, DateTime.Today.AddDays(1), _nextPayday.Value); + _userId, DateTime.Today.AddDays(1), _nextPayday.Value.AddDays(-1)); foreach (var account in _data.Accounts) { diff --git a/BudgetApp/Components/Pages/Salary.razor b/BudgetApp/Components/Pages/Salary.razor index d19cef7..85a9116 100644 --- a/BudgetApp/Components/Pages/Salary.razor +++ b/BudgetApp/Components/Pages/Salary.razor @@ -51,6 +51,7 @@ + @@ -192,6 +193,7 @@ else + diff --git a/BudgetApp/Components/Pages/Transactions.razor b/BudgetApp/Components/Pages/Transactions.razor index 45b4d8a..81bdc4b 100644 --- a/BudgetApp/Components/Pages/Transactions.razor +++ b/BudgetApp/Components/Pages/Transactions.razor @@ -80,6 +80,7 @@ + @@ -178,8 +179,15 @@ else - @foreach (var t in PagedTransactions) + @foreach (var group in GroupedPagedTransactions) { + + + @group.Key.ToString("dddd, d MMMM") + + + @foreach (var t in OrderGroupByIncomeFirst(group)) + { @if (t.SalaryId is null) @@ -221,11 +229,11 @@ else @if (t.RecurrenceType != RecurrenceType.None) { - 🔁 @t.RecurrenceType + 🔁 @GetRecurrenceLabel(t.RecurrenceType) } else if (t.ParentTransactionId is not null) { - 🔁 @(t.ParentTransaction?.RecurrenceType.ToString() ?? "Recurring") + 🔁 @GetRecurrenceLabel(t.ParentTransaction?.RecurrenceType ?? RecurrenceType.None) } else { @@ -236,7 +244,8 @@ else @(t.Type == TransactionType.Income ? "+" : "-")@t.Account.FormatAmount(t.Amount) - } + } + } diff --git a/BudgetApp/Components/Pages/Transactions.razor.cs b/BudgetApp/Components/Pages/Transactions.razor.cs index 4b240c8..4e56ed1 100644 --- a/BudgetApp/Components/Pages/Transactions.razor.cs +++ b/BudgetApp/Components/Pages/Transactions.razor.cs @@ -26,6 +26,20 @@ public partial class Transactions .Take(_pageSize) .ToList(); + private IEnumerable> GroupedPagedTransactions + { + get + { + var groups = PagedTransactions.GroupBy(t => t.Date.Date); + return _sortColumn == "date" && _sortAscending + ? groups.OrderBy(g => g.Key) + : groups.OrderByDescending(g => g.Key); + } + } + + private static IEnumerable OrderGroupByIncomeFirst(IGrouping group) => + group.OrderByDescending(t => t.Type == TransactionType.Income).ThenBy(t => t.Description); + private string _sortColumn = "date"; private bool _sortAscending = true; @@ -305,6 +319,12 @@ public partial class Transactions _bulkDeleteCount = 0; } + private static string GetRecurrenceLabel(RecurrenceType type) => type switch + { + RecurrenceType.FourWeekly => "4 Weekly", + _ => type.ToString() + }; + private async Task TogglePaid(Transaction t) { if (_userId is null) return; diff --git a/BudgetApp/budgetapp.db-shm b/BudgetApp/budgetapp.db-shm index b1ccf5b..7306cb1 100644 Binary files a/BudgetApp/budgetapp.db-shm and b/BudgetApp/budgetapp.db-shm differ diff --git a/BudgetApp/budgetapp.db-wal b/BudgetApp/budgetapp.db-wal index 36c7307..4760394 100644 Binary files a/BudgetApp/budgetapp.db-wal and b/BudgetApp/budgetapp.db-wal differ diff --git a/BudgetApp/budgetapp.db.db b/BudgetApp/budgetapp.db.db new file mode 100644 index 0000000..bcde87d Binary files /dev/null and b/BudgetApp/budgetapp.db.db differ