Integrate HTMX for dynamic content, add language detection middleware, and fix template rendering.
spc
- commit
- 1bc57276e473a8db6e4ac7380e5a689431be9725
- author
- bnema · · 2023-09-02 16:27 UTC
- committer
- bnema · · 2023-09-04 06:59 UTC
- parent
- ff1eebd
- signature
- unsigned
16 files · +131 -44
M internal/http/handlers/htmx.go +10 -2
| @@ -4,6 +4,7 @@ import ( | |||
| 4 | 4 | "net/http" | |
| 5 | 5 | ||
| 6 | 6 | "github.com/labstack/echo/v4" | |
| 7 | + | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | |
| 7 | 8 | "gogs.bnema.dev/gordon-echo/pkg/htmx" | |
| 8 | 9 | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| 9 | 10 | ) | |
| @@ -34,12 +35,19 @@ func HTMXHandler(c echo.Context) error { | |||
| 34 | 35 | } | |
| 35 | 36 | ||
| 36 | 37 | func htmxFragmentHandler(c echo.Context, logger *utils.Logger) error { | |
| 37 | - | fragment := c.Request().Header.Get("X-Fragment") | |
| 38 | + | fragment := c.Request().Header.Get("HX-Fragment") | |
| 38 | 39 | if fragment == "" { | |
| 39 | 40 | logger.Error().Msg("Missing fragment header") | |
| 40 | 41 | return c.String(http.StatusBadRequest, "Missing fragment header") | |
| 41 | 42 | } | |
| 42 | - | content, err := utils.GetHTMLFragmentByID(fragment) | |
| 43 | + | ||
| 44 | + | lang := c.Get(middlewares.LangKey).(string) | |
| 45 | + | staticData, err := utils.PopulateDataFromYAML(lang) | |
| 46 | + | if err != nil { | |
| 47 | + | return err | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | content, err := utils.GetHTMLFragmentByID(fragment, staticData.CurrentLang) | |
| 43 | 51 | if err != nil { | |
| 44 | 52 | logger.Error().Err(err).Msg("Failed to get fragment") | |
| 45 | 53 | return c.String(http.StatusInternalServerError, err.Error()) | |
| @@ -4,6 +4,7 @@ import ( | |||||
| 4 | "net/http" | 4 | "net/http" | ||
| 5 | 5 | ||||
| 6 | "github.com/labstack/echo/v4" | 6 | "github.com/labstack/echo/v4" | ||
| 7 | + | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | |||
| 7 | "gogs.bnema.dev/gordon-echo/pkg/htmx" | 8 | "gogs.bnema.dev/gordon-echo/pkg/htmx" | ||
| 8 | "gogs.bnema.dev/gordon-echo/pkg/utils" | 9 | "gogs.bnema.dev/gordon-echo/pkg/utils" | ||
| 9 | ) | 10 | ) | ||
| @@ -34,12 +35,19 @@ func HTMXHandler(c echo.Context) error { | |||||
| 34 | } | 35 | } | ||
| 35 | 36 | ||||
| 36 | func htmxFragmentHandler(c echo.Context, logger *utils.Logger) error { | 37 | func htmxFragmentHandler(c echo.Context, logger *utils.Logger) error { | ||
| 37 | - | fragment := c.Request().Header.Get("X-Fragment") | 38 | + | fragment := c.Request().Header.Get("HX-Fragment") |
| 38 | if fragment == "" { | 39 | if fragment == "" { | ||
| 39 | logger.Error().Msg("Missing fragment header") | 40 | logger.Error().Msg("Missing fragment header") | ||
| 40 | return c.String(http.StatusBadRequest, "Missing fragment header") | 41 | return c.String(http.StatusBadRequest, "Missing fragment header") | ||
| 41 | } | 42 | } | ||
| 42 | - | content, err := utils.GetHTMLFragmentByID(fragment) | 43 | + | |
| 44 | + | lang := c.Get(middlewares.LangKey).(string) | |||
| 45 | + | staticData, err := utils.PopulateDataFromYAML(lang) | |||
| 46 | + | if err != nil { | |||
| 47 | + | return err | |||
| 48 | + | } | |||
| 49 | + | ||||
| 50 | + | content, err := utils.GetHTMLFragmentByID(fragment, staticData.CurrentLang) | |||
| 43 | if err != nil { | 51 | if err != nil { | ||
| 44 | logger.Error().Err(err).Msg("Failed to get fragment") | 52 | logger.Error().Err(err).Msg("Failed to get fragment") | ||
| 45 | return c.String(http.StatusInternalServerError, err.Error()) | 53 | return c.String(http.StatusInternalServerError, err.Error()) | ||
A internal/http/middlewares/detect-lang.go +27 -0
| @@ -0,0 +1,27 @@ | |||
| 1 | + | package middlewares | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "strings" | |
| 5 | + | ||
| 6 | + | "github.com/labstack/echo/v4" | |
| 7 | + | ) | |
| 8 | + | ||
| 9 | + | // LangKey is a key for setting and getting the language from the context | |
| 10 | + | const LangKey = "CurrentLang" | |
| 11 | + | ||
| 12 | + | // LanguageDetectionMiddleware detects the current language and sets it in the context | |
| 13 | + | func LanguageDetectionMiddleware(next echo.HandlerFunc) echo.HandlerFunc { | |
| 14 | + | return func(c echo.Context) error { | |
| 15 | + | lang := detectCurrentLanguage(c) | |
| 16 | + | c.Set(LangKey, lang) | |
| 17 | + | return next(c) | |
| 18 | + | } | |
| 19 | + | } | |
| 20 | + | ||
| 21 | + | func detectCurrentLanguage(c echo.Context) string { | |
| 22 | + | header := c.Request().Header.Get("Accept-Language") | |
| 23 | + | if strings.Contains(header, "fr") { | |
| 24 | + | return "fr" | |
| 25 | + | } | |
| 26 | + | return "en" // Default to English | |
| 27 | + | } | |
| @@ -0,0 +1,27 @@ | |||||
| 1 | + | package middlewares | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "strings" | |||
| 5 | + | ||||
| 6 | + | "github.com/labstack/echo/v4" | |||
| 7 | + | ) | |||
| 8 | + | ||||
| 9 | + | // LangKey is a key for setting and getting the language from the context | |||
| 10 | + | const LangKey = "CurrentLang" | |||
| 11 | + | ||||
| 12 | + | // LanguageDetectionMiddleware detects the current language and sets it in the context | |||
| 13 | + | func LanguageDetectionMiddleware(next echo.HandlerFunc) echo.HandlerFunc { | |||
| 14 | + | return func(c echo.Context) error { | |||
| 15 | + | lang := detectCurrentLanguage(c) | |||
| 16 | + | c.Set(LangKey, lang) | |||
| 17 | + | return next(c) | |||
| 18 | + | } | |||
| 19 | + | } | |||
| 20 | + | ||||
| 21 | + | func detectCurrentLanguage(c echo.Context) string { | |||
| 22 | + | header := c.Request().Header.Get("Accept-Language") | |||
| 23 | + | if strings.Contains(header, "fr") { | |||
| 24 | + | return "fr" | |||
| 25 | + | } | |||
| 26 | + | return "en" // Default to English | |||
| 27 | + | } | |||
M internal/http/routes/admin_routes.go +6 -22
| @@ -3,37 +3,21 @@ package routes | |||
| 3 | 3 | import ( | |
| 4 | 4 | "fmt" | |
| 5 | 5 | "net/http" | |
| 6 | - | "strings" | |
| 7 | 6 | ||
| 8 | 7 | "github.com/labstack/echo/v4" | |
| 8 | + | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | |
| 9 | 9 | "gogs.bnema.dev/gordon-echo/internal/ui" | |
| 10 | 10 | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | - | func getPreferredLanguage(c echo.Context) string { | |
| 14 | - | header := c.Request().Header.Get("Accept-Language") | |
| 15 | - | // Check the header value, parse it and determine the primary language. | |
| 16 | - | // For this example, let's assume English and French only. | |
| 17 | - | if strings.Contains(header, "fr") { | |
| 18 | - | return "fr" | |
| 19 | - | } | |
| 20 | - | return "en" // Default to English | |
| 21 | - | } | |
| 22 | - | ||
| 23 | 13 | func AdminRoute(c echo.Context) error { | |
| 24 | - | staticData, err := utils.ReadDataFromYAML() | |
| 25 | - | if err != nil { | |
| 26 | - | return err | |
| 27 | - | } | |
| 28 | - | ||
| 29 | - | lang := getPreferredLanguage(c) | |
| 14 | + | // Retrieve the current language from the context | |
| 15 | + | lang := c.Get(middlewares.LangKey).(string) | |
| 30 | 16 | fmt.Println(lang) | |
| 31 | 17 | ||
| 32 | - | switch lang { | |
| 33 | - | case "fr": | |
| 34 | - | staticData.CurrentLang = staticData.FR | |
| 35 | - | default: // default to English if no match | |
| 36 | - | staticData.CurrentLang = staticData.EN | |
| 18 | + | staticData, err := utils.PopulateDataFromYAML(lang) | |
| 19 | + | if err != nil { | |
| 20 | + | return err | |
| 37 | 21 | } | |
| 38 | 22 | ||
| 39 | 23 | renderer, err := utils.GetRenderer("index.gohtml", ui.PublicFS, utils.NewLogger()) | |
| @@ -3,37 +3,21 @@ package routes | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | "net/http" | 5 | "net/http" | ||
| 6 | - | "strings" | |||
| 7 | 6 | ||||
| 8 | "github.com/labstack/echo/v4" | 7 | "github.com/labstack/echo/v4" | ||
| 8 | + | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | |||
| 9 | "gogs.bnema.dev/gordon-echo/internal/ui" | 9 | "gogs.bnema.dev/gordon-echo/internal/ui" | ||
| 10 | "gogs.bnema.dev/gordon-echo/pkg/utils" | 10 | "gogs.bnema.dev/gordon-echo/pkg/utils" | ||
| 11 | ) | 11 | ) | ||
| 12 | 12 | ||||
| 13 | - | func getPreferredLanguage(c echo.Context) string { | |||
| 14 | - | header := c.Request().Header.Get("Accept-Language") | |||
| 15 | - | // Check the header value, parse it and determine the primary language. | |||
| 16 | - | // For this example, let's assume English and French only. | |||
| 17 | - | if strings.Contains(header, "fr") { | |||
| 18 | - | return "fr" | |||
| 19 | - | } | |||
| 20 | - | return "en" // Default to English | |||
| 21 | - | } | |||
| 22 | - | ||||
| 23 | func AdminRoute(c echo.Context) error { | 13 | func AdminRoute(c echo.Context) error { | ||
| 24 | - | staticData, err := utils.ReadDataFromYAML() | 14 | + | // Retrieve the current language from the context |
| 25 | - | if err != nil { | 15 | + | lang := c.Get(middlewares.LangKey).(string) |
| 26 | - | return err | |||
| 27 | - | } | |||
| 28 | - | ||||
| 29 | - | lang := getPreferredLanguage(c) | |||
| 30 | fmt.Println(lang) | 16 | fmt.Println(lang) | ||
| 31 | 17 | ||||
| 32 | - | switch lang { | 18 | + | staticData, err := utils.PopulateDataFromYAML(lang) |
| 33 | - | case "fr": | 19 | + | if err != nil { |
| 34 | - | staticData.CurrentLang = staticData.FR | 20 | + | return err |
| 35 | - | default: // default to English if no match | |||
| 36 | - | staticData.CurrentLang = staticData.EN | |||
| 37 | } | 21 | } | ||
| 38 | 22 | ||||
| 39 | renderer, err := utils.GetRenderer("index.gohtml", ui.PublicFS, utils.NewLogger()) | 23 | renderer, err := utils.GetRenderer("index.gohtml", ui.PublicFS, utils.NewLogger()) | ||
M internal/http/routes/router.go +1 -0
| @@ -12,6 +12,7 @@ func NewRouter(app *app.App) *echo.Echo { | |||
| 12 | 12 | ||
| 13 | 13 | // Register middlewares | |
| 14 | 14 | e.Use(middlewares.NewRequestLoggerMiddleware(app.HTTPLogger.Logger)) | |
| 15 | + | e.Use(middlewares.LanguageDetectionMiddleware) | |
| 15 | 16 | ||
| 16 | 17 | // Register routes | |
| 17 | 18 | e = bindStaticAdminUI(e) | |
| @@ -12,6 +12,7 @@ func NewRouter(app *app.App) *echo.Echo { | |||||
| 12 | 12 | ||||
| 13 | // Register middlewares | 13 | // Register middlewares | ||
| 14 | e.Use(middlewares.NewRequestLoggerMiddleware(app.HTTPLogger.Logger)) | 14 | e.Use(middlewares.NewRequestLoggerMiddleware(app.HTTPLogger.Logger)) | ||
| 15 | + | e.Use(middlewares.LanguageDetectionMiddleware) | |||
| 15 | 16 | ||||
| 16 | // Register routes | 17 | // Register routes | ||
| 17 | e = bindStaticAdminUI(e) | 18 | e = bindStaticAdminUI(e) | ||
A internal/ui/public/assets/fonts/IBMPlex/IBMPlexSans-Bold.woff2 +0 -0 binary file: no text diff
binary file: no text diff
A internal/ui/public/assets/fonts/IBMPlex/IBMPlexSans-Light.woff2 +0 -0 binary file: no text diff
binary file: no text diff
A internal/ui/public/assets/fonts/IBMPlex/IBMPlexSans-Medium.woff2 +0 -0 binary file: no text diff
binary file: no text diff
A internal/ui/public/assets/fonts/IBMPlex/IBMPlexSans-Regular.woff2 +0 -0 binary file: no text diff
binary file: no text diff
A internal/ui/public/assets/fonts/IBMPlex/IBMPlexSans-Thin.woff2 +0 -0 binary file: no text diff
binary file: no text diff
A internal/ui/public/assets/fonts/JetBrainsMonoNerdFont/JetBrainsMonoNerdFont-Bold.woff2 +0 -0 binary file: no text diff
binary file: no text diff
A internal/ui/public/assets/fonts/JetBrainsMonoNerdFont/JetBrainsMonoNerdFont-Regular.woff2 +0 -0 binary file: no text diff
binary file: no text diff
M internal/ui/public/index.gohtml +23 -14
| @@ -1,9 +1,13 @@ | |||
| 1 | + | <!-- @format --> | |
| 2 | + | ||
| 1 | 3 | <!DOCTYPE html> | |
| 2 | 4 | <html lang="en"> | |
| 3 | 5 | <head> | |
| 4 | 6 | <meta charset="UTF-8" /> | |
| 5 | 7 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| 6 | 8 | <title>{{ .Header.Meta.Title }}</title> | |
| 9 | + | <meta name="description" content="{{ .Header.Meta.Description }}" /> | |
| 10 | + | <meta name="keywords" content="{{ .Header.Meta.Keywords }}" /> | |
| 7 | 11 | <!-- Custom CSS from assets/css --> | |
| 8 | 12 | <link rel="stylesheet" href="assets/css/custom.css" /> | |
| 9 | 13 | <!-- Alpine --> | |
| @@ -12,23 +16,28 @@ | |||
| 12 | 16 | <script src="libs/htmx.min.js"></script> | |
| 13 | 17 | <!-- UnoCSS (need to embed locally) --> | |
| 14 | 18 | <script src="https://cdn.jsdelivr.net/npm/@unocss/runtime"></script> | |
| 19 | + | <!-- color --> | |
| 15 | 20 | <!-- Custom JS from assets/js --> | |
| 16 | 21 | <script src="assets/js/custom.js"></script> | |
| 17 | 22 | </head> | |
| 18 | 23 | ||
| 19 | - | <header | |
| 20 | - | id="header" | |
| 21 | - | hx-get="/htmx" | |
| 22 | - | hx-headers='{"X-Fragment": "header"}' | |
| 23 | - | hx-trigger="load" | |
| 24 | - | hx-swap="innerHTML" | |
| 25 | - | ></header> | |
| 24 | + | <body class="bg-gray-100 flex flex-col min-h-screen"> | |
| 25 | + | <!-- Header Fragment --> | |
| 26 | + | <div | |
| 27 | + | id="header" | |
| 28 | + | hx-get="/htmx" | |
| 29 | + | hx-headers='{"HX-Fragment": "header"}' | |
| 30 | + | hx-trigger="load" | |
| 31 | + | hx-swap="outerHTML" | |
| 32 | + | ></div> | |
| 26 | 33 | ||
| 27 | - | <body | |
| 28 | - | id="app" | |
| 29 | - | hx-get="/htmx" | |
| 30 | - | hx-headers='{"X-Fragment": "hello"}' | |
| 31 | - | hx-trigger="load" | |
| 32 | - | hx-swap="innerHTML" | |
| 33 | - | ></body> | |
| 34 | + | <!-- App Fragment --> | |
| 35 | + | <div | |
| 36 | + | id="app" | |
| 37 | + | hx-get="/htmx" | |
| 38 | + | hx-headers='{"HX-Fragment": "hello"}' | |
| 39 | + | hx-trigger="load" | |
| 40 | + | hx-swap="outerHTML" | |
| 41 | + | ></div> | |
| 42 | + | </body> | |
| 34 | 43 | </html> | |
| @@ -1,9 +1,13 @@ | |||||
| 1 | + | <!-- @format --> | |||
| 2 | + | ||||
| 1 | <!DOCTYPE html> | 3 | <!DOCTYPE html> | ||
| 2 | <html lang="en"> | 4 | <html lang="en"> | ||
| 3 | <head> | 5 | <head> | ||
| 4 | <meta charset="UTF-8" /> | 6 | <meta charset="UTF-8" /> | ||
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | 7 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| 6 | <title>{{ .Header.Meta.Title }}</title> | 8 | <title>{{ .Header.Meta.Title }}</title> | ||
| 9 | + | <meta name="description" content="{{ .Header.Meta.Description }}" /> | |||
| 10 | + | <meta name="keywords" content="{{ .Header.Meta.Keywords }}" /> | |||
| 7 | <!-- Custom CSS from assets/css --> | 11 | <!-- Custom CSS from assets/css --> | ||
| 8 | <link rel="stylesheet" href="assets/css/custom.css" /> | 12 | <link rel="stylesheet" href="assets/css/custom.css" /> | ||
| 9 | <!-- Alpine --> | 13 | <!-- Alpine --> | ||
| @@ -12,23 +16,28 @@ | |||||
| 12 | <script src="libs/htmx.min.js"></script> | 16 | <script src="libs/htmx.min.js"></script> | ||
| 13 | <!-- UnoCSS (need to embed locally) --> | 17 | <!-- UnoCSS (need to embed locally) --> | ||
| 14 | <script src="https://cdn.jsdelivr.net/npm/@unocss/runtime"></script> | 18 | <script src="https://cdn.jsdelivr.net/npm/@unocss/runtime"></script> | ||
| 19 | + | <!-- color --> | |||
| 15 | <!-- Custom JS from assets/js --> | 20 | <!-- Custom JS from assets/js --> | ||
| 16 | <script src="assets/js/custom.js"></script> | 21 | <script src="assets/js/custom.js"></script> | ||
| 17 | </head> | 22 | </head> | ||
| 18 | 23 | ||||
| 19 | - | <header | 24 | + | <body class="bg-gray-100 flex flex-col min-h-screen"> |
| 20 | - | id="header" | 25 | + | <!-- Header Fragment --> |
| 21 | - | hx-get="/htmx" | 26 | + | <div |
| 22 | - | hx-headers='{"X-Fragment": "header"}' | 27 | + | id="header" |
| 23 | - | hx-trigger="load" | 28 | + | hx-get="/htmx" |
| 24 | - | hx-swap="innerHTML" | 29 | + | hx-headers='{"HX-Fragment": "header"}' |
| 25 | - | ></header> | 30 | + | hx-trigger="load" |
| 31 | + | hx-swap="outerHTML" | |||
| 32 | + | ></div> | |||
| 26 | 33 | ||||
| 27 | - | <body | 34 | + | <!-- App Fragment --> |
| 28 | - | id="app" | 35 | + | <div |
| 29 | - | hx-get="/htmx" | 36 | + | id="app" |
| 30 | - | hx-headers='{"X-Fragment": "hello"}' | 37 | + | hx-get="/htmx" |
| 31 | - | hx-trigger="load" | 38 | + | hx-headers='{"HX-Fragment": "hello"}' |
| 32 | - | hx-swap="innerHTML" | 39 | + | hx-trigger="load" |
| 33 | - | ></body> | 40 | + | hx-swap="outerHTML" |
| 41 | + | ></div> | |||
| 42 | + | </body> | |||
| 34 | </html> | 43 | </html> | ||
M internal/ui/templates/components.gohtml +2 -0
| @@ -1,3 +1,5 @@ | |||
| 1 | + | <!-- @format --> | |
| 2 | + | ||
| 1 | 3 | <div id="header"> | |
| 2 | 4 | <h1 | |
| 3 | 5 | class="text-5xl fw100 animate-bounce-alt animate-count-infinite animate-duration-1s" | |
| @@ -1,3 +1,5 @@ | |||||
| 1 | + | <!-- @format --> | |||
| 2 | + | ||||
| 1 | <div id="header"> | 3 | <div id="header"> | ||
| 2 | <h1 | 4 | <h1 | ||
| 3 | class="text-5xl fw100 animate-bounce-alt animate-count-infinite animate-duration-1s" | 5 | class="text-5xl fw100 animate-bounce-alt animate-count-infinite animate-duration-1s" | ||
M pkg/utils/fragments.go +13 -6
| @@ -1,6 +1,7 @@ | |||
| 1 | 1 | package utils | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | + | "bytes" | |
| 4 | 5 | "fmt" | |
| 5 | 6 | ||
| 6 | 7 | "github.com/PuerkitoBio/goquery" | |
| @@ -8,19 +9,25 @@ import ( | |||
| 8 | 9 | ) | |
| 9 | 10 | ||
| 10 | 11 | // GetHTMLFragmentByID returns the HTML fragment with the specified id | |
| 11 | - | func GetHTMLFragmentByID(id string) (string, error) { | |
| 12 | - | data, err := ui.TemplateFS.Open("components.gohtmlll") | |
| 12 | + | func GetHTMLFragmentByID(id string, data interface{}) (string, error) { | |
| 13 | + | // 1. Render the template | |
| 14 | + | renderer, err := GetRenderer("components.gohtml", ui.TemplateFS, NewLogger().GetTypeLogger(App)) | |
| 13 | 15 | if err != nil { | |
| 14 | - | return "", fmt.Errorf("failed to open components: %w", err) | |
| 16 | + | return "", fmt.Errorf("failed to get renderer: %w", err) | |
| 15 | 17 | } | |
| 16 | - | defer data.Close() | |
| 17 | 18 | ||
| 18 | - | doc, err := goquery.NewDocumentFromReader(data) | |
| 19 | + | renderedHTML, err := renderer.Render(data) | |
| 20 | + | if err != nil { | |
| 21 | + | return "", fmt.Errorf("failed to render template: %w", err) | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | // 2. Parse the rendered HTML with goquery | |
| 25 | + | doc, err := goquery.NewDocumentFromReader(bytes.NewReader([]byte(renderedHTML))) | |
| 19 | 26 | if err != nil { | |
| 20 | 27 | return "", fmt.Errorf("failed to parse HTML document: %w", err) | |
| 21 | 28 | } | |
| 22 | 29 | ||
| 23 | - | // Find the div with the specified id | |
| 30 | + | // 3. Find the div with the specified id | |
| 24 | 31 | fragmentContent, err := doc.Find("#" + id).Html() | |
| 25 | 32 | if err != nil { | |
| 26 | 33 | return "", fmt.Errorf("failed to extract HTML for id %s: %w", id, err) | |
| @@ -1,6 +1,7 @@ | |||||
| 1 | package utils | 1 | package utils | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | + | "bytes" | |||
| 4 | "fmt" | 5 | "fmt" | ||
| 5 | 6 | ||||
| 6 | "github.com/PuerkitoBio/goquery" | 7 | "github.com/PuerkitoBio/goquery" | ||
| @@ -8,19 +9,25 @@ import ( | |||||
| 8 | ) | 9 | ) | ||
| 9 | 10 | ||||
| 10 | // GetHTMLFragmentByID returns the HTML fragment with the specified id | 11 | // GetHTMLFragmentByID returns the HTML fragment with the specified id | ||
| 11 | - | func GetHTMLFragmentByID(id string) (string, error) { | 12 | + | func GetHTMLFragmentByID(id string, data interface{}) (string, error) { |
| 12 | - | data, err := ui.TemplateFS.Open("components.gohtmlll") | 13 | + | // 1. Render the template |
| 14 | + | renderer, err := GetRenderer("components.gohtml", ui.TemplateFS, NewLogger().GetTypeLogger(App)) | |||
| 13 | if err != nil { | 15 | if err != nil { | ||
| 14 | - | return "", fmt.Errorf("failed to open components: %w", err) | 16 | + | return "", fmt.Errorf("failed to get renderer: %w", err) |
| 15 | } | 17 | } | ||
| 16 | - | defer data.Close() | |||
| 17 | 18 | ||||
| 18 | - | doc, err := goquery.NewDocumentFromReader(data) | 19 | + | renderedHTML, err := renderer.Render(data) |
| 20 | + | if err != nil { | |||
| 21 | + | return "", fmt.Errorf("failed to render template: %w", err) | |||
| 22 | + | } | |||
| 23 | + | ||||
| 24 | + | // 2. Parse the rendered HTML with goquery | |||
| 25 | + | doc, err := goquery.NewDocumentFromReader(bytes.NewReader([]byte(renderedHTML))) | |||
| 19 | if err != nil { | 26 | if err != nil { | ||
| 20 | return "", fmt.Errorf("failed to parse HTML document: %w", err) | 27 | return "", fmt.Errorf("failed to parse HTML document: %w", err) | ||
| 21 | } | 28 | } | ||
| 22 | 29 | ||||
| 23 | - | // Find the div with the specified id | 30 | + | // 3. Find the div with the specified id |
| 24 | fragmentContent, err := doc.Find("#" + id).Html() | 31 | fragmentContent, err := doc.Find("#" + id).Html() | ||
| 25 | if err != nil { | 32 | if err != nil { | ||
| 26 | return "", fmt.Errorf("failed to extract HTML for id %s: %w", id, err) | 33 | return "", fmt.Errorf("failed to extract HTML for id %s: %w", id, err) | ||
M pkg/utils/parser.go +17 -0
| @@ -63,4 +63,21 @@ func ReadDataFromYAML() (YamlData, error) { | |||
| 63 | 63 | return data, nil | |
| 64 | 64 | } | |
| 65 | 65 | ||
| 66 | + | // PopulateDataFromYAML retrieves the data from the YAML file and sets the appropriate language data | |
| 67 | + | func PopulateDataFromYAML(currentLang string) (YamlData, error) { | |
| 68 | + | data, err := ReadDataFromYAML() | |
| 69 | + | if err != nil { | |
| 70 | + | return data, err | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | switch currentLang { | |
| 74 | + | case "fr": | |
| 75 | + | data.CurrentLang = data.FR | |
| 76 | + | default: // default to English if no match | |
| 77 | + | data.CurrentLang = data.EN | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | return data, nil | |
| 81 | + | } | |
| 82 | + | ||
| 66 | 83 | // Json Parser | |
| @@ -63,4 +63,21 @@ func ReadDataFromYAML() (YamlData, error) { | |||||
| 63 | return data, nil | 63 | return data, nil | ||
| 64 | } | 64 | } | ||
| 65 | 65 | ||||
| 66 | + | // PopulateDataFromYAML retrieves the data from the YAML file and sets the appropriate language data | |||
| 67 | + | func PopulateDataFromYAML(currentLang string) (YamlData, error) { | |||
| 68 | + | data, err := ReadDataFromYAML() | |||
| 69 | + | if err != nil { | |||
| 70 | + | return data, err | |||
| 71 | + | } | |||
| 72 | + | ||||
| 73 | + | switch currentLang { | |||
| 74 | + | case "fr": | |||
| 75 | + | data.CurrentLang = data.FR | |||
| 76 | + | default: // default to English if no match | |||
| 77 | + | data.CurrentLang = data.EN | |||
| 78 | + | } | |||
| 79 | + | ||||
| 80 | + | return data, nil | |||
| 81 | + | } | |||
| 82 | + | ||||
| 66 | // Json Parser | 83 | // Json Parser | ||
A uno.config.ts +32 -0
| @@ -0,0 +1,32 @@ | |||
| 1 | + | // uno.config.ts | |
| 2 | + | import { | |
| 3 | + | defineConfig, presetAttributify, presetIcons, | |
| 4 | + | presetTypography, presetUno, presetWebFonts, | |
| 5 | + | transformerDirectives, transformerVariantGroup | |
| 6 | + | } from 'unocss' | |
| 7 | + | ||
| 8 | + | export default defineConfig({ | |
| 9 | + | shortcuts: [ | |
| 10 | + | // ... | |
| 11 | + | ], | |
| 12 | + | theme: { | |
| 13 | + | colors: { | |
| 14 | + | // ... | |
| 15 | + | } | |
| 16 | + | }, | |
| 17 | + | presets: [ | |
| 18 | + | presetUno(), | |
| 19 | + | presetAttributify(), | |
| 20 | + | presetIcons(), | |
| 21 | + | presetTypography(), | |
| 22 | + | presetWebFonts({ | |
| 23 | + | fonts: { | |
| 24 | + | // ... | |
| 25 | + | }, | |
| 26 | + | }), | |
| 27 | + | ], | |
| 28 | + | transformers: [ | |
| 29 | + | transformerDirectives(), | |
| 30 | + | transformerVariantGroup(), | |
| 31 | + | ], | |
| 32 | + | })⏎̸ | |
| @@ -0,0 +1,32 @@ | |||||
| 1 | + | // uno.config.ts | |||
| 2 | + | import { | |||
| 3 | + | defineConfig, presetAttributify, presetIcons, | |||
| 4 | + | presetTypography, presetUno, presetWebFonts, | |||
| 5 | + | transformerDirectives, transformerVariantGroup | |||
| 6 | + | } from 'unocss' | |||
| 7 | + | ||||
| 8 | + | export default defineConfig({ | |||
| 9 | + | shortcuts: [ | |||
| 10 | + | // ... | |||
| 11 | + | ], | |||
| 12 | + | theme: { | |||
| 13 | + | colors: { | |||
| 14 | + | // ... | |||
| 15 | + | } | |||
| 16 | + | }, | |||
| 17 | + | presets: [ | |||
| 18 | + | presetUno(), | |||
| 19 | + | presetAttributify(), | |||
| 20 | + | presetIcons(), | |||
| 21 | + | presetTypography(), | |||
| 22 | + | presetWebFonts({ | |||
| 23 | + | fonts: { | |||
| 24 | + | // ... | |||
| 25 | + | }, | |||
| 26 | + | }), | |||
| 27 | + | ], | |||
| 28 | + | transformers: [ | |||
| 29 | + | transformerDirectives(), | |||
| 30 | + | transformerVariantGroup(), | |||
| 31 | + | ], | |||
| 32 | + | })⏎̸ | |||