diff --git a/MoneyMap/Pages/Merchants.cshtml b/MoneyMap/Pages/Merchants.cshtml new file mode 100644 index 0000000..61b4eb5 --- /dev/null +++ b/MoneyMap/Pages/Merchants.cshtml @@ -0,0 +1,186 @@ +@page +@model MoneyMap.Pages.MerchantsModel +@{ + ViewData["Title"] = "Merchants"; +} + +
+

Merchants

+ Back to Dashboard +
+ +@if (!string.IsNullOrEmpty(Model.SuccessMessage)) +{ + +} + +@if (!string.IsNullOrEmpty(Model.ErrorMessage)) +{ + +} + +
+
+
+
+
Total Merchants
+
@Model.TotalMerchants
+
+
+
+
+ + +
+ +
+ +@if (Model.Merchants.Any()) +{ +
+
+ All Merchants + Click to edit, or delete merchants +
+
+
+ + + + + + + + + + + @foreach (var merchant in Model.Merchants) + { + + + + + + + } + +
Merchant NameTransactionsMappingsActions
+ @merchant.Name + + @merchant.TransactionCount + + @merchant.MappingCount + + +
+ +
+
+
+
+
+} +else +{ +
+
No merchants found
+

Click "Add New Merchant" to create your first merchant.

+
+} + + + + + + + +@section Scripts { + +} diff --git a/MoneyMap/Pages/Merchants.cshtml.cs b/MoneyMap/Pages/Merchants.cshtml.cs new file mode 100644 index 0000000..f2d144a --- /dev/null +++ b/MoneyMap/Pages/Merchants.cshtml.cs @@ -0,0 +1,169 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.EntityFrameworkCore; +using MoneyMap.Data; +using MoneyMap.Models; + +namespace MoneyMap.Pages +{ + public class MerchantsModel : PageModel + { + private readonly MoneyMapContext _db; + + public MerchantsModel(MoneyMapContext db) + { + _db = db; + } + + public List Merchants { get; set; } = new(); + public int TotalMerchants { get; set; } + + [TempData] + public string? SuccessMessage { get; set; } + + [TempData] + public string? ErrorMessage { get; set; } + + public async Task OnGetAsync() + { + await LoadDataAsync(); + } + + public async Task OnPostAddMerchantAsync(AddMerchantModel model) + { + if (!ModelState.IsValid) + { + ErrorMessage = "Please fill in all required fields."; + await LoadDataAsync(); + return Page(); + } + + // Check if merchant already exists + var existing = await _db.Merchants + .FirstOrDefaultAsync(m => m.Name == model.Name.Trim()); + + if (existing != null) + { + ErrorMessage = $"Merchant '{model.Name}' already exists."; + await LoadDataAsync(); + return Page(); + } + + var merchant = new Merchant + { + Name = model.Name.Trim() + }; + + _db.Merchants.Add(merchant); + await _db.SaveChangesAsync(); + + SuccessMessage = $"Added merchant '{merchant.Name}'."; + return RedirectToPage(); + } + + public async Task OnPostUpdateMerchantAsync(UpdateMerchantModel model) + { + if (!ModelState.IsValid) + { + ErrorMessage = "Please fill in all required fields."; + await LoadDataAsync(); + return Page(); + } + + var merchant = await _db.Merchants.FindAsync(model.Id); + if (merchant == null) + { + ErrorMessage = "Merchant not found."; + return RedirectToPage(); + } + + // Check if another merchant with the same name exists + var existing = await _db.Merchants + .FirstOrDefaultAsync(m => m.Name == model.Name.Trim() && m.Id != model.Id); + + if (existing != null) + { + ErrorMessage = $"Merchant '{model.Name}' already exists."; + await LoadDataAsync(); + return Page(); + } + + merchant.Name = model.Name.Trim(); + await _db.SaveChangesAsync(); + + SuccessMessage = "Merchant updated successfully."; + return RedirectToPage(); + } + + public async Task OnPostDeleteMerchantAsync(int id) + { + var merchant = await _db.Merchants + .Include(m => m.Transactions) + .Include(m => m.CategoryMappings) + .FirstOrDefaultAsync(m => m.Id == id); + + if (merchant == null) + { + ErrorMessage = "Merchant not found."; + return RedirectToPage(); + } + + var transactionCount = merchant.Transactions.Count; + var mappingCount = merchant.CategoryMappings.Count; + + _db.Merchants.Remove(merchant); + await _db.SaveChangesAsync(); + + SuccessMessage = $"Deleted merchant '{merchant.Name}'. {transactionCount} transactions and {mappingCount} category mappings are now unlinked."; + return RedirectToPage(); + } + + private async Task LoadDataAsync() + { + var merchants = await _db.Merchants + .Include(m => m.Transactions) + .Include(m => m.CategoryMappings) + .OrderBy(m => m.Name) + .ToListAsync(); + + Merchants = merchants.Select(m => new MerchantRow + { + Id = m.Id, + Name = m.Name, + TransactionCount = m.Transactions.Count, + MappingCount = m.CategoryMappings.Count + }).ToList(); + + TotalMerchants = Merchants.Count; + } + + public class MerchantRow + { + public int Id { get; set; } + public string Name { get; set; } = ""; + public int TransactionCount { get; set; } + public int MappingCount { get; set; } + } + + public class AddMerchantModel + { + [Required(ErrorMessage = "Merchant name is required")] + [StringLength(100)] + public string Name { get; set; } = ""; + } + + public class UpdateMerchantModel + { + [Required] + public int Id { get; set; } + + [Required(ErrorMessage = "Merchant name is required")] + [StringLength(100)] + public string Name { get; set; } = ""; + } + } +} diff --git a/MoneyMap/Pages/Shared/_Layout.cshtml b/MoneyMap/Pages/Shared/_Layout.cshtml index e5a6773..c4039fd 100644 --- a/MoneyMap/Pages/Shared/_Layout.cshtml +++ b/MoneyMap/Pages/Shared/_Layout.cshtml @@ -34,6 +34,9 @@ +