feat: view receipt image from expense list
- Added 🖼️ button on each expense — opens full-screen lightbox
- Click anywhere on the overlay to close
- Images served via /storage/{filename} (auth-protected)
- Fixed ImagePath to store only filename (was storage/storage/...)
- Both expense_list.html fragment and event_expenses.html page updated
This commit is contained in:
parent
4593dd1f66
commit
8ff1a4175a
3 changed files with 38 additions and 9 deletions
|
|
@ -136,8 +136,11 @@ func (h *ExpenseHandler) UploadReceipt(w http.ResponseWriter, r *http.Request) {
|
|||
exchangeRate = 1.0
|
||||
}
|
||||
|
||||
// 9. Call the Gemini Vision API for AI extraction.
|
||||
receipt, aiErr := ai.ExtractReceipt(storagePath)
|
||||
// 9. Call the Gemini Vision API for AI extraction (uses full disk path).
|
||||
receipt, aiErr := ai.ExtractReceipt(filepath.Join("storage", filename))
|
||||
|
||||
// Strip the storage/ prefix so the template can build a proper URL: /storage/{file}
|
||||
storagePath = filename
|
||||
|
||||
// 10. Render the receipt_form.html fragment.
|
||||
tmpl := getTemplate("receipt_form.html")
|
||||
|
|
|
|||
|
|
@ -43,12 +43,25 @@
|
|||
<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 style="display: flex; gap: 0.25rem;">
|
||||
{{if .ImagePath}}
|
||||
<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);"
|
||||
onclick="document.getElementById('img-{{.ID}}').classList.toggle('hidden')"
|
||||
title="View receipt image">🖼️</button>
|
||||
{{end}}
|
||||
<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>
|
||||
</div>
|
||||
{{if .ImagePath}}
|
||||
<div id="img-{{.ID}}" class="hidden" style="position: fixed; inset: 0; background: rgba(0,0,0,0.85); z-index: 999; display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 1rem;"
|
||||
onclick="this.classList.add('hidden')">
|
||||
<img src="/storage/{{.ImagePath}}" alt="Receipt" style="max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 0.5rem;">
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,15 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; margin-left: 0.5rem;">
|
||||
<button class="btn btn-sm" style="background: none; border: 1px solid #d1d5db; border-radius: 0.375rem; padding: 0.25rem 0.5rem; font-size: 0.75rem; cursor: pointer;"
|
||||
<div style="display: flex; align-items: center; gap: 0.25rem;">
|
||||
{{if .ImagePath}}
|
||||
<button class="btn btn-sm" style="background: none; border: 1px solid #475569; border-radius: 0.375rem; padding: 0.25rem 0.5rem; font-size: 0.75rem; cursor: pointer;"
|
||||
onclick="document.getElementById('img-{{.ID}}').classList.toggle('hidden')"
|
||||
title="View receipt image">
|
||||
🖼️
|
||||
</button>
|
||||
{{end}}
|
||||
<button class="btn btn-sm" style="background: none; border: 1px solid #475569; border-radius: 0.375rem; padding: 0.25rem 0.5rem; font-size: 0.75rem; cursor: pointer;"
|
||||
hx-get="/expenses/{{.ID}}/edit"
|
||||
hx-target="#receipt-form"
|
||||
hx-swap="innerHTML"
|
||||
|
|
@ -37,6 +44,12 @@
|
|||
✏️
|
||||
</button>
|
||||
</div>
|
||||
{{if .ImagePath}}
|
||||
<div id="img-{{.ID}}" class="hidden" style="position: fixed; inset: 0; background: rgba(0,0,0,0.85); z-index: 999; display: flex; align-items: center; justify-content: center; cursor: pointer; padding: 1rem;"
|
||||
onclick="this.classList.add('hidden')">
|
||||
<img src="/storage/{{.ImagePath}}" alt="Receipt" style="max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 0.5rem;">
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue