diff --git a/page.go b/page.go index 77060d0..0855287 100644 --- a/page.go +++ b/page.go @@ -3,8 +3,13 @@ package ersteller_lib import ( "github.com/labstack/echo/v4" . "maragu.dev/gomponents" + . "maragu.dev/gomponents/html" ) +const DeIndexUrl = "/de/index" +const EnIndexUrl = "/en/index" +const DefaultLanguage = En + type NavItem struct { Label string Url string @@ -36,3 +41,110 @@ type PageWebsiteMetaData struct { type CreatePageFunc func(c echo.Context, metadata PageWebsiteMetaData, content ...Node) error type CreateHtmxPageFunc func(req HtmxContext, metadata PageWebsiteMetaData, content ...Node) + +func AddLanguageSelectScript(metadata PageWebsiteMetaData) Node { + script := InlineTemplate(` +(function() { + const langs = { + de: "$.DeUrl$", + en: "$.EnUrl$", + }; + const currentLang = "$.CurrentLang$"; + const defaultLang = "$.DefaultLang$"; + selectWebsiteLanguage(currentLang, langs, defaultLang); +})();`, struct { + CurrentLang string + DeUrl string + EnUrl string + DefaultLang string + }{ + CurrentLang: string(metadata.Lang), + DeUrl: DeIndexUrl, + EnUrl: EnIndexUrl, + DefaultLang: string(DefaultLanguage), + }) + return Script(Type("text/javascript"), Raw(script)) +} + +func GetNav(c HtmxContext, metadata PageWebsiteMetaData) Node { + return Nav(Class("nav"), Aria("label", "Main Menu"), + Map(metadata.NavItems, func(path ActivePath) Node { + return CreateNavItem(c, path) + }), + ) +} + +func CreateNavItem(c HtmxContext, activePath ActivePath) Node { + isActive := activePath.IsActive(c) + return A( + Href(activePath.GetPath(c.GetLanguage())), + Text(activePath.From(c)), + If(isActive, Attr("aria-current", "page")), + If(isActive, Class("selected")), + ) +} + +func GetLanguageSwitcher(c HtmxContext, metadata PageWebsiteMetaData) Node { + currentLang := c.GetLanguage() + + var currentRoute HtmxRoute + for _, route := range c.GetAllRoutes() { + if route.IsCurrentRoute(c) { + currentRoute = route + break + } + } + + return Div(Class("language-switcher"), + CreateLanguageButton(En, currentLang, currentRoute, c), + CreateLanguageButton(De, currentLang, currentRoute, c), + ) +} + +func GetFooterMenu(c HtmxContext, metadata PageWebsiteMetaData) Node { + lang := c.GetLanguage() + return Div( + Map(metadata.FooterNavItems, func(path ActivePath) Node { + return Span( + A(Href(path.GetPath(lang)), Class("footer-link"), Text(path.From(c))), + ) + }), + ) +} + +func CreateLanguageButton(lang Language, currentLang Language, route HtmxRoute, c HtmxContext) Node { + isActive := lang == currentLang + var href string + + if route != nil { + href = route.ToUrlFromContext(c, lang) + } else { + // Fallback to index page if no active path found + if lang == En { + href = EnIndexUrl + } else { + href = DeIndexUrl + } + } + + var langText string + if lang == En { + langText = "EN" + } else { + langText = "DE" + } + + // Combine classes properly + var classes string + if isActive { + classes = "language-button active" + } else { + classes = "language-button inactive" + } + + return A( + Href(href), + Text(langText), + Class(classes), + ) +} diff --git a/starter/create/create.go b/starter/create/create.go index ec55e05..934d6af 100644 --- a/starter/create/create.go +++ b/starter/create/create.go @@ -7,6 +7,7 @@ import ( "log" "os" "os/exec" + "path" "path/filepath" "runtime" "strings" @@ -99,6 +100,15 @@ func (s StarterCreator) Create() { s.copyFile("../.gitignore", ".gitignore") s.createEnvironment() + directories := []string{ + "index", + "page", + "routes", + "static", + } + for _, dir := range directories { + s.copyDirectoryRecursive(path.Join(s.thisDir, "..", dir), path.Join(s.params.ProjectDir, dir)) + } } func (s StarterCreator) createEnvironment() { @@ -144,6 +154,26 @@ func (s StarterCreator) copyFile(src string, dst string) { must(os.WriteFile(filepath.Join(s.params.ProjectDir, dst), content, 0o644)) } +func (s StarterCreator) copyDirectoryRecursive(src string, dst string) { + must(os.MkdirAll(dst, 0o755)) + entries, err := os.ReadDir(src) + Debug() + must(err) + for _, entry := range entries { + srcPath := filepath.Join(src, entry.Name()) + dstPath := filepath.Join(dst, entry.Name()) + Debug("copying srcPath: ", srcPath, " to dstPath: ", dstPath) + if entry.IsDir() { + s.copyDirectoryRecursive(srcPath, dstPath) + return + } + content, err := os.ReadFile(srcPath) + content = s.replaceImports(content) + must(err) + must(os.WriteFile(filepath.Join(dstPath), content, 0o644)) + } +} + func (s StarterCreator) copySchema() { content, err := os.ReadFile(filepath.Join(s.thisDir, "../../schema_template.prisma")) must(err) @@ -200,7 +230,7 @@ func (s StarterCreator) executeGetPrismaClient() { func (s StarterCreator) replaceImports(content []byte) []byte { contentString := string(content) - contentString = strings.ReplaceAll(contentString, "\"ersteller-lib/starter/env\"", fmt.Sprint("\"", s.params.ModuleName, "/env\"")) + contentString = strings.ReplaceAll(contentString, "\"ersteller-lib/starter/", fmt.Sprint("\"", s.params.ModuleName, "/")) return []byte(contentString) } diff --git a/starter/page/page.go b/starter/page/page.go index 51986d6..29eacae 100644 --- a/starter/page/page.go +++ b/starter/page/page.go @@ -10,10 +10,6 @@ import ( . "maragu.dev/gomponents/html" ) -const DeIndexUrl = "/de/index" -const EnIndexUrl = "/en/index" -const DefaultLanguage = En - var _texts *Texts func CreatePage(req HtmxContext, metadata PageWebsiteMetaData, content ...Node) { @@ -54,7 +50,7 @@ func CreatePage(req HtmxContext, metadata PageWebsiteMetaData, content ...Node) Map(scripts, func(s string) Node { return Script(Type("text/javascript"), Src(s)) }), - addLanguageSelectScript(metadata), + AddLanguageSelectScript(metadata), }, Body: []Node{ Header(Class("header"), @@ -63,8 +59,8 @@ func CreatePage(req HtmxContext, metadata PageWebsiteMetaData, content ...Node) Span(Class("logo-icon"), Text("🚀")), Span(Class("logo-text"), Text("White Label App")), ), - getNav(req, metadata), - getLanguageSwitcher(req, metadata), + GetNav(req, metadata), + GetLanguageSwitcher(req, metadata), ), ), Div(Class("container"), @@ -72,7 +68,7 @@ func CreatePage(req HtmxContext, metadata PageWebsiteMetaData, content ...Node) ), Footer(Class("footer"), Div(Class("footer-menu"), Aria("label", "Footer Menu"), - getFooterMenu(req, metadata), + GetFooterMenu(req, metadata), ), P(Class("footer-disclaimer"), Text(getTexts().disclaimer.From(req))), P(Text(getTexts().getCopyright(req.GetLanguage()))), @@ -82,113 +78,6 @@ func CreatePage(req HtmxContext, metadata PageWebsiteMetaData, content ...Node) req.Render(page) } -func addLanguageSelectScript(metadata PageWebsiteMetaData) Node { - script := InlineTemplate(` -(function() { - const langs = { - de: "$.DeUrl$", - en: "$.EnUrl$", - }; - const currentLang = "$.CurrentLang$"; - const defaultLang = "$.DefaultLang$"; - selectWebsiteLanguage(currentLang, langs, defaultLang); -})();`, struct { - CurrentLang string - DeUrl string - EnUrl string - DefaultLang string - }{ - CurrentLang: string(metadata.Lang), - DeUrl: DeIndexUrl, - EnUrl: EnIndexUrl, - DefaultLang: string(DefaultLanguage), - }) - return Script(Type("text/javascript"), Raw(script)) -} - -func getNav(c HtmxContext, metadata PageWebsiteMetaData) Node { - return Nav(Class("nav"), Aria("label", "Main Menu"), - Map(metadata.NavItems, func(path ActivePath) Node { - return createNavItem(c, path) - }), - ) -} - -func createNavItem(c HtmxContext, activePath ActivePath) Node { - isActive := activePath.IsActive(c) - return A( - Href(activePath.GetPath(c.GetLanguage())), - Text(activePath.From(c)), - If(isActive, Attr("aria-current", "page")), - If(isActive, Class("selected")), - ) -} - -func getLanguageSwitcher(c HtmxContext, metadata PageWebsiteMetaData) Node { - currentLang := c.GetLanguage() - - var currentRoute HtmxRoute - for _, route := range c.GetAllRoutes() { - if route.IsCurrentRoute(c) { - currentRoute = route - break - } - } - - return Div(Class("language-switcher"), - createLanguageButton(En, currentLang, currentRoute, c), - createLanguageButton(De, currentLang, currentRoute, c), - ) -} - -func getFooterMenu(c HtmxContext, metadata PageWebsiteMetaData) Node { - lang := c.GetLanguage() - return Div( - Map(metadata.FooterNavItems, func(path ActivePath) Node { - return Span( - A(Href(path.GetPath(lang)), Class("footer-link"), Text(path.From(c))), - ) - }), - ) -} - -func createLanguageButton(lang Language, currentLang Language, route HtmxRoute, c HtmxContext) Node { - isActive := lang == currentLang - var href string - - if route != nil { - href = route.ToUrlFromContext(c, lang) - } else { - // Fallback to index page if no active path found - if lang == En { - href = EnIndexUrl - } else { - href = DeIndexUrl - } - } - - var langText string - if lang == En { - langText = "EN" - } else { - langText = "DE" - } - - // Combine classes properly - var classes string - if isActive { - classes = "language-button active" - } else { - classes = "language-button inactive" - } - - return A( - Href(href), - Text(langText), - Class(classes), - ) -} - type Texts struct { disclaimer I18nText copyright I18nText