From 1c7ffb9a488ea7a1a8c1f339ee336428940b952a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galicz=20Mikl=C3=B3s?= Date: Fri, 8 Mar 2024 06:16:00 +0100 Subject: [PATCH] feat: artwork search moved to templ --- assets/templ/layouts/layout.templ | 2 +- assets/templ/pages/artworks.templ | 198 ++++++++++++++++++++ assets/views/pages/artworks.html | 99 ---------- assets/views/pages/contributors.html | 42 ----- assets/views/partials/artworks_results.html | 49 ----- esbuild.mjs | 2 + handlers/artworks/main.go | 125 ++++++------ resources/js/app.js | 7 + 8 files changed, 276 insertions(+), 248 deletions(-) create mode 100644 assets/templ/pages/artworks.templ delete mode 100644 assets/views/pages/artworks.html delete mode 100644 assets/views/pages/contributors.html delete mode 100644 assets/views/partials/artworks_results.html diff --git a/assets/templ/layouts/layout.templ b/assets/templ/layouts/layout.templ index 939c7b58..ffd3f4d3 100644 --- a/assets/templ/layouts/layout.templ +++ b/assets/templ/layouts/layout.templ @@ -39,7 +39,7 @@ templ layout_base() { - +
diff --git a/assets/templ/pages/artworks.templ b/assets/templ/pages/artworks.templ new file mode 100644 index 00000000..c2d01c58 --- /dev/null +++ b/assets/templ/pages/artworks.templ @@ -0,0 +1,198 @@ +package pages + +import ( + "github.com/blackfyre/wga/assets/templ/layouts" + // "github.com/blackfyre/wga/assets/templ/utils" + "github.com/blackfyre/wga/assets/templ/components" +) + +type ArtworkSearchDTO struct { + ArtFormOptions map[string]string + ArtTypeOptions map[string]string + ArtSchoolOptions map[string]string + ActiveFilterValues *ArtworkSearchFilterValues + ArtistNameList []string + NewFilterValues string + Results ArtworkSearchResultDTO +} + +type ArtworkSearchFilterValues struct { + ArtFormString string + ArtTypeString string + SchoolString string + Title string + ArtistString string +} + +type ArtworkSearchResultDTO struct { + ActiveFiltering bool + Artworks components.ImageGrid + Pagination string +} + +// ArtistPage is the template for the artist page +templ ArtworkSearchPage(s ArtworkSearchDTO) { + @layouts.LayoutMain() { + @ArtworkSearchBlock(s) + } +} + +templ ArtworkSeachFilterBlock(b ArtworkSearchDTO) { +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +

+ +

+
+
+ +

+ + + for _, v := range b.ArtistNameList { + + } + +

+
+
+
+} + +templ searchIndicator() { +
+
+ Search in progress... + + + +
+
+} + +templ loadIndicator() { + +} + +templ ArtworkSearchResults(r ArtworkSearchResultDTO) { +
+ // @loadIndicator() + if len(r.Artworks) > 0 { + @components.ImageGridComponent(r.Artworks, true) + } else if !r.ActiveFiltering { +
+
+ Use the filters to find artworks. +
+
+ } else { +
+
+ Sorry, no matching artworks found. +
+
+ } + if len(r.Pagination) > 10 { + + } +
+} + +templ ArtworkSearchBlock(s ArtworkSearchDTO) { +
+

Artwork search

+
+
+
+ @ArtworkSeachFilterBlock(s) +
+
+
+ @ArtworkSearchResults(s.Results) +
+
+
+} diff --git a/assets/views/pages/artworks.html b/assets/views/pages/artworks.html deleted file mode 100644 index 86cee04b..00000000 --- a/assets/views/pages/artworks.html +++ /dev/null @@ -1,99 +0,0 @@ -{{define "title"}} -Artwork search - Web Gallery of Art -{{end}} - -{{define "body"}} -{{block "artworks:content" .}} -
-

Artwork search

-
-
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
- -
-
- -
-
-
-
- -

- -

-
-
- -

- - - {{range .ArtistNameList}} - - {{end}} - -

-
- -
-
-
-
-
-
-
- Search in progress... - - -
-
- -
-
-
-{{end}} -{{end}} \ No newline at end of file diff --git a/assets/views/pages/contributors.html b/assets/views/pages/contributors.html deleted file mode 100644 index 211e2805..00000000 --- a/assets/views/pages/contributors.html +++ /dev/null @@ -1,42 +0,0 @@ -{{define "title"}} -Contributors - Web Gallery of Art -{{end}} - -{{define "body"}} -{{block "contributors:content" .}} -
-
-

- Code Contributors -

-
- {{range .Contributors}} -
-
-
-
-
-
- Placeholder image -
-
-
-

@{{.Login}}

-
-
- -
- Contributions: {{.Contributions}} commits -
-
- -
-
- {{end}} -
-
-
-{{end}} -{{end}} \ No newline at end of file diff --git a/assets/views/partials/artworks_results.html b/assets/views/partials/artworks_results.html deleted file mode 100644 index 4131a01a..00000000 --- a/assets/views/partials/artworks_results.html +++ /dev/null @@ -1,49 +0,0 @@ -{{block "artworks:results" .}} -
- {{range .Artworks}} -
-
-
-
- - - - - {{ .Title}} - -
{{.Title}} - {{ .Technique }}
-
-
-
-
-

{{ .Title}}

-

{{ .Technique }}

-
- {{safeHTML .Comment}} -
-
-
- - -
-
- {{else}} -
-
- Sorry, no matching artworks found. -
-
- {{end}} -
- -{{end}} \ No newline at end of file diff --git a/esbuild.mjs b/esbuild.mjs index 3a877b59..2d85bd40 100644 --- a/esbuild.mjs +++ b/esbuild.mjs @@ -57,6 +57,8 @@ let result = await esbuild.build({ "fa-times", "fa-2x", "mb-2", + "fa-spinner", + "fa-pulse", "has-sticky-header", "is-sticky", "is-reversed-mobile", diff --git a/handlers/artworks/main.go b/handlers/artworks/main.go index 799d07f7..73e1bf12 100644 --- a/handlers/artworks/main.go +++ b/handlers/artworks/main.go @@ -1,13 +1,15 @@ package artworks import ( + "context" "net/http" "strconv" - "github.com/blackfyre/wga/assets" + "github.com/blackfyre/wga/assets/templ/components" + "github.com/blackfyre/wga/assets/templ/pages" + tmplUtils "github.com/blackfyre/wga/assets/templ/utils" "github.com/blackfyre/wga/models" "github.com/blackfyre/wga/utils" - "github.com/blackfyre/wga/utils/jsonld" "github.com/blackfyre/wga/utils/url" "github.com/labstack/echo/v5" "github.com/pocketbase/pocketbase" @@ -16,39 +18,40 @@ import ( ) func searchPage(app *pocketbase.PocketBase, e *core.ServeEvent, c echo.Context) error { - //setup request variables - htmx := utils.IsHtmxRequest(c) - // currentUrl := c.Request().URL.String() - currentUrlPath := c.Request().URL.Path - //build filters + fullUrl := c.Scheme() + "://" + c.Request().Host + c.Request().URL.String() filters := buildFilters(app, c) - //set page + content := pages.ArtworkSearchDTO{ + ActiveFilterValues: &pages.ArtworkSearchFilterValues{ + Title: filters.Title, + SchoolString: filters.SchoolString, + ArtFormString: filters.ArtFormString, + ArtTypeString: filters.ArtTypeString, + ArtistString: filters.ArtistString, + }, + } - //check cache - td := assets.NewRenderData(app) + content.ArtFormOptions, _ = getArtFormOptions(app) + content.ArtTypeOptions, _ = getArtTypesOptions(app) + content.ArtSchoolOptions, _ = getArtSchoolOptions(app) + content.ArtistNameList, _ = getArtistNameList(app) + content.NewFilterValues = filters.BuildFilterString() - td["ArtFormOptions"], _ = getArtFormOptions(app) - td["ArtTypeOptions"], _ = getArtTypesOptions(app) - td["ArtSchoolOptions"], _ = getArtSchoolOptions(app) - td["ArtistNameList"], _ = getArtistNameList(app) - td["ActiveFilterValues"] = filters - td["NewFilterValues"] = filters.BuildFilterString() + ctx := tmplUtils.DecorateContext(context.Background(), tmplUtils.TitleKey, "Artworks Search") + ctx = tmplUtils.DecorateContext(ctx, tmplUtils.DescriptionKey, "On this page you can search for artworks by title, artist, art form, art type and art school!") + ctx = tmplUtils.DecorateContext(ctx, tmplUtils.OgUrlKey, fullUrl) - html, err := assets.Render(assets.Renderable{ - IsHtmx: htmx, - Block: "artworks:content", - Data: td, - }) + c.Response().Header().Set("HX-Push-Url", fullUrl) + err := pages.ArtworkSearchPage(content).Render(ctx, c.Response().Writer) if err != nil { - return apis.NewNotFoundError("", err) + app.Logger().Error("Error rendering artwork search page", err) + return utils.ServerFaultError(c) } - c.Response().Header().Set("HX-Push-Url", currentUrlPath+"?"+filters.BuildFilterString()) + return nil - return c.HTML(http.StatusOK, html) } func search(app *pocketbase.PocketBase, e *core.ServeEvent, c echo.Context) error { @@ -79,8 +82,6 @@ func search(app *pocketbase.PocketBase, e *core.ServeEvent, c echo.Context) erro filterString, filterParams := filters.BuildFilter() - td := assets.NewRenderData(app) - records, err := app.Dao().FindRecordsByFilter( "artworks", filterString, @@ -110,64 +111,74 @@ func search(app *pocketbase.PocketBase, e *core.ServeEvent, c echo.Context) erro recordsCount := len(totalRecords) - td["Artworks"] = []any{} + content := pages.ArtworkSearchDTO{ + Results: pages.ArtworkSearchResultDTO{ + Artworks: components.ImageGrid{}, + }, + ActiveFilterValues: &pages.ArtworkSearchFilterValues{ + Title: filters.Title, + SchoolString: filters.SchoolString, + ArtFormString: filters.ArtFormString, + ArtTypeString: filters.ArtTypeString, + ArtistString: filters.ArtistString, + }, + } + + content.ArtFormOptions, _ = getArtFormOptions(app) + content.ArtTypeOptions, _ = getArtTypesOptions(app) + content.ArtSchoolOptions, _ = getArtSchoolOptions(app) + content.ArtistNameList, _ = getArtistNameList(app) + content.NewFilterValues = filters.BuildFilterString() + content.Results.ActiveFiltering = filters.AnyFilterActive() for _, v := range records { artistIds := v.GetStringSlice("author") if len(artistIds) == 0 { - // wating for the promised logging system by @pocketbase + // waiting for the promised logging system by @pocketbase continue } artist, err := models.GetArtistById(app.Dao(), artistIds[0]) if err != nil { - // wating for the promised logging system by @pocketbase + // waiting for the promised logging system by @pocketbase continue } - jsonLd := jsonld.GenerateVisualArtworkJsonLdContent(v, c) - - jsonLd["image"] = url.GenerateFileUrl("artworks", v.GetString("id"), v.GetString("image"), "") - jsonLd["creator"] = jsonld.GenerateArtistJsonLdContent(artist, c) - jsonLd["thumbnailUrl"] = url.GenerateThumbUrl("artworks", v.GetString("id"), v.GetString("image"), "320x240", "") - - row := map[string]any{ - "Id": v.GetId(), - "Title": v.GetString("title"), - "Comment": v.GetString("comment"), - "Technique": v.GetString("technique"), - "Image": jsonLd["image"].(string), - "Thumb": jsonLd["thumbnailUrl"].(string), - "ArtworkUrl": "/artists/" + artist.Slug + "-" + artist.Id + "/artworks/" + utils.Slugify(v.GetString("title")) + "-" + v.Id, - "Jsonld": jsonLd, - } + content.Results.Artworks = append(content.Results.Artworks, components.Image{ + Url: "/artists/" + artist.Slug + "-" + artist.Id + "/artworks/" + utils.Slugify(v.GetString("title")) + "-" + v.Id, + Image: url.GenerateFileUrl("artworks", v.GetString("id"), v.GetString("image"), ""), + Thumb: url.GenerateThumbUrl("artworks", v.GetString("id"), v.GetString("image"), "320x240", ""), + Comment: v.GetString("comment"), + Title: v.GetString("title"), + Technique: v.GetString("technique"), + Id: v.GetId(), + }) - td["Artworks"] = append(td["Artworks"].([]any), row) } pUrl := "/artworks?" + filters.BuildFilterString() pHtmxUrl := "/artworks/results?" + filters.BuildFilterString() - pagination := utils.NewPagination(recordsCount, limit, page, pUrl, "artwork-search-results", pHtmxUrl) + pagination := utils.NewPagination(recordsCount, limit, page, pUrl, "", pHtmxUrl) - td["Pagination"] = pagination.Render() + content.Results.Pagination = string(pagination.Render()) - html, err := assets.Render(assets.Renderable{ - IsHtmx: htmx, - Block: "artworks:results", - Data: td, - }) + ctx := tmplUtils.DecorateContext(context.Background(), tmplUtils.TitleKey, "Artworks Search") + ctx = tmplUtils.DecorateContext(ctx, tmplUtils.DescriptionKey, "On this page you can search for artworks by title, artist, art form, art type and art school!") + ctx = tmplUtils.DecorateContext(ctx, tmplUtils.OgUrlKey, pHtmxUrl) + + c.Response().Header().Set("HX-Push-Url", pHtmxUrl) + err = pages.ArtworkSearchPage(content).Render(ctx, c.Response().Writer) if err != nil { - return apis.NewNotFoundError("", err) + app.Logger().Error("Error rendering artwork search page", err) + return utils.ServerFaultError(c) } - // c.Response().Header().Set("HX-Push-Url", currentUrl) - - return c.HTML(http.StatusOK, html) + return nil } // RegisterArtworksHandlers registers search handlers to the given PocketBase app. diff --git a/resources/js/app.js b/resources/js/app.js index 5f3cef12..685ea0fe 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -186,6 +186,13 @@ function InitEventListeners() { }); }); + //! This is a workaround, has to be removed when htmx fixes the issue + addEventListener("htmx:beforeHistorySave", () => { + document + .querySelectorAll(":disabled") + .forEach((el) => (el.disabled = false)); + }); + document.addEventListener("trix-before-initialize", () => { Trix.config.toolbar.getDefaultHTML = () => { return `