- Email now includes both report (CSV/PDF) + ZIP of all receipt images
- ZIP images named {event-name}-{index}.{ext} matching list order
- Uses Go's archive/zip (stdlib, no external deps)
- Sender.SendReport now accepts []*Attachment for multiple files
- Gracefully skips missing image files with warnings
131 lines
6.9 KiB
HTML
131 lines
6.9 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>{{.Event.Name}} - ReceiptNext</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=4">
|
|
<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">← Events</a>
|
|
<h1 class="app-title" style="font-size: 1.1rem;">{{.Event.Name}}</h1>
|
|
</header>
|
|
|
|
<main class="main-content">
|
|
<!-- Receipt List -->
|
|
<div id="expense-list">
|
|
<h3 style="margin-bottom: 1rem; font-size: 1rem; font-weight: 600;">
|
|
Receipts
|
|
{{if .Event.BaseCurrency}}
|
|
<span style="font-weight: 400; color: var(--color-text-muted);">(claim in {{.Event.BaseCurrency}})</span>
|
|
{{end}}
|
|
</h3>
|
|
|
|
{{if .Expenses}}
|
|
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
|
|
{{range .Expenses}}
|
|
<div style="display: flex; align-items: center; background: var(--color-card); border: 1px solid var(--color-border); border-radius: 0.5rem; padding: 0.75rem;">
|
|
<div style="flex: 1; min-width: 0;">
|
|
<div style="font-weight: 500; color: var(--color-text);">{{.Merchant}}</div>
|
|
<div style="font-size: 0.75rem; color: var(--color-text-muted);">{{.Date}} · {{.Category}} {{if .Description}}· {{.Description}}{{end}}</div>
|
|
</div>
|
|
<div style="text-align: right; margin-right: 0.75rem;">
|
|
<div style="font-weight: 600; color: var(--color-text);">{{printf "%.2f" .Amount}} {{.Currency}}</div>
|
|
{{if .ConvertedAmount}}
|
|
<div style="font-size: 0.75rem; color: var(--color-primary);">{{printf "%.2f" .ConvertedAmount}} {{.BaseCurrency}}</div>
|
|
{{end}}
|
|
</div>
|
|
<button class="btn btn-sm" style="background: none; border: 1px solid var(--color-border); border-radius: 0.375rem; padding: 0.25rem 0.5rem; font-size: 0.75rem; cursor: pointer; flex-shrink: 0; color: var(--color-text);"
|
|
hx-get="/expenses/{{.ID}}/edit"
|
|
hx-target="#receipt-form"
|
|
hx-swap="innerHTML"
|
|
title="Edit receipt">✏️</button>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{else}}
|
|
<div style="text-align: center; padding: 2rem; color: var(--color-text-muted); font-size: 0.875rem;">
|
|
<p>No receipts yet.</p>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
|
|
<!-- Receipt Form (edit/add) -->
|
|
<div id="receipt-form" style="margin-top: 1.5rem;"></div>
|
|
|
|
<!-- Add Receipt Button -->
|
|
<div style="margin-top: 1.5rem; text-align: center;">
|
|
<label for="receipt-upload" class="btn btn-primary btn-block" style="display: block; text-align: center; cursor: pointer;">
|
|
+ Add Receipt
|
|
</label>
|
|
<input type="file" id="receipt-upload" name="receipt" accept="image/*" capture="environment" style="display: none;"
|
|
hx-post="/expenses/upload"
|
|
hx-encoding="multipart/form-data"
|
|
hx-target="#receipt-form"
|
|
hx-swap="innerHTML"
|
|
hx-indicator="#upload-indicator"
|
|
hx-trigger="change">
|
|
<div id="upload-indicator" class="htmx-indicator" style="text-align: center; padding: 1rem;">
|
|
<div class="spinner"></div>
|
|
<p>Analyzing receipt...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Submit Event (only if open and has expenses) -->
|
|
{{if and (eq .Event.Status "open") .Expenses}}
|
|
<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;">Submit Event</h3>
|
|
<form hx-post="/events/{{.Event.ID}}/file" hx-target="body" hx-push-url="true">
|
|
<div class="form-group">
|
|
<label for="file-email">Send claim to</label>
|
|
<input type="email" id="file-email" name="email" placeholder="finance@company.com" required>
|
|
</div>
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label>Format</label>
|
|
<div style="display: flex; gap: 1rem; margin-top: 0.25rem;">
|
|
<label style="display: flex; align-items: center; gap: 0.25rem;">
|
|
<input type="radio" name="format" value="csv" checked> CSV
|
|
</label>
|
|
<label style="display: flex; align-items: center; gap: 0.25rem;">
|
|
<input type="radio" name="format" value="pdf"> PDF
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Total claim</label>
|
|
<div style="font-size: 1.25rem; font-weight: 700; color: #166534; margin-top: 0.25rem;">
|
|
{{printf "%.2f" .TotalClaim}} {{.Event.BaseCurrency}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary btn-block" style="margin-top: 0.5rem;">
|
|
Submit & Close Event
|
|
</button>
|
|
</form>
|
|
</div>
|
|
{{end}}
|
|
|
|
<!-- Reopen (only if closed) -->
|
|
{{if eq .Event.Status "closed"}}
|
|
<div style="margin-top: 2rem; text-align: center;">
|
|
<button class="btn btn-secondary"
|
|
hx-put="/events/{{.Event.ID}}/reopen"
|
|
hx-target="body"
|
|
hx-push-url="true">
|
|
Reopen Event
|
|
</button>
|
|
</div>
|
|
{{end}}
|
|
</main>
|
|
</div>
|
|
</body>
|
|
</html>
|