From d915e1c2ae71b9d4e4ded6ce0b80778372917450 Mon Sep 17 00:00:00 2001 From: AlDu2407 <40807522+AlDu2407@users.noreply.github.com> Date: Sat, 14 Dec 2024 22:41:21 +0100 Subject: [PATCH] Major overhaul of Urchin (#97) - Added TailwindCSS to Urchin. - Updated Makefile to download TailwindCSS (currently only Linux is supported) - Refactored templates to share similar logic between templates. - Redesigned UI for Urchin. - Added layout page shared between all sub pages. - Added dark mode support. - Added new `renderHtml` method to reduce duplicate code in go. - Added self-designed minimalistic 'sea urchin' favicon. :D - Added missing sites `about` and `services`. - Added `Not Found` handling if a resource is requested that is not present, displaying an error message while keeping the layout page. - Restructured `static` folder to include stylesheets, scripts and assets. --------- Co-authored-by: matheusgomes28 --- .air.toml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/failfast.yml | 2 +- .github/workflows/test.yml | 2 +- .gitignore | 6 + .vscode/launch.json | 2 - Makefile | 14 ++- README.md | 4 +- app/about.go | 12 ++ app/app.go | 18 +++ app/contact.go | 9 +- app/image.go | 10 +- app/page.go | 9 +- app/post.go | 8 +- app/renderer.go | 15 +++ app/services.go | 12 ++ docker/Dockerfile | 1 - docker/docker-compose.yml | 4 +- static/assets/favicon.ico | Bin 0 -> 9510 bytes static/{ => assets}/nohead.gif | Bin static/{ => assets}/urchin-architecture.png | Bin static/css/custom.css | 9 ++ static/{ => css}/simple.min.css | 0 static/scripts/application.js | 33 ++++++ static/{ => scripts}/client-side-templates.js | 0 static/{ => scripts}/htmx.min.js | 0 tailwind.config.js | 33 ++++++ views/about.templ | 9 ++ views/contact-failure.templ | 4 +- views/contact-success.templ | 4 +- views/contact.templ | 109 ++++++++---------- views/error.templ | 32 ++--- views/footer.templ | 5 +- views/header.templ | 54 ++++++++- views/image.templ | 36 ++---- views/images.templ | 82 ++++--------- views/index.templ | 53 ++++----- views/layout.templ | 34 ++++++ views/not-found.templ | 11 ++ views/page.templ | 23 +--- views/post.templ | 34 ++---- views/services.templ | 10 ++ views/shared.templ | 5 + 43 files changed, 403 insertions(+), 309 deletions(-) create mode 100644 app/about.go create mode 100644 app/services.go create mode 100644 static/assets/favicon.ico rename static/{ => assets}/nohead.gif (100%) rename static/{ => assets}/urchin-architecture.png (100%) create mode 100644 static/css/custom.css rename static/{ => css}/simple.min.css (100%) create mode 100644 static/scripts/application.js rename static/{ => scripts}/client-side-templates.js (100%) rename static/{ => scripts}/htmx.min.js (100%) create mode 100644 tailwind.config.js create mode 100644 views/about.templ create mode 100644 views/layout.templ create mode 100644 views/not-found.templ create mode 100644 views/services.templ create mode 100644 views/shared.templ diff --git a/.air.toml b/.air.toml index b9d186a..2330c51 100644 --- a/.air.toml +++ b/.air.toml @@ -14,7 +14,7 @@ tmp_dir = "tmp" exclude_unchanged = false follow_symlink = false include_dir = [] - include_ext = ["go", "tpl", "tmpl", "templ", "html"] + include_ext = ["go", "tpl", "tmpl", "templ", "html", "js"] include_file = [] kill_delay = "0s" log = "build-errors.log" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 09ae49f..ff02476 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest container: - image: mattgomes28/urchin-golang:0.2 + image: mattgomes28/urchin-golang:0.4 options: --user 1001 steps: diff --git a/.github/workflows/failfast.yml b/.github/workflows/failfast.yml index 7bf49be..c8dcb9b 100644 --- a/.github/workflows/failfast.yml +++ b/.github/workflows/failfast.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest container: - image: mattgomes28/urchin-golang:0.2 + image: mattgomes28/urchin-golang:0.4 options: --user 1001 steps: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c087a28..315cf1e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest container: - image: mattgomes28/urchin-golang:0.2 + image: mattgomes28/urchin-golang:0.4 options: --user 1001 steps: diff --git a/.gitignore b/.gitignore index 34bb690..caa1dfe 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,9 @@ images/ tests/helpers/migrations + +# The name of the TailwindCSS CLI +tailwindcss + +# Generated TailwindCSS styles +static/css/style.css \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 4b515d9..57de0c8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,6 @@ "mode": "auto", "program": "${workspaceFolder}/cmd/urchin", "cwd": "${workspaceFolder}", - "envFile": "${workspaceFolder}/envfile", "args": [ "--config", "${workspaceFolder}/urchin_config.toml" @@ -24,7 +23,6 @@ "mode": "auto", "program": "${workspaceFolder}/cmd/urchin-admin", "cwd": "${workspaceFolder}", - "envFile": "${workspaceFolder}/envfile", "args": [ "--config", "${workspaceFolder}/urchin_config.toml" diff --git a/Makefile b/Makefile index bdb3dfc..1aaae94 100644 --- a/Makefile +++ b/Makefile @@ -14,10 +14,11 @@ all: build test prepare_env: cp -r migrations tests/helpers/ -build: prepare_env +build: prepare_env install-tailwindcss $(TEMPL) generate GIN_MODE=release $(GOCMD) build -ldflags "-s" -v -o $(BUILD_DIR)/$(BINARY_NAME) $(URCHIN_DIR) GIN_MODE=release $(GOCMD) build -ldflags "-s" -v -o $(BUILD_DIR)/$(ADMIN_BINARY_NAME) $(URCHIN_ADMIN_DIR) + ./tailwindcss -i ./static/css/custom.css -o ./static/css/style.css --minify test: prepare_env $(GOCMD) test -v ./... @@ -26,9 +27,18 @@ clean: $(GOCMD) clean rm -rf $(BUILD_DIR) +# TODO: For now we support only the linux version of tailwindcss, has to be updated in the future to support Windows and MacOS as well. install-tools: go install github.com/pressly/goose/v3/cmd/goose@v3.18.0 go install github.com/a-h/templ/cmd/templ@v0.2.543 go install github.com/cosmtrek/air@v1.49.0 -.PHONY: all build test clean +install-tailwindcss: + if [ ! -f tailwindcss ]; then \ + wget -q https://github.com/tailwindlabs/tailwindcss/releases/download/v3.4.16/tailwindcss-linux-x64 \ + && echo "33f254b54c8754f16efbe2be1de38ca25192630dc36f164595a770d4bbf4d893 tailwindcss-linux-x64" | sha256sum -c \ + && chmod +x tailwindcss-linux-x64 \ + && mv tailwindcss-linux-x64 tailwindcss; \ + fi + +.PHONY: all build test clean install-tailwindcss diff --git a/README.md b/README.md index 294d831..c52835b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Urchin is a headless CMS (Content Management System) written in Golang, designed create a website or blog, with any template you like, in only a few commands. -![Really no head?](static/nohead.gif "So no head meme?") +![Really no head?](static/assets/nohead.gif "So no head meme?") ## Features 🚀 @@ -122,7 +122,7 @@ The plan is to have two main applications: the public facing application to serve the content through a website, and the admin application that can be hidden, where users can modify the settings, add posts, pages, etc. -![diagram of urchin's architecture](static/urchin-architecture.png "Urchin Application Architecture") +![diagram of urchin's architecture](static/assets/urchin-architecture.png "Urchin Application Architecture") In the above image, you can see the two applications running alongside, and they share a database connection where the data is actually stored. diff --git a/app/about.go b/app/about.go new file mode 100644 index 0000000..61b7071 --- /dev/null +++ b/app/about.go @@ -0,0 +1,12 @@ +package app + +import ( + "github.com/gin-gonic/gin" + "github.com/matheusgomes28/urchin/common" + "github.com/matheusgomes28/urchin/database" + "github.com/matheusgomes28/urchin/views" +) + +func aboutHandler(c *gin.Context, app_settings common.AppSettings, db database.Database) ([]byte, error) { + return renderHtml(c, views.MakeAboutPage(app_settings.AppNavbar.Links)) +} diff --git a/app/app.go b/app/app.go index 7b0413b..24330ff 100644 --- a/app/app.go +++ b/app/app.go @@ -25,6 +25,8 @@ func SetupRoutes(app_settings common.AppSettings, database database.Database) *g cache := MakeCache(4, time.Minute*10, &TimeValidator{}) addCachableHandler(r, "GET", "/", homeHandler, &cache, app_settings, database) addCachableHandler(r, "GET", "/contact", contactHandler, &cache, app_settings, database) + addCachableHandler(r, "GET", "/about", aboutHandler, &cache, app_settings, database) + addCachableHandler(r, "GET", "/services", servicesHandler, &cache, app_settings, database) addCachableHandler(r, "GET", "/post/:id", postHandler, &cache, app_settings, database) addCachableHandler(r, "GET", "/images/:name", imageHandler, &cache, app_settings, database) addCachableHandler(r, "GET", "/images", imagesHandler, &cache, app_settings, database) @@ -45,6 +47,8 @@ func SetupRoutes(app_settings common.AppSettings, database database.Database) *g // Where all the static files (css, js, etc) are served from r.Static("/static", "./static") + r.NoRoute(notFoundHandler(app_settings)) + return r } @@ -127,3 +131,17 @@ func homeHandler(c *gin.Context, settings common.AppSettings, db database.Databa return html_buffer.Bytes(), nil } + +func notFoundHandler(app_settings common.AppSettings) func(*gin.Context) { + handler := func(c *gin.Context) { + buffer, err := renderHtml(c, views.MakeNotFoundPage(app_settings.AppNavbar.Links)) + if err != nil { + c.JSON(http.StatusInternalServerError, common.ErrorRes("could not render HTML", err)) + return + } + + c.Data(http.StatusOK, "text/html; charset=utf-8", buffer) + } + + return handler +} diff --git a/app/contact.go b/app/contact.go index 0a1e13a..b558a55 100644 --- a/app/contact.go +++ b/app/contact.go @@ -1,7 +1,6 @@ package app import ( - "bytes" "encoding/json" "fmt" "io" @@ -137,11 +136,5 @@ func makeContactFormHandler(app_settings common.AppSettings) func(*gin.Context) // TODO : This is a duplicate of the index handler... abstract func contactHandler(c *gin.Context, app_settings common.AppSettings, db database.Database) ([]byte, error) { - index_view := views.MakeContactPage(app_settings.AppNavbar.Links, app_settings.RecaptchaSiteKey) - html_buffer := bytes.NewBuffer(nil) - if err := index_view.Render(c, html_buffer); err != nil { - log.Error().Msgf("could not render: %v", err) - } - - return html_buffer.Bytes(), nil + return renderHtml(c, views.MakeContactPage(app_settings.AppNavbar.Links, app_settings.RecaptchaSiteKey)) } diff --git a/app/image.go b/app/image.go index 984efc7..4b39e3c 100644 --- a/app/image.go +++ b/app/image.go @@ -102,14 +102,6 @@ func imageHandler(c *gin.Context, app_settings common.AppSettings, database data Name: filename, Ext: ext, } - index_view := views.MakeImagePage(image, app_settings.AppNavbar.Links) - html_buffer := bytes.NewBuffer(nil) - err := index_view.Render(c, html_buffer) - if err != nil { - log.Error().Msgf("Could not render index: %v", err) - return []byte{}, err - } - - return html_buffer.Bytes(), nil + return renderHtml(c, views.MakeImagePage(image, app_settings.AppNavbar.Links)) } diff --git a/app/page.go b/app/page.go index 31931ff..50ef2a4 100644 --- a/app/page.go +++ b/app/page.go @@ -1,14 +1,12 @@ package app import ( - "bytes" "net/http" "github.com/gin-gonic/gin" "github.com/matheusgomes28/urchin/common" "github.com/matheusgomes28/urchin/database" "github.com/matheusgomes28/urchin/views" - "github.com/rs/zerolog/log" ) func pageHandler(c *gin.Context, app_settings common.AppSettings, database database.Database) ([]byte, error) { @@ -33,11 +31,6 @@ func pageHandler(c *gin.Context, app_settings common.AppSettings, database datab // Generate HTML page page.Content = string(mdToHTML([]byte(page.Content))) - post_view := views.MakePage(page.Title, page.Content, app_settings.AppNavbar.Links) - html_buffer := bytes.NewBuffer(nil) - if err = post_view.Render(c, html_buffer); err != nil { - log.Error().Msgf("could not render: %v", err) - } - return html_buffer.Bytes(), nil + return renderHtml(c, views.MakePage(page.Title, page.Content, app_settings.AppNavbar.Links)) } diff --git a/app/post.go b/app/post.go index 66361b1..9a64538 100644 --- a/app/post.go +++ b/app/post.go @@ -1,7 +1,6 @@ package app import ( - "bytes" "net/http" "github.com/gin-gonic/gin" @@ -70,11 +69,6 @@ func postHandler(c *gin.Context, app_settings common.AppSettings, database datab // Generate HTML page post.Content = string(mdToHTML([]byte(post.Content))) - post_view := views.MakePostPage(post.Title, post.Content, app_settings.AppNavbar.Links) - html_buffer := bytes.NewBuffer(nil) - if err = post_view.Render(c, html_buffer); err != nil { - log.Error().Msgf("could not render: %v", err) - } - return html_buffer.Bytes(), nil + return renderHtml(c, views.MakePostPage(post.Title, post.Content, app_settings.AppNavbar.Links)) } diff --git a/app/renderer.go b/app/renderer.go index 9ae4b1f..7b5454f 100644 --- a/app/renderer.go +++ b/app/renderer.go @@ -1,8 +1,11 @@ package app import ( + "bytes" + "github.com/a-h/templ" "github.com/gin-gonic/gin" + "github.com/rs/zerolog/log" ) // / This function will render the templ component into @@ -11,3 +14,15 @@ func render(c *gin.Context, status int, template templ.Component) error { c.Status(status) return template.Render(c.Request.Context(), c.Writer) } + +func renderHtml(c *gin.Context, template templ.Component) ([]byte, error) { + html_buffer := bytes.NewBuffer(nil) + + err := template.Render(c, html_buffer) + if err != nil { + log.Error().Msgf("Could not render index: %v", err) + return []byte{}, err + } + + return html_buffer.Bytes(), nil +} diff --git a/app/services.go b/app/services.go new file mode 100644 index 0000000..369f8a3 --- /dev/null +++ b/app/services.go @@ -0,0 +1,12 @@ +package app + +import ( + "github.com/gin-gonic/gin" + "github.com/matheusgomes28/urchin/common" + "github.com/matheusgomes28/urchin/database" + "github.com/matheusgomes28/urchin/views" +) + +func servicesHandler(c *gin.Context, app_settings common.AppSettings, db database.Database) ([]byte, error) { + return renderHtml(c, views.MakeServicesPage(app_settings.AppNavbar.Links)) +} diff --git a/docker/Dockerfile b/docker/Dockerfile index 65fd331..6b116cf 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -32,7 +32,6 @@ RUN apt-get update \ && wget https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh -P golangci-install \ && sh ./golangci-install/install.sh -b /usr/local/bin v1.56.2 \ && rm -rf ./golangci-install \ - && apt-get remove -y wget \ && apt-get clean \ && apt-get autoremove -y \ && apt-get autoremove --purge -y \ diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index a40ae2d..b25dd53 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,6 +1,6 @@ services: urchin_app: - image: mattgomes28/urchin-golang:0.3 + image: mattgomes28/urchin-golang:0.4 ports: - "8080:8080" volumes: @@ -18,7 +18,7 @@ services: - common-net urchin_admin_app: - image: mattgomes28/urchin-golang:0.3 + image: mattgomes28/urchin-golang:0.4 ports: - "8081:8081" volumes: diff --git a/static/assets/favicon.ico b/static/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b24b3d8a772be3d4c49b551e4283260f93f07479 GIT binary patch literal 9510 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 to send message from { email }! -

Your message could not be sent. Error: { err }

+

Failed to send message from { email }!

+

Your message could not be sent. Error: { err }

} diff --git a/views/contact-success.templ b/views/contact-success.templ index 60507f3..936cadf 100644 --- a/views/contact-success.templ +++ b/views/contact-success.templ @@ -1,6 +1,6 @@ package views templ MakeContactSuccess(email string, name string) { -

Message Sent From { email }!

-

Thank you { name }. The message was successfully sent.

+

Message Sent From { email }!

+

Thank you { name }. The message was successfully sent.

} diff --git a/views/contact.templ b/views/contact.templ index 17e3a6d..cc23ed0 100644 --- a/views/contact.templ +++ b/views/contact.templ @@ -3,73 +3,56 @@ package views import "github.com/matheusgomes28/urchin/common" templ MakeContactFormWithRecaptcha(recaptcha_sitekey string) { -
- -

- - -

- -
-

- - - -
+
+ + + + + + + + + +
+ +
+
} templ MakeContactForm() { -
- -

- - -

+ + + + + + + + + +
+ +
+
+} -
-

- - +templ makeContact(recaptcha_sitekey string) { +
+

Contact Us

+ if len(recaptcha_sitekey) > 0 { + @MakeContactFormWithRecaptcha(recaptcha_sitekey) + } else { + @MakeContactForm() + } +
+ if len(recaptcha_sitekey) > 0 { + + } } templ MakeContactPage(links []common.Link, recaptcha_sitekey string) { - - - - - - Menu and Contact Form - - - - - if len(recaptcha_sitekey) > 0 { - - } - - - - @MakeNavBar(links) -
-
-

Contact Us

- if len(recaptcha_sitekey) > 0 { - @MakeContactFormWithRecaptcha(recaptcha_sitekey) - } else { - @MakeContactForm() - } -
-
- @MakeFooter() - - -} + @MakeLayout("Menu and Contact Form", links, makeContact(recaptcha_sitekey)) +} \ No newline at end of file diff --git a/views/error.templ b/views/error.templ index 00b3934..9385983 100644 --- a/views/error.templ +++ b/views/error.templ @@ -2,31 +2,13 @@ package views import "github.com/matheusgomes28/urchin/common" +templ makeError(error_str string) { +
+

Error Occurred!

+

{ error_str }

+
+} templ MakeErrorPage(error_str string, links []common.Link) { - - - - - - Error! - - - - - - - - @MakeNavBar(links) -
-
-

Error Occurred!

-

{ error_str }

-
-
- - @MakeFooter() - - - + @MakeLayout("Error!", links, makeError(error_str)) } diff --git a/views/footer.templ b/views/footer.templ index 4eb0576..432f677 100644 --- a/views/footer.templ +++ b/views/footer.templ @@ -1,4 +1,7 @@ package views + templ MakeFooter() { - +
+ © 2024 Urchin. All rights reserved. +
} \ No newline at end of file diff --git a/views/header.templ b/views/header.templ index 524d980..4b7ae8d 100644 --- a/views/header.templ +++ b/views/header.templ @@ -2,15 +2,57 @@ package views import "github.com/matheusgomes28/urchin/common" +templ makeDarkModeButton() { + +} + templ MakeNavBar(links []common.Link) {
-
-
-} +} \ No newline at end of file diff --git a/views/image.templ b/views/image.templ index 7c2a163..575de90 100644 --- a/views/image.templ +++ b/views/image.templ @@ -1,35 +1,15 @@ package views import ( - "fmt" - . "github.com/matheusgomes28/urchin/common" + "fmt" + . "github.com/matheusgomes28/urchin/common" ) -templ MakeImagePage(image Image, links []Link) { - - - - - - Menu and Contact Form - - - - - - - - @MakeNavBar(links) -
-
-

{ image.Name }

- -
-
- Back - - @MakeFooter() - +templ makeImage(image Image) { +

{ image.Name }

+ +} - +templ MakeImagePage(image Image, links []Link) { + @MakeLayout(image.Name, links, makeImage(image)) } diff --git a/views/images.templ b/views/images.templ index 8968ca9..557093d 100644 --- a/views/images.templ +++ b/views/images.templ @@ -1,64 +1,30 @@ package views import ( - "fmt" - . "github.com/matheusgomes28/urchin/common" + "fmt" + . "github.com/matheusgomes28/urchin/common" ) -templ MakeImagesPage(images []Image, links []Link) { - - - - - - Menu and Contact Form - - - - - - - - - @MakeNavBar(links) -
-
- if len(images) == 0 { -

No images uploaded.

- } else { - for _, image := range images { - -
- - {image.Name} -
-
- } - } -
-
- - @MakeFooter() - - - +templ makeImages(images []Image) { + if len(images) == 0 { +
+

No images uploaded

+
+ } else { +
+ for _, image := range images { + + +
+

{image.Name}

+
+
+ } +
+ } } + +templ MakeImagesPage(images []Image, links []Link) { + @MakeLayout("Images", links, makeImages(images)) +} \ No newline at end of file diff --git a/views/index.templ b/views/index.templ index d0b3edc..9a4ee79 100644 --- a/views/index.templ +++ b/views/index.templ @@ -1,37 +1,28 @@ package views import ( - "fmt" - . "github.com/matheusgomes28/urchin/common" + "fmt" + . "github.com/matheusgomes28/urchin/common" ) -templ MakeIndex(posts []Post, links []Link) { - - - - // This should go into Make HTML Headers - - - Home Page - - - - - - - @MakeNavBar(links) -
- for _, post := range posts { - - } -
- @MakeFooter() - - +templ makePosts(posts []Post) { +
+ for _, post := range posts { + + } +
} + +templ MakeIndex(posts []Post, links []Link) { + @MakeLayout("Home Page", links, makePosts(posts)) +} \ No newline at end of file diff --git a/views/layout.templ b/views/layout.templ new file mode 100644 index 0000000..6cfa97d --- /dev/null +++ b/views/layout.templ @@ -0,0 +1,34 @@ +package views + +import ( + . "github.com/matheusgomes28/urchin/common" +) + + +templ MakeLayout(title string, links []Link, content templ.Component) { + + + // This should go into Make HTML Headers + + + + { title } + + + + + + + + + @MakeNavBar(links) + +
+ @content +
+ + @MakeFooter() + + + +} \ No newline at end of file diff --git a/views/not-found.templ b/views/not-found.templ new file mode 100644 index 0000000..e285804 --- /dev/null +++ b/views/not-found.templ @@ -0,0 +1,11 @@ +package views + +import "github.com/matheusgomes28/urchin/common" + +templ makeNotFound() { +

Oops - Resource cannot be found 🙁

+} + +templ MakeNotFoundPage(links []common.Link) { + @MakeLayout("Not Found", links, makeNotFound()) +} \ No newline at end of file diff --git a/views/page.templ b/views/page.templ index 247cd77..81eb0b8 100644 --- a/views/page.templ +++ b/views/page.templ @@ -4,26 +4,5 @@ import "github.com/matheusgomes28/urchin/common" templ MakePage(title string, content string, links []common.Link) { - - - - - - { title } - - - - - - - - @MakeNavBar(links) -
- @templ.Raw(content) -
- - @MakeFooter() - - - + @MakeLayout(title, links, templ.Raw(content)) } diff --git a/views/post.templ b/views/post.templ index 6131382..fc21610 100644 --- a/views/post.templ +++ b/views/post.templ @@ -2,31 +2,13 @@ package views import "github.com/matheusgomes28/urchin/common" +templ makePost(title string, content string) { +
+

{ title }

+ @templ.Raw(content) +
+} templ MakePostPage(title string, content string, links []common.Link) { - - - - - - { title } - - - - - - - - @MakeNavBar(links) -
-
-

{ title }

- @templ.Raw(content) -
-
- - @MakeFooter() - - - -} + @MakeLayout(title, links, makePost(title, content)) +} \ No newline at end of file diff --git a/views/services.templ b/views/services.templ new file mode 100644 index 0000000..42bb2e5 --- /dev/null +++ b/views/services.templ @@ -0,0 +1,10 @@ +package views + +import ( + . "github.com/matheusgomes28/urchin/common" +) + + +templ MakeServicesPage(links []Link) { + @MakeLayout("Services", links, makeUnderConstruction()) +} \ No newline at end of file diff --git a/views/shared.templ b/views/shared.templ new file mode 100644 index 0000000..efa0057 --- /dev/null +++ b/views/shared.templ @@ -0,0 +1,5 @@ +package views + +templ makeUnderConstruction() { +

Under construction...

+} \ No newline at end of file