fix: admin upload action fillForm()->refreshFormData(['photo_url']) (fillForm threw in action closure); photo cache-busting (?v=updated_at) so overwritten photos don't show stale browser cache (user 'reverting to old images')

This commit is contained in:
2026-08-10 16:18:08 +10:00
parent 0a66d92d31
commit fd91d3c53e
6 changed files with 10 additions and 4 deletions

View File

@@ -3,11 +3,14 @@ package handlers
import ( import (
"net/http" "net/http"
"github.com/jackc/pgx/v5/pgtype"
"wherewoof/frontend/internal/db" "wherewoof/frontend/internal/db"
) )
// locationData is passed to the owner's location page template. // locationData is passed to the owner's location page template.
type locationData struct { type locationData struct {
UpdatedAt pgtype.Timestamptz
Lat float64 Lat float64
Lng float64 Lng float64
FinderPhone string FinderPhone string
@@ -44,6 +47,7 @@ func (a *App) ServeShort(w http.ResponseWriter, r *http.Request) {
} }
data := locationData{ data := locationData{
UpdatedAt: tag.UpdatedAt,
Lat: scan.Lat.Float64, Lat: scan.Lat.Float64,
Lng: scan.Lng.Float64, Lng: scan.Lng.Float64,
FinderPhone: scan.ScannerPhone.String, FinderPhone: scan.ScannerPhone.String,

View File

@@ -11,6 +11,7 @@ import (
) )
type publicData struct { type publicData struct {
UpdatedAt pgtype.Timestamptz
ID int64 ID int64
TagCode string TagCode string
Status string Status string
@@ -47,6 +48,7 @@ func (a *App) PublicTag(w http.ResponseWriter, r *http.Request) {
} }
pd := publicData{ pd := publicData{
UpdatedAt: tag.UpdatedAt,
ID: tag.ID, ID: tag.ID,
TagCode: tag.TagCode, TagCode: tag.TagCode,
Status: tag.Status, Status: tag.Status,

View File

@@ -26,7 +26,7 @@
<div class="mt-4 rounded-xl border border-stone-200 p-4"> <div class="mt-4 rounded-xl border border-stone-200 p-4">
<h2 class="font-bold">About your {{if .Data.ItemType}}{{.Data.ItemType}}{{else}}item{{end}}</h2> <h2 class="font-bold">About your {{if .Data.ItemType}}{{.Data.ItemType}}{{else}}item{{end}}</h2>
<div class="mt-2 flex gap-4"> <div class="mt-2 flex gap-4">
{{if .Data.PhotoURL}}<img src="{{.Data.PhotoURL}}" alt="Your item" class="h-28 w-28 rounded-lg object-cover">{{end}} {{if .Data.PhotoURL}}<img src="{{.Data.PhotoURL}}?v={{.Data.UpdatedAt.Time.Unix}}" alt="Your item" class="h-28 w-28 rounded-lg object-cover">{{end}}
<div class="text-sm"> <div class="text-sm">
{{if .Data.Name}}<p class="font-medium">{{.Data.Name}}</p>{{end}} {{if .Data.Name}}<p class="font-medium">{{.Data.Name}}</p>{{end}}
{{if .Data.Address}}<p class="text-stone-600">{{.Data.Address}}</p>{{end}} {{if .Data.Address}}<p class="text-stone-600">{{.Data.Address}}</p>{{end}}

View File

@@ -30,7 +30,7 @@
<textarea name="notes" rows="1" class="mt-0.5 w-full rounded border border-stone-300 px-2 py-1">{{if .Notes.Valid}}{{.Notes.String}}{{end}}</textarea> <textarea name="notes" rows="1" class="mt-0.5 w-full rounded border border-stone-300 px-2 py-1">{{if .Notes.Valid}}{{.Notes.String}}{{end}}</textarea>
</div> </div>
<div class="col-span-2 flex items-center gap-3"> <div class="col-span-2 flex items-center gap-3">
{{if .PhotoUrl.Valid}}<img src="{{.PhotoUrl.String}}" alt="Photo" class="h-10 w-10 rounded object-cover">{{end}} {{if .PhotoUrl.Valid}}<img src="{{.PhotoUrl.String}}?v={{.UpdatedAt.Time.Unix}}" alt="Photo" class="h-10 w-10 rounded object-cover">{{end}}
<form hx-encoding="multipart/form-data" hx-post="/account/tags/{{.ID}}/photo" hx-target="#account-panel" hx-swap="outerHTML" class="flex items-center gap-2"> <form hx-encoding="multipart/form-data" hx-post="/account/tags/{{.ID}}/photo" hx-target="#account-panel" hx-swap="outerHTML" class="flex items-center gap-2">
<input type="file" name="photo" accept="image/*" class="text-xs"> <input type="file" name="photo" accept="image/*" class="text-xs">
<button class="rounded bg-stone-200 px-2 py-1 text-xs hover:bg-stone-300">Upload photo</button> <button class="rounded bg-stone-200 px-2 py-1 text-xs hover:bg-stone-300">Upload photo</button>

View File

@@ -27,7 +27,7 @@
<div> <div>
<label class="block text-sm font-medium">Photo</label> <label class="block text-sm font-medium">Photo</label>
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<img id="photo-preview" src="{{if .Data.PhotoUrl.Valid}}{{.Data.PhotoUrl.String}}{{end}}" alt="Photo" class="h-24 w-24 rounded-lg object-cover {{if not .Data.PhotoUrl.Valid}}hidden{{end}}"> <img id="photo-preview" src="{{if .Data.PhotoUrl.Valid}}{{.Data.PhotoUrl.String}}?v={{.Data.UpdatedAt.Time.Unix}}{{end}}" alt="Photo" class="h-24 w-24 rounded-lg object-cover {{if not .Data.PhotoUrl.Valid}}hidden{{end}}">
<div> <div>
<input type="file" name="photo" accept="image/*" class="text-sm" onchange="var f=this.files[0]; if(f){ var p=document.getElementById('photo-preview'); p.classList.remove('hidden'); var r=new FileReader(); r.onload=function(e){ p.src=e.target.result; }; r.readAsDataURL(f); }"> <input type="file" name="photo" accept="image/*" class="text-sm" onchange="var f=this.files[0]; if(f){ var p=document.getElementById('photo-preview'); p.classList.remove('hidden'); var r=new FileReader(); r.onload=function(e){ p.src=e.target.result; }; r.readAsDataURL(f); }">
<p class="mt-1 text-xs text-stone-500">Choose a photo and press Save. If you choose nothing, the current photo is kept.</p> <p class="mt-1 text-xs text-stone-500">Choose a photo and press Save. If you choose nothing, the current photo is kept.</p>

View File

@@ -37,7 +37,7 @@
</div> </div>
<div class="p-6"> <div class="p-6">
{{if .Data.PhotoUrl.Valid}} {{if .Data.PhotoUrl.Valid}}
<img src="{{.Data.PhotoUrl.String}}" alt="Item photo" class="mb-4 h-56 w-full rounded-lg object-cover"> <img src="{{.Data.PhotoUrl.String}}?v={{.Data.UpdatedAt.Time.Unix}}" alt="Item photo" class="mb-4 h-56 w-full rounded-lg object-cover">
{{end}} {{end}}
{{if .Data.Description.Valid}} {{if .Data.Description.Valid}}
<p class="text-stone-700">{{.Data.Description.String}}</p> <p class="text-stone-700">{{.Data.Description.String}}</p>