Portfolio: add filtering, URL sharing, and improved tag input

- Add category filters with dynamic counts that reduce as filters
  applied
- Add ?filter[category]=value URL params for filterable links
- Add ?img= timestamp param that bypasses filters to show specific image
- Update URL when opening/navigating/closing modal for shareable links
- Backend: add /api/portfolio/filters endpoint with filter logic
- Backend: add timestamp lookup fallback for GetImage endpoint
  Frontend:
- Portfolio page: filter dropdowns, keyboard nav, mobile improvements
- ImageUpload: live tag suggestions from API, arrow/Tab navigation,
  confirmation modal before upload, mobile-optimized touch targets
- Add scrollbar-hide utility and fix filter dropdown overflow
- Move Clear all button, add vertical separator on desktop
This commit is contained in:
2026-02-20 00:32:09 +00:00
parent dfd552b02f
commit 9259de9393
15 changed files with 1560 additions and 290 deletions
+16
View File
@@ -19,6 +19,7 @@ import (
authHandlers "crussell/handlers/auth"
"crussell/handlers/bookings"
"crussell/handlers/notifications"
"crussell/handlers/portfolio"
"crussell/handlers/scheduling"
"crussell/handlers/services"
"crussell/handlers/today"
@@ -85,6 +86,21 @@ func main() {
r.Post("/register", authHandlers.RegisterHandler)
r.Post("/login", authHandlers.LoginHandler)
// --- Portfolio Routes (public read, admin write) ---
r.Route("/portfolio", func(r chi.Router) {
r.Get("/images", portfolio.ListImages)
r.Get("/tags", portfolio.ListTags)
r.Get("/filters", portfolio.ListFilters)
r.Get("/images/{id}", portfolio.GetImage)
r.Group(func(r chi.Router) {
r.Use(mw.RequireAuth)
r.Use(mw.RequireAdmin)
r.Post("/images", portfolio.UploadImage)
r.Delete("/images/{id}", portfolio.DeleteImage)
})
})
// --- Scheduling Routes ---
r.Route("/scheduling", func(r chi.Router) {
// Public GET routes