- Two upload buttons: Camera (capture) and Upload (gallery/PDF) - PDF receipts from Uber/email now accepted and processed by Gemini Vision - PDF detection via %PDF magic bytes in both handler and AI module - Descriptions updated to reflect broader file support
146 lines
7.5 KiB
HTML
146 lines
7.5 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 Buttons -->
|
|
<div style="margin-top: 1.5rem; display: flex; gap: 0.75rem;">
|
|
<label for="receipt-camera" class="btn btn-primary" style="flex: 1; text-align: center; cursor: pointer;">
|
|
📷 Camera
|
|
</label>
|
|
<label for="receipt-upload" class="btn btn-secondary" style="flex: 1; text-align: center; cursor: pointer;">
|
|
📁 Upload
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Hidden file input: camera (mobile) -->
|
|
<input type="file" id="receipt-camera" 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">
|
|
|
|
<!-- Hidden file input: gallery / PDF upload -->
|
|
<input type="file" id="receipt-upload" name="receipt" accept="image/*,.pdf" 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>
|
|
|
|
<!-- 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>
|