diff options
| -rw-r--r-- | controllers/home.go | 17 | ||||
| -rw-r--r-- | controllers/login.go | 12 | ||||
| -rw-r--r-- | controllers/posts.go | 22 | ||||
| -rw-r--r-- | controllers/preferences.go | 12 | ||||
| -rw-r--r-- | controllers/register.go | 12 | ||||
| -rw-r--r-- | go.mod | 4 | ||||
| -rw-r--r-- | go.sum | 14 | ||||
| -rw-r--r-- | imageboard/main.go | 4 | ||||
| -rw-r--r-- | router/routes.go | 5 | ||||
| -rw-r--r-- | static/css/main.css | 324 | ||||
| -rw-r--r-- | static/scripts/theme.js | 37 | ||||
| -rw-r--r-- | templates/home.html | 7 | ||||
| -rw-r--r-- | templates/layout.html | 13 | ||||
| -rw-r--r-- | templates/layouts/main.django | 19 | ||||
| -rw-r--r-- | templates/login.django | 33 | ||||
| -rw-r--r-- | templates/partials/navbar.django | 23 | ||||
| -rw-r--r-- | templates/partials/search.django | 16 | ||||
| -rw-r--r-- | templates/posts.django | 39 | ||||
| -rw-r--r-- | templates/preferences.django | 21 | ||||
| -rw-r--r-- | templates/register.django | 35 |
20 files changed, 626 insertions, 43 deletions
diff --git a/controllers/home.go b/controllers/home.go deleted file mode 100644 index 1b513c8..0000000 --- a/controllers/home.go +++ /dev/null @@ -1,17 +0,0 @@ -package controllers
-
-import (
- "imageboard/utils/shortcuts"
-
- "github.com/gofiber/fiber/v2"
-)
-
-func HomeController(ctx *fiber.Ctx) error {
- ctx.Locals("Title", "Home Page")
- customdata := struct {
- Custommessage string
- }{
- Custommessage: "Welcome to the Imageboard!",
- }
- return shortcuts.Render(ctx, "home", customdata)
-}
diff --git a/controllers/login.go b/controllers/login.go new file mode 100644 index 0000000..1d6bc5e --- /dev/null +++ b/controllers/login.go @@ -0,0 +1,12 @@ +package controllers + +import ( + "imageboard/utils/shortcuts" + + "github.com/gofiber/fiber/v2" +) + +func LoginController(ctx *fiber.Ctx) error { + ctx.Locals("Title", "Login") + return shortcuts.Render(ctx, "login", nil) +} diff --git a/controllers/posts.go b/controllers/posts.go new file mode 100644 index 0000000..6fdcd26 --- /dev/null +++ b/controllers/posts.go @@ -0,0 +1,22 @@ +package controllers
+
+import (
+ "imageboard/utils/shortcuts"
+
+ "github.com/gofiber/fiber/v2"
+)
+
+func PostsController(ctx *fiber.Ctx) error {
+ ctx.Locals("Title", "Posts")
+
+ searchQuery := ctx.Query("tags", "")
+
+ customdata := struct {
+ SearchQuery string
+ Posts []interface{}
+ }{
+ SearchQuery: searchQuery,
+ Posts: []interface{}{},
+ }
+ return shortcuts.Render(ctx, "posts", customdata)
+}
diff --git a/controllers/preferences.go b/controllers/preferences.go new file mode 100644 index 0000000..86e0fb3 --- /dev/null +++ b/controllers/preferences.go @@ -0,0 +1,12 @@ +package controllers + +import ( + "imageboard/utils/shortcuts" + + "github.com/gofiber/fiber/v2" +) + +func PreferencesController(ctx *fiber.Ctx) error { + ctx.Locals("Title", "Site Preferences") + return shortcuts.Render(ctx, "preferences", nil) +} diff --git a/controllers/register.go b/controllers/register.go new file mode 100644 index 0000000..3be4e64 --- /dev/null +++ b/controllers/register.go @@ -0,0 +1,12 @@ +package controllers + +import ( + "imageboard/utils/shortcuts" + + "github.com/gofiber/fiber/v2" +) + +func RegisterController(ctx *fiber.Ctx) error { + ctx.Locals("Title", "Register") + return shortcuts.Render(ctx, "register", nil) +} @@ -5,7 +5,7 @@ go 1.24.4 require ( github.com/gofiber/fiber/v2 v2.52.8 github.com/gofiber/storage/postgres/v2 v2.0.3 - github.com/gofiber/template/html/v2 v2.1.3 + github.com/gofiber/template/django/v3 v3.1.14 github.com/joho/godotenv v1.5.1 golang.org/x/crypto v0.31.0 gorm.io/driver/postgres v1.6.0 @@ -14,6 +14,7 @@ require ( require ( github.com/andybalholm/brotli v1.1.0 // indirect + github.com/flosch/pongo2/v6 v6.0.0 // indirect github.com/gofiber/template v1.8.3 // indirect github.com/gofiber/utils v1.1.0 // indirect github.com/google/uuid v1.6.0 // indirect @@ -28,6 +29,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.51.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect @@ -3,14 +3,16 @@ github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer5 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/flosch/pongo2/v6 v6.0.0 h1:lsGru8IAzHgIAw6H2m4PCyleO58I40ow6apih0WprMU= +github.com/flosch/pongo2/v6 v6.0.0/go.mod h1:CuDpFm47R0uGGE7z13/tTlt1Y6zdxvr2RLT5LJhsHEU= github.com/gofiber/fiber/v2 v2.52.8 h1:xl4jJQ0BV5EJTA2aWiKw/VddRpHrKeZLF0QPUxqn0x4= github.com/gofiber/fiber/v2 v2.52.8/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/gofiber/storage/postgres/v2 v2.0.3 h1:pN2PAKZMhy7oUkyZ3zS4fPZOVYa8gH/pciBkCw150K0= github.com/gofiber/storage/postgres/v2 v2.0.3/go.mod h1:6Hr+F+1/gslAsdpiJY2jwSJaJe368oTIJoCrUewfbRo= github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc= github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8= -github.com/gofiber/template/html/v2 v2.1.3 h1:n1LYBtmr9C0V/k/3qBblXyMxV5B0o/gpb6dFLp8ea+o= -github.com/gofiber/template/html/v2 v2.1.3/go.mod h1:U5Fxgc5KpyujU9OqKzy6Kn6Qup6Tm7zdsISR+VpnHRE= +github.com/gofiber/template/django/v3 v3.1.14 h1:SvTvs+u5vTZuu1Y2pMUD2NhaGIjBj9FmDA3XD50QBvw= +github.com/gofiber/template/django/v3 v3.1.14/go.mod h1:gP4vH+T1ajZw7yaejqG1dZVdHQkMC/jPoQbmlG812I0= github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM= github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -31,6 +33,10 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -42,6 +48,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -64,6 +72,8 @@ golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/imageboard/main.go b/imageboard/main.go index fbba38c..a8dc2c0 100644 --- a/imageboard/main.go +++ b/imageboard/main.go @@ -15,7 +15,7 @@ import ( "github.com/gofiber/fiber/v2/middleware/helmet"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
- "github.com/gofiber/template/html/v2"
+ "github.com/gofiber/template/django/v3"
)
func main() {
@@ -23,7 +23,7 @@ func main() { log.Println("Warning: AppSecret is set to a default value which is not secure. Please set a strong random secret in your APP_SECRET environment variable or .env file.")
}
- engine := html.New("./templates", ".html")
+ engine := django.New("./templates", ".django")
engine.Reload(config.Server.IsDevMode)
app := fiber.New(fiber.Config{
Views: engine,
diff --git a/router/routes.go b/router/routes.go index 814b767..216719f 100644 --- a/router/routes.go +++ b/router/routes.go @@ -7,7 +7,10 @@ import ( )
func Initialize(router *fiber.App) {
- router.Get("/", controllers.HomeController)
+ router.Get("/", controllers.PostsController)
+ router.Get("/register", controllers.RegisterController)
+ router.Get("/login", controllers.LoginController)
+ router.Get("/preferences", controllers.PreferencesController)
router.Use(func(c *fiber.Ctx) error {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
diff --git a/static/css/main.css b/static/css/main.css new file mode 100644 index 0000000..47a393e --- /dev/null +++ b/static/css/main.css @@ -0,0 +1,324 @@ +:root { + --bg-main: #ffffff; + --bg-section: #f8f8f8; + --bg-nav: #e0e0e0; + --text-main: #000000; + --text-dim: #666666; + --text-active: #ff0000; + --link-default: #0000ee; + --link-visited: #551a8b; + --link-hover: #ff0000; + --border-main: #c0c0c0; + --border-dark: #808080; + --button-bg: #e0e0e0; + --button-shadow: #808080; + --input-bg: #ffffff; + --error-bg: #ffe0e0; + --error-border: #ff0000; + --success-bg: #e0ffe0; + --success-border: #00aa00; +} + +[data-theme="dark"] { + --bg-main: #000000; + --bg-section: #1a1a1a; + --bg-nav: #333333; + --text-main: #c0c0c0; + --text-dim: #808080; + --text-active: #ff6666; + --link-default: #6699ff; + --link-visited: #cc99ff; + --link-hover: #ffff66; + --border-main: #666666; + --border-dark: #999999; + --button-bg: #404040; + --button-shadow: #202020; + --input-bg: #1a1a1a; + --error-bg: #330000; + --error-border: #ff6666; + --success-bg: #003300; + --success-border: #66ff66; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: "MS Gothic", "MS ゴシック", "Courier New", monospace; + font-size: 12px; + line-height: 1.2; + background: var(--bg-main); + color: var(--text-main); + width: 800px; + margin: 0 auto; +} + +nav { + background: var(--bg-nav); + border: 2px outset var(--border-main); + padding: 6px 8px; + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; +} + +nav div { + display: flex; + gap: 12px; + align-items: center; +} + +nav a { + color: var(--link-default); + text-decoration: underline; + font-size: 12px; + font-weight: normal; +} + +nav a:visited { + color: var(--link-visited); +} + +nav a:hover { + color: var(--link-hover); +} + +nav a.active { + color: var(--text-active); + font-weight: bold; + text-decoration: none; +} + +main { + padding: 8px; + min-height: 400px; +} + +h1 { + font-size: 14px; + font-weight: bold; + text-align: center; + margin-bottom: 12px; + color: var(--text-main); +} + +h2 { + font-size: 13px; + font-weight: bold; + margin-bottom: 8px; + color: var(--text-main); +} + +h3, +h4 { + font-size: 12px; + font-weight: bold; + margin-bottom: 6px; + color: var(--text-main); +} + +form { + margin: 8px 0; +} + +label { + display: block; + font-weight: bold; + font-size: 12px; + margin: 4px 0 2px 0; + color: var(--text-main); +} + +input, +textarea, +select { + background: var(--input-bg); + color: var(--text-main); + border: 2px inset var(--border-main); + font-family: inherit; + font-size: 12px; + padding: 2px 4px; + margin-bottom: 6px; +} + +input[type="text"], +input[type="password"], +input[type="email"], +textarea { + width: 180px; +} + +input[type="checkbox"], +input[type="radio"] { + width: auto; + margin-right: 4px; +} + +button, +input[type="submit"] { + background: var(--button-bg); + color: var(--text-main); + border: 2px outset var(--border-main); + font-family: inherit; + font-size: 12px; + padding: 3px 8px; + cursor: pointer; + margin: 2px 4px 2px 0; +} + +button:hover, +input[type="submit"]:hover { + background: var(--bg-section); +} + +button:active, +input[type="submit"]:active { + border: 2px inset var(--border-main); +} + +.posts-container { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 4px; + margin: 8px 0; +} + +article { + background: var(--bg-section); + border: 1px solid var(--border-main); + padding: 4px; + text-align: center; +} + +article img { + width: 100%; + height: 120px; + object-fit: cover; + border: 1px solid var(--border-dark); + margin-bottom: 4px; +} + +article h4 { + font-size: 11px; + font-weight: bold; + margin: 2px 0; + color: var(--text-main); +} + +article p { + font-size: 10px; + color: var(--text-dim); + margin: 1px 0; +} + +aside { + background: var(--bg-section); + border: 2px inset var(--border-main); + padding: 6px; + margin: 8px 0; + display: flex; + align-items: center; + gap: 8px; +} + +aside input[type="text"] { + flex: 1; + margin-bottom: 0; +} + +.empty-state { + background: var(--bg-section); + border: 2px inset var(--border-main); + padding: 24px; + text-align: center; + margin: 12px 0; +} + +.empty-state h3 { + color: var(--text-main); + margin-bottom: 8px; +} + +.error-message, +.error { + background: var(--error-bg); + color: var(--text-main); + border: 1px solid var(--error-border); + padding: 6px; + margin: 6px 0; + text-align: center; +} + +.success-message, +.success { + background: var(--success-bg); + color: var(--text-main); + border: 1px solid var(--success-border); + padding: 6px; + margin: 6px 0; + text-align: center; +} + +footer { + background: var(--bg-nav); + border: 2px outset var(--border-main); + padding: 8px; + text-align: center; + font-size: 10px; + color: var(--text-dim); + margin-top: 16px; +} + +footer p { + margin: 1px 0; +} + +a { + color: var(--link-default); + text-decoration: underline; +} + +a:visited { + color: var(--link-visited); +} + +a:hover { + color: var(--link-hover); +} + +p { + margin: 4px 0; + line-height: 1.3; +} + +small { + font-size: 10px; + color: var(--text-dim); +} + +.button-group { + margin: 8px 0; + display: flex; + gap: 4px; +} + +section { + background: var(--bg-section); + border: 2px inset var(--border-main); + padding: 12px; + margin: 12px 0; +} + +section h2 { + text-align: center; + margin-bottom: 12px; + color: var(--text-main); +} + +.center { + text-align: center; +}
\ No newline at end of file diff --git a/static/scripts/theme.js b/static/scripts/theme.js new file mode 100644 index 0000000..0ba6625 --- /dev/null +++ b/static/scripts/theme.js @@ -0,0 +1,37 @@ +document.addEventListener('DOMContentLoaded', function () { + const savedTheme = localStorage.getItem('theme') || 'light'; + document.documentElement.setAttribute('data-theme', savedTheme); + + const preferencesForm = document.getElementById('preferences-form'); + if (preferencesForm) { + const themeRadios = document.querySelectorAll('input[name="theme"]'); + themeRadios.forEach(radio => { + if (radio.value === savedTheme) { + radio.checked = true; + } + }); + + preferencesForm.addEventListener('submit', function (e) { + e.preventDefault(); + const selectedTheme = document.querySelector('input[name="theme"]:checked').value; + localStorage.setItem('theme', selectedTheme); + document.documentElement.setAttribute('data-theme', selectedTheme); + + let successMsg = document.querySelector('.success-message'); + if (successMsg) { + successMsg.remove(); + } + + const message = document.createElement('div'); + message.className = 'success-message'; + message.textContent = 'Preferences saved successfully!'; + preferencesForm.parentNode.insertBefore(message, preferencesForm); + + setTimeout(() => { + if (message.parentNode) { + message.remove(); + } + }, 3000); + }); + } +});
\ No newline at end of file diff --git a/templates/home.html b/templates/home.html deleted file mode 100644 index 708a02e..0000000 --- a/templates/home.html +++ /dev/null @@ -1,7 +0,0 @@ -{{define "content"}}
-<main>
- <h2>{{.Title}}</h2>
- <p>Welcome to {{.Appname}}</p>
- <p>Custom Data: {{ .Custommessage }}</p>
-</main>
-{{end}} {{template "layout" .}}
diff --git a/templates/layout.html b/templates/layout.html deleted file mode 100644 index d2a5b98..0000000 --- a/templates/layout.html +++ /dev/null @@ -1,13 +0,0 @@ -{{define "layout"}}
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>{{.Title}} - {{.Appname}}</title>
- </head>
- <body>
- {{template "content" .}}
- </body>
-</html>
-{{end}}
diff --git a/templates/layouts/main.django b/templates/layouts/main.django new file mode 100644 index 0000000..5c9eeb6 --- /dev/null +++ b/templates/layouts/main.django @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <title>{{ Title }} - {{ Appname }}</title> + <link rel="stylesheet" href="/css/main.css" /> + </head> + <body> + {% include 'partials/navbar.django' %} + + <main>{{ embed }}</main> + + <footer> + <p>© 2025 {{ Appname }}. All rights reserved.</p> + </footer> + + <script src="/scripts/theme.js"></script> + </body> +</html> diff --git a/templates/login.django b/templates/login.django new file mode 100644 index 0000000..e3d632a --- /dev/null +++ b/templates/login.django @@ -0,0 +1,33 @@ +<h2>Login to {{ Appname }}</h2> + +{% if Error %} + <div class="error">{{ Error }}</div> +{% endif %} + +<form action="/login" method="POST"> + <table> + <tr> + <th colspan="2">User Login</th> + </tr> + <tr> + <td><label for="username">Username or Email:</label></td> + <td><input type="text" id="username" name="username" required value="{{ Username }}" /></td> + </tr> + <tr> + <td><label for="password">Password:</label></td> + <td><input type="password" id="password" name="password" required /></td> + </tr> + <tr> + <td colspan="2" class="center"> + <input type="submit" value="LOGIN" /> + </td> + </tr> + </table> +</form> + +<p> + Don't have an account? <a href="/register">Register here</a> +</p> +<p> + <a href="/forgot-password">Forgot your password?</a> +</p> diff --git a/templates/partials/navbar.django b/templates/partials/navbar.django new file mode 100644 index 0000000..52596ee --- /dev/null +++ b/templates/partials/navbar.django @@ -0,0 +1,23 @@ +<nav> + <div class="nav-left"> + <a href="/">{{ Appname }}</a> + <a href="/" class="{% if request.path == '/' %}active{% endif %}">POSTS</a> + <a href="/comments" class="{% if request.path == '/comments' %}active{% endif %}">COMMENTS</a> + <a href="/tags" class="{% if request.path == '/tags' %}active{% endif %}">TAGS</a> + {% if User %}{% if User.IsAdmin %} + <a href="/users" class="{% if request.path == '/users' %}active{% endif %}">USERS</a> + {% endif %}{% endif %} + </div> + + <div class="nav-right"> + {% if User %} + <a href="/account">{{ User.Username }}</a> + <a href="/preferences" class="{% if request.path == '/preferences' %}active{% endif %}">⚙</a> + <a href="/logout">LOGOUT</a> + {% else %} + <a href="/login" class="{% if request.path == '/login' %}active{% endif %}">LOGIN</a> + <a href="/register" class="{% if request.path == '/register' %}active{% endif %}">REGISTER</a> + <a href="/preferences" class="{% if request.path == '/preferences' %}active{% endif %}">⚙</a> + {% endif %} + </div> +</nav> diff --git a/templates/partials/search.django b/templates/partials/search.django new file mode 100644 index 0000000..58c2480 --- /dev/null +++ b/templates/partials/search.django @@ -0,0 +1,16 @@ +<form action="/" method="GET"> + <div class="search-box"> + <table> + <tr> + <th>Search Posts</th> + </tr> + <tr> + <td> + <input type="text" name="tags" placeholder="tags..." value="{{ SearchQuery }}" /> + <input type="submit" value="SEARCH" /> + <input type="button" value="CLEAR" onclick="this.form.reset(); window.location.href='/';" /> + </td> + </tr> + </table> + </div> +</form> diff --git a/templates/posts.django b/templates/posts.django new file mode 100644 index 0000000..1a22b97 --- /dev/null +++ b/templates/posts.django @@ -0,0 +1,39 @@ +{% include 'partials/search.django' %} + +<h2>{{ Title }}</h2> + +<div class="posts-grid"> + {% if Posts %} + {% for post in Posts %} + <div class="post-cell"> + <img src="/uploads/thumbnails/{{ post.FileName }}" alt="{{ post.Title }}" class="post-img" /> + <div class="post-title"> + {% if post.Title %} + {{ post.Title }} + {% else %} + Post #{{ post.ID }} + {% endif %} + </div> + <div class="post-info">{{ post.Tags|length }} tags</div> + </div> + {% endfor %} + {% else %} + <table class="post-table"> + <tr> + <th>NO POSTS FOUND!</th> + </tr> + <tr> + <td class="center"> + Be the first to share something awesome!<br> + <input type="button" value="UPLOAD IMAGE" onclick="location.href='/upload'"> + </td> + </tr> + </table> + {% endif %} +</div> + +<p class="center"> + <input type="button" value="UPLOAD IMAGE" onclick="location.href='/upload'"> +</p> + <button class="secondary" onclick="location.href='/tags'">BROWSE TAGS</button> +</div> diff --git a/templates/preferences.django b/templates/preferences.django new file mode 100644 index 0000000..837d94a --- /dev/null +++ b/templates/preferences.django @@ -0,0 +1,21 @@ +<h2>Site Preferences</h2> + +<form id="preferences-form"> + <div> + <label>Theme</label> + <div> + <input type="radio" name="theme" value="light" id="theme-light" /> + <label for="theme-light">Light Mode</label> + </div> + <div> + <input type="radio" name="theme" value="dark" id="theme-dark" /> + <label for="theme-dark">Dark Mode</label> + </div> + </div> + + <button type="submit">SAVE PREFERENCES</button> +</form> + +<p> + <a href="/">Back to Posts</a> +</p> diff --git a/templates/register.django b/templates/register.django new file mode 100644 index 0000000..c096104 --- /dev/null +++ b/templates/register.django @@ -0,0 +1,35 @@ +<h2>Join {{ Appname }}</h2> + +{% if Error %} + <div class="error-message">{{ Error }}</div> +{% endif %} + +<form action="/register" method="POST"> + <div> + <label for="username">Username</label> + <input type="text" id="username" name="username" required value="{{ Username }}" minlength="3" maxlength="72" pattern="[a-zA-Z0-9_-]+" /> + <small>3-72 characters, letters, numbers, underscores, and hyphens only</small> + </div> + + <div> + <label for="email">Email Address</label> + <input type="email" id="email" name="email" required value="{{ Email }}" /> + </div> + + <div> + <label for="password">Password</label> + <input type="password" id="password" name="password" required minlength="8" /> + <small>Minimum 8 characters</small> + </div> + + <div> + <label for="confirm_password">Confirm Password</label> + <input type="password" id="confirm_password" name="confirm_password" required /> + </div> + + <button type="submit">CREATE ACCOUNT</button> +</form> + +<p> + Already have an account? <a href="/login">Login here</a> +</p> |
