feat(router): add collection modal routes for HTMX

Add three new frontend routes to support HTMX-powered modal dialogs:

1. GET /collections/create-modal
   - Renders empty collection creation modal
   - Uses CollectionModal template with empty CollectionData

2. GET /collections/:id/edit-modal
   - Fetches collection by ID from database
   - Pre-populates modal with existing collection data
   - Returns 400 for invalid UUID, 404 if collection not found

3. GET /collections/restore-modal
   - Renders system collection restoration modal
   - Allows users to restore deleted system collections

Route registration order:
- /collections/:id/edit-modal must be registered before /collections/:id
  to avoid path conflicts in Echo's router

These routes enable the collections page to load modals dynamically via
HTMX (hx-get) instead of embedding modal HTML in the base page.
This commit is contained in:
2026-03-01 21:00:00 -05:00
parent 511ae66688
commit 87f53b56e8
+57
View File
@@ -238,6 +238,63 @@ func registerFrontendRoutes(cfg *Config) {
return c.HTML(http.StatusOK, buf.String())
})
// Collection create modal
frontendProtected.GET("/collections/create-modal", func(c echo.Context) error {
var buf bytes.Buffer
err := templates.CollectionModal(templates.CollectionData{}).Render(c.Request().Context(), &buf)
if err != nil {
return err
}
return c.HTML(http.StatusOK, buf.String())
})
// Collection edit modal
frontendProtected.GET("/collections/:id/edit-modal", func(c echo.Context) error {
// Parse collection ID
collectionID := c.Param("id")
collUUID, err := uuid.Parse(collectionID)
if err != nil {
return c.HTML(http.StatusBadRequest, "<div>Invalid collection ID</div>")
}
// Fetch collection data
collection, err := cfg.Queries.GetCollection(c.Request().Context(), pgtype.UUID{Bytes: collUUID, Valid: true})
if err != nil {
return c.HTML(http.StatusNotFound, "<div>Collection not found</div>")
}
// Convert to template type
colData := templates.CollectionData{
ID: uuid.UUID(collection.ID.Bytes).String(),
Name: collection.Name,
Description: getText(collection.Description),
Color: getText(collection.Color),
Icon: getText(collection.Icon),
}
// Ensure default color if empty
if colData.Color == "" {
colData.Color = "blue"
}
var buf bytes.Buffer
err = templates.CollectionModal(colData).Render(c.Request().Context(), &buf)
if err != nil {
return err
}
return c.HTML(http.StatusOK, buf.String())
})
// Restore system collection modal
frontendProtected.GET("/collections/restore-modal", func(c echo.Context) error {
var buf bytes.Buffer
err := templates.RestoreSystemCollectionModal().Render(c.Request().Context(), &buf)
if err != nil {
return err
}
return c.HTML(http.StatusOK, buf.String())
})
// Collection detail page (works for both system and user collections)
frontendProtected.GET("/collections/:id", func(c echo.Context) error {
user, err := getTemplateUserWithTheme(c, cfg)