NextExpense/templates/month_events.html
cclohmar d65c0cd5fa feat: add month hierarchy, AI categories, and UI polish
- Restructure hierarchy: Month → Event → Expense (new months table, FK)
- Add MonthHandler with CRUD, monthly reports, dropdown create form
- Events now scoped under months with extended ownership chain
- AI extraction: 16 specific expense categories (Airfare, Meals, etc.)
- UI: category dropdown, button-consistent cards, centered mobile shell on desktop
- Dashboard/month views show claim totals per card
- Description field now mandatory, forms simplified
- Months sorted by name chronologically (latest first)
2026-07-14 09:29:26 +00:00

159 lines
9.1 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="theme-color" content="#10b981">
<title>{{.Month.Name}} - NextExpense</title>
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
<link rel="alternate icon" href="/static/icons/icon-192.png">
<link rel="manifest" href="/manifest.json">
<link rel="stylesheet" href="/static/css/style.css?v=5">
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
</head>
<body>
<div class="app-shell">
<header class="app-header">
<a href="/dashboard" class="btn btn-secondary btn-sm"
hx-get="/dashboard" hx-target="body" hx-push-url="true">&larr; Dashboard</a>
<h1 class="app-title" style="font-size: 1.1rem;">{{.Month.Name}}</h1>
<button class="btn btn-secondary btn-sm" style="font-size: 0.75rem;"
hx-post="/logout" hx-target="body" hx-push-url="true">Logout</button>
</header>
<main class="main-content">
<!-- Breadcrumb -->
<div style="font-size: 0.75rem; color: var(--color-text-muted); margin-bottom: 1rem;">
<a href="/dashboard" style="color: var(--color-primary); text-decoration: none;"
hx-get="/dashboard" hx-target="body" hx-push-url="true">Dashboard</a>
&rsaquo; {{.Month.Name}}
</div>
<div class="dashboard-header">
<h2>Events</h2>
<button class="btn btn-primary btn-sm"
onclick="document.getElementById('create-event-form').classList.toggle('hidden')">
+ New
</button>
</div>
<div id="create-event-form" class="hidden" style="margin-bottom: 1rem;">
<div class="card" style="padding: 1rem;">
<h3 style="margin-bottom: 1rem;">New Event</h3>
<form hx-post="/months/{{.Month.ID}}/events" hx-target="body" hx-push-url="true">
<div class="form-group">
<label class="form-label">Event Name</label>
<input type="text" name="name" placeholder="Event name" required>
</div>
<div style="background: #064e3b; border: 1px solid #065f46; border-radius: 0.5rem; padding: 1rem; margin-bottom: 0.5rem;">
<div style="font-size: 0.8rem; font-weight: 600; color: #6ee7b7; margin-bottom: 0.75rem;">Conversion Rate</div>
<div class="form-row" style="gap: 1rem;">
<div style="flex: 1;">
<label class="form-label">Claim Amount</label>
<input type="number" name="sample_claim_amount" step="0.01" min="0.01" placeholder="e.g. 7.73" required>
</div>
<div style="flex: 0 0 80px;">
<label class="form-label">Currency</label>
<input type="text" name="base_currency" value="USD" required maxlength="3" style="text-transform: uppercase;">
</div>
</div>
<div class="form-row" style="gap: 1rem; margin-top: 0.5rem;">
<div style="flex: 1;">
<label class="form-label">Local Amount</label>
<input type="number" name="sample_receipt_amount" step="0.01" min="0.01" placeholder="e.g. 1000" required>
</div>
<div style="flex: 0 0 80px;">
<label class="form-label">Currency</label>
<input type="text" name="sample_receipt_currency" value="KES" required maxlength="3" style="text-transform: uppercase;">
</div>
</div>
<div style="font-size: 0.7rem; color: var(--color-text-muted); margin-top: 0.5rem;">
Rate = Claim / Local
</div>
</div>
<button type="submit" class="btn btn-primary btn-block">Create Event</button>
</form>
</div>
</div>
<div id="event-list">
{{if .Events}}
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
{{range .Events}}
<div style="display: flex; align-items: center; justify-content: space-between; background: var(--color-card); border: 1px solid var(--color-border); border-radius: 0.5rem; padding: 0.75rem 1rem;">
<div>
<div style="font-weight: 500; color: var(--color-text);">{{.Event.Name}}</div>
<div style="font-size: 0.75rem; color: var(--color-text-muted);">
{{.Event.BaseCurrency}} · {{printf "%.6f" .Event.ExchangeRate}} rate
</div>
</div>
{{if .Total}}
<div style="text-align: right; margin-right: 0.75rem;">
<div style="font-size: 0.7rem; color: var(--color-text-muted);">claim</div>
<div style="font-weight: 600; color: var(--color-primary); font-size: 0.95rem;">{{printf "%.2f" .Total}} {{.Currency}}</div>
</div>
{{end}}
<div style="display: flex; gap: 0.5rem;">
<button class="btn btn-primary btn-sm"
hx-get="/months/{{$.Month.ID}}/events/{{.Event.ID}}/expenses" hx-target="body" hx-push-url="true">
Open
</button>
{{if eq .Event.Status "open"}}
<button class="btn btn-secondary btn-sm"
hx-get="/months/{{$.Month.ID}}/events/{{.Event.ID}}/edit"
hx-target="#create-event-form"
hx-swap="innerHTML"
onclick="document.getElementById('create-event-form').classList.remove('hidden')"
style="font-size: 0.75rem;">
Edit
</button>
{{end}}
{{if eq .Event.Status "closed"}}
<button class="btn btn-secondary btn-sm"
hx-put="/months/{{$.Month.ID}}/events/{{.Event.ID}}/reopen"
hx-target="body"
hx-push-url="true"
style="font-size: 0.75rem;">
Reopen
</button>
{{end}}
</div>
</div>
{{end}}
</div>
{{else}}
<div class="empty-state" style="text-align: center; padding: 3rem; color: #94a3b8;">
<p>No events yet. Create one to get started.</p>
</div>
{{end}}
</div>
<!-- Monthly Report Generation -->
{{if .Events}}
<div style="margin-top: 2rem; border-top: 1px solid var(--color-border); padding-top: 1.5rem;">
<h3 style="font-size: 1rem; font-weight: 600; margin-bottom: 1rem;">Monthly Report</h3>
<div id="submit-error"></div>
<div id="report-section">
<form hx-post="/months/{{.Month.ID}}/generate" hx-target="#report-section" hx-swap="outerHTML"
hx-indicator="#generate-spinner">
<div class="form-group">
<label>Generate a complete report with all event expenses and receipt images.</label>
<div style="font-size: 0.85rem; color: var(--color-text-muted); margin-bottom: 0.5rem;">
Includes {{len .Events}} event(s) with all receipts.
</div>
</div>
<div id="generate-spinner" class="htmx-indicator" style="text-align: center; padding: 0.5rem;">
<div class="spinner"></div>
<p style="font-size: 0.8rem; color: var(--color-text-muted); margin-top: 0.25rem;">Packaging monthly report…</p>
</div>
<button type="submit" class="btn btn-primary btn-block" style="margin-top: 0.5rem;">
Generate Monthly Report
</button>
</form>
</div>
</div>
{{end}}
</main>
</div>
</body>
</html>