Improve receipt mapping UI with transaction selector
Replaced the manual transaction ID input with a user-friendly
transaction selector dropdown in the map receipt modal.
Changes:
- Added RecentTransactions list to Receipts page model
- Load last 100 transactions without receipts for dropdown
- Updated modal dialog:
- Larger modal (modal-lg) for better visibility
- Multi-select dropdown showing formatted transaction details:
- Date | Amount | Name | (Merchant)
- Monospace font for aligned columns
- Size 10 to show multiple options at once
- Receipt info displayed in styled info box
- Manual ID input field as fallback option
- Link to Transactions page for finding IDs
- Two-way binding between selector and manual input
User Experience:
- Users can now visually browse and select transactions
- Transaction details are formatted for easy scanning
- Only shows transactions that don't already have receipts
- Fallback to manual ID entry for edge cases
- Receipt parsed data clearly displayed for comparison
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -200,7 +200,7 @@
|
||||
@foreach (var r in Model.Receipts.Where(r => !r.TransactionId.HasValue))
|
||||
{
|
||||
<div class="modal fade" id="mapModal@(r.Id)" tabindex="-1" aria-labelledby="mapModalLabel@(r.Id)" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="mapModalLabel@(r.Id)">Map Receipt to Transaction</h5>
|
||||
@@ -209,29 +209,48 @@
|
||||
<form method="post" asp-page-handler="MapToTransaction" asp-route-receiptId="@r.Id">
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Receipt: @r.FileName</label>
|
||||
<label class="form-label fw-bold">Receipt: @r.FileName</label>
|
||||
@if (!string.IsNullOrWhiteSpace(r.Merchant) || r.ReceiptDate.HasValue || r.Total.HasValue)
|
||||
{
|
||||
<div class="small text-muted">
|
||||
<div class="small text-muted bg-light p-2 rounded">
|
||||
@if (!string.IsNullOrWhiteSpace(r.Merchant))
|
||||
{
|
||||
<div>Merchant: @r.Merchant</div>
|
||||
<div><strong>Merchant:</strong> @r.Merchant</div>
|
||||
}
|
||||
@if (r.ReceiptDate.HasValue)
|
||||
{
|
||||
<div>Date: @r.ReceiptDate.Value.ToString("yyyy-MM-dd")</div>
|
||||
<div><strong>Date:</strong> @r.ReceiptDate.Value.ToString("yyyy-MM-dd")</div>
|
||||
}
|
||||
@if (r.Total.HasValue)
|
||||
{
|
||||
<div>Total: @r.Total.Value.ToString("C")</div>
|
||||
<div><strong>Total:</strong> @r.Total.Value.ToString("C")</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="transactionId@(r.Id)" class="form-label">Transaction ID</label>
|
||||
<input type="number" class="form-control" id="transactionId@(r.Id)" name="transactionId" required />
|
||||
<div class="form-text">Enter the transaction ID to map this receipt to. You can find transaction IDs on the Transactions page.</div>
|
||||
<label for="transactionId@(r.Id)" class="form-label">Select Transaction</label>
|
||||
<select class="form-select" id="transactionId@(r.Id)" name="transactionId" required size="10" style="font-family: monospace; font-size: 0.9rem;">
|
||||
<option value="" disabled selected>-- Select a transaction --</option>
|
||||
@foreach (var txn in Model.RecentTransactions)
|
||||
{
|
||||
var displayText = $"{txn.Date:yyyy-MM-dd} | {txn.Amount,10:C} | {txn.Name}";
|
||||
if (!string.IsNullOrWhiteSpace(txn.MerchantName))
|
||||
{
|
||||
displayText += $" ({txn.MerchantName})";
|
||||
}
|
||||
<option value="@txn.Id">@displayText</option>
|
||||
}
|
||||
</select>
|
||||
<div class="form-text">
|
||||
Showing last 100 transactions without receipts.
|
||||
Can't find it? <a asp-page="/Transactions" target="_blank">Open Transactions page</a> to find the ID and enter it manually below.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="manualTransactionId@(r.Id)" class="form-label">Or Enter Transaction ID Manually</label>
|
||||
<input type="number" class="form-control" id="manualTransactionId@(r.Id)" placeholder="Enter transaction ID..."
|
||||
onchange="document.getElementById('transactionId@(r.Id)').value = this.value" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace MoneyMap.Pages
|
||||
}
|
||||
|
||||
public List<ReceiptRow> Receipts { get; set; } = new();
|
||||
public List<TransactionOption> RecentTransactions { get; set; } = new();
|
||||
|
||||
[BindProperty]
|
||||
public IFormFile? UploadFile { get; set; }
|
||||
@@ -168,6 +169,31 @@ namespace MoneyMap.Pages
|
||||
Total = r.Total,
|
||||
StoragePath = r.StoragePath
|
||||
}).ToList();
|
||||
|
||||
// Load recent transactions without receipts for mapping dropdown
|
||||
var transactionsWithReceipts = await _db.Receipts
|
||||
.Where(r => r.TransactionId != null)
|
||||
.Select(r => r.TransactionId!.Value)
|
||||
.ToListAsync();
|
||||
|
||||
RecentTransactions = await _db.Transactions
|
||||
.Include(t => t.Card)
|
||||
.Include(t => t.Account)
|
||||
.Include(t => t.Merchant)
|
||||
.Where(t => !transactionsWithReceipts.Contains(t.Id))
|
||||
.OrderByDescending(t => t.Date)
|
||||
.ThenByDescending(t => t.Id)
|
||||
.Take(100) // Last 100 transactions without receipts
|
||||
.Select(t => new TransactionOption
|
||||
{
|
||||
Id = t.Id,
|
||||
Date = t.Date,
|
||||
Name = t.Name,
|
||||
Amount = t.Amount,
|
||||
MerchantName = t.Merchant != null ? t.Merchant.Name : null,
|
||||
PaymentMethod = t.PaymentMethodLabel
|
||||
})
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public class ReceiptRow
|
||||
@@ -186,5 +212,15 @@ namespace MoneyMap.Pages
|
||||
public decimal? Total { get; set; }
|
||||
public string StoragePath { get; set; } = "";
|
||||
}
|
||||
|
||||
public class TransactionOption
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public decimal Amount { get; set; }
|
||||
public string? MerchantName { get; set; }
|
||||
public string PaymentMethod { get; set; } = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user