convert most frontend to templ, implement design wireframes

This commit is contained in:
yequari 2025-03-01 13:12:21 -07:00
parent 19364225c9
commit d899769ba0
21 changed files with 1801 additions and 560 deletions

View File

@ -5,57 +5,49 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"git.32bit.cafe/32bitcafe/guestbook/internal/forms"
"git.32bit.cafe/32bitcafe/guestbook/internal/models" "git.32bit.cafe/32bitcafe/guestbook/internal/models"
"git.32bit.cafe/32bitcafe/guestbook/internal/validator" "git.32bit.cafe/32bitcafe/guestbook/internal/validator"
"git.32bit.cafe/32bitcafe/guestbook/ui/views"
) )
func (app *application) home(w http.ResponseWriter, r *http.Request) { func (app *application) home(w http.ResponseWriter, r *http.Request) {
data := app.newTemplateData(r) data := app.newCommonData(r)
app.render(w, r, http.StatusOK, "home.tmpl.html", data) views.Home("Home", data).Render(r.Context(), w)
}
type userRegistrationForm struct {
Name string `schema:"username"`
Email string `schema:"email"`
Password string `schema:"password"`
validator.Validator `schema:"-"`
} }
func (app *application) getUserRegister(w http.ResponseWriter, r *http.Request) { func (app *application) getUserRegister(w http.ResponseWriter, r *http.Request) {
data := app.newTemplateData(r) form := forms.UserRegistrationForm{}
data.Form = userRegistrationForm{} data := app.newCommonData(r)
app.render(w, r, http.StatusOK, "usercreate.view.tmpl.html", data) views.UserRegistration("User Registration", data, form).Render(r.Context(), w)
} }
func (app *application) postUserRegister(w http.ResponseWriter, r *http.Request) { func (app *application) postUserRegister(w http.ResponseWriter, r *http.Request) {
var form userRegistrationForm var form forms.UserRegistrationForm
err := app.decodePostForm(r, &form) err := app.decodePostForm(r, &form)
if err != nil { if err != nil {
app.clientError(w, http.StatusBadRequest) app.clientError(w, http.StatusBadRequest)
return return
} }
form.CheckField(validator.NotBlank(form.Name), "name", "This field cannot be blank") form.CheckField(validator.NotBlank(form.Name), "name", "This field cannot be blank")
form.CheckField(validator.NotBlank(form.Email), "email", "This field cannot be blank") form.CheckField(validator.NotBlank(form.Email), "email", "This field cannot be blank")
form.CheckField(validator.Matches(form.Email, validator.EmailRX), "email", "This field must be a valid email address") form.CheckField(validator.Matches(form.Email, validator.EmailRX), "email", "This field must be a valid email address")
form.CheckField(validator.NotBlank(form.Password), "password", "This field cannot be blank") form.CheckField(validator.NotBlank(form.Password), "password", "This field cannot be blank")
form.CheckField(validator.MinChars(form.Password, 8), "password", "This field must be at least 8 characters long") form.CheckField(validator.MinChars(form.Password, 8), "password", "This field must be at least 8 characters long")
if !form.Valid() { if !form.Valid() {
data := app.newTemplateData(r) data := app.newCommonData(r)
data.Form = form w.WriteHeader(http.StatusUnprocessableEntity)
app.render(w, r, http.StatusUnprocessableEntity, "usercreate.view.tmpl.html", data) views.UserRegistration("User Registration", data, form).Render(r.Context(), w)
return return
} }
shortId := app.createShortId() shortId := app.createShortId()
err = app.users.Insert(shortId, form.Name, form.Email, form.Password) err = app.users.Insert(shortId, form.Name, form.Email, form.Password)
if err != nil { if err != nil {
if errors.Is(err, models.ErrDuplicateEmail) { if errors.Is(err, models.ErrDuplicateEmail) {
form.AddFieldError("email", "Email address is already in use") form.AddFieldError("email", "Email address is already in use")
data := app.newTemplateData(r) data := app.newCommonData(r)
data.Form = form w.WriteHeader(http.StatusUnprocessableEntity)
app.render(w ,r, http.StatusUnprocessableEntity, "usercreate.view.tmpl.html", data) views.UserRegistration("User Registration", data, form).Render(r.Context(), w)
} else { } else {
app.serverError(w, r, err) app.serverError(w, r, err)
} }
@ -65,58 +57,42 @@ func (app *application) postUserRegister(w http.ResponseWriter, r *http.Request)
http.Redirect(w, r, "/users/login", http.StatusSeeOther) http.Redirect(w, r, "/users/login", http.StatusSeeOther)
} }
type userLoginForm struct {
Email string `schema:"email"`
Password string `schema:"password"`
validator.Validator `schema:"-"`
}
func (app *application) getUserLogin(w http.ResponseWriter, r *http.Request) { func (app *application) getUserLogin(w http.ResponseWriter, r *http.Request) {
data := app.newTemplateData(r) views.UserLogin("Login", app.newCommonData(r), forms.UserLoginForm{}).Render(r.Context(), w)
data.Form = userLoginForm{}
app.render(w, r, http.StatusOK, "login.view.tmpl.html", data)
} }
func (app *application) postUserLogin(w http.ResponseWriter, r *http.Request) { func (app *application) postUserLogin(w http.ResponseWriter, r *http.Request) {
var form userLoginForm var form forms.UserLoginForm
err := app.decodePostForm(r, &form) err := app.decodePostForm(r, &form)
if err != nil { if err != nil {
app.clientError(w, http.StatusBadRequest) app.clientError(w, http.StatusBadRequest)
} }
form.CheckField(validator.NotBlank(form.Email), "email", "This field cannot be blank") form.CheckField(validator.NotBlank(form.Email), "email", "This field cannot be blank")
form.CheckField(validator.Matches(form.Email, validator.EmailRX), "email", "This field must be a valid email address") form.CheckField(validator.Matches(form.Email, validator.EmailRX), "email", "This field must be a valid email address")
form.CheckField(validator.NotBlank(form.Password), "password", "This field cannot be blank") form.CheckField(validator.NotBlank(form.Password), "password", "This field cannot be blank")
if !form.Valid() { if !form.Valid() {
data := app.newTemplateData(r) data := app.newCommonData(r)
data.Form = userLoginForm{} w.WriteHeader(http.StatusUnprocessableEntity)
app.render(w, r, http.StatusUnprocessableEntity, "login.view.tmpl.html", data) views.UserLogin("Login", data, form).Render(r.Context(), w)
return return
} }
id, err := app.users.Authenticate(form.Email, form.Password) id, err := app.users.Authenticate(form.Email, form.Password)
if err != nil { if err != nil {
if errors.Is(err, models.ErrInvalidCredentials) { if errors.Is(err, models.ErrInvalidCredentials) {
form.AddNonFieldError("Email or password is incorrect") form.AddNonFieldError("Email or password is incorrect")
data := app.newTemplateData(r) data := app.newCommonData(r)
data.Form = form views.UserLogin("Login", data, form).Render(r.Context(), w)
app.render(w, r, http.StatusUnprocessableEntity, "login.view.tmpl.html", data)
} else { } else {
app.serverError(w, r, err) app.serverError(w, r, err)
} }
return return
} }
err = app.sessionManager.RenewToken(r.Context()) err = app.sessionManager.RenewToken(r.Context())
if err != nil { if err != nil {
app.serverError(w, r, err) app.serverError(w, r, err)
return return
} }
app.sessionManager.Put(r.Context(), "authenticatedUserId", id) app.sessionManager.Put(r.Context(), "authenticatedUserId", id)
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)
} }
@ -126,13 +102,14 @@ func (app *application) postUserLogout(w http.ResponseWriter, r *http.Request) {
app.serverError(w, r, err) app.serverError(w, r, err)
return return
} }
app.sessionManager.Remove(r.Context(), "authenticatedUserId") app.sessionManager.Remove(r.Context(), "authenticatedUserId")
app.sessionManager.Put(r.Context(), "flash", "You've been logged out successfully!") app.sessionManager.Put(r.Context(), "flash", "You've been logged out successfully!")
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)
} }
func (app *application) getUsersList(w http.ResponseWriter, r *http.Request) { func (app *application) getUsersList(w http.ResponseWriter, r *http.Request) {
// skip templ conversion for this view, which will not be available in the final app
// something similar will be available in the admin panel
users, err := app.users.GetAll() users, err := app.users.GetAll()
if err != nil { if err != nil {
app.serverError(w, r, err) app.serverError(w, r, err)
@ -154,18 +131,13 @@ func (app *application) getUser(w http.ResponseWriter, r *http.Request) {
} }
return return
} }
data := app.newTemplateData(r) data := app.newCommonData(r)
data.User = user views.UserProfile(user.Username, data, user).Render(r.Context(), w)
app.render(w, r, http.StatusOK, "user.view.tmpl.html", data)
} }
func (app *application) getGuestbookCreate(w http.ResponseWriter, r* http.Request) { func (app *application) getGuestbookCreate(w http.ResponseWriter, r* http.Request) {
data := app.newTemplateData(r) data := app.newCommonData(r)
if r.Header.Get("HX-Request") == "true" { views.GuestbookCreate("New Guestbook", data).Render(r.Context(), w)
app.renderHTMX(w, r, http.StatusOK, "guestbookcreate.part.html", data)
return
}
app.render(w, r, http.StatusOK, "guestbookcreate.view.tmpl.html", data)
} }
func (app *application) postGuestbookCreate(w http.ResponseWriter, r* http.Request) { func (app *application) postGuestbookCreate(w http.ResponseWriter, r* http.Request) {
@ -194,24 +166,13 @@ func (app *application) postGuestbookCreate(w http.ResponseWriter, r* http.Reque
func (app *application) getGuestbookList(w http.ResponseWriter, r *http.Request) { func (app *application) getGuestbookList(w http.ResponseWriter, r *http.Request) {
userId := app.sessionManager.GetInt64(r.Context(), "authenticatedUserId") userId := app.sessionManager.GetInt64(r.Context(), "authenticatedUserId")
user, err := app.users.GetById(userId)
if err != nil {
app.serverError(w, r, err)
return
}
guestbooks, err := app.guestbooks.GetAll(userId) guestbooks, err := app.guestbooks.GetAll(userId)
if err != nil { if err != nil {
app.serverError(w, r, err) app.serverError(w, r, err)
return return
} }
data := app.newTemplateData(r) data := app.newCommonData(r)
data.Guestbooks = guestbooks views.GuestbookList("Guestbooks", data, guestbooks).Render(r.Context(), w)
data.User = user
if r.Header.Get("HX-Request") == "true" {
app.renderHTMX(w, r, http.StatusCreated, "guestbooklist.part.html", data)
return
}
app.render(w, r, http.StatusOK, "guestbooklist.view.tmpl.html", data)
} }
func (app *application) getGuestbook(w http.ResponseWriter, r *http.Request) { func (app *application) getGuestbook(w http.ResponseWriter, r *http.Request) {
@ -230,10 +191,8 @@ func (app *application) getGuestbook(w http.ResponseWriter, r *http.Request) {
app.serverError(w, r, err) app.serverError(w, r, err)
return return
} }
data := app.newTemplateData(r) data := app.newCommonData(r)
data.Guestbook = guestbook views.GuestbookView("Guestbook", data, guestbook, comments).Render(r.Context(), w)
data.Comments = comments
app.render(w, r, http.StatusOK, "guestbook.view.tmpl.html", data)
} }
func (app *application) getGuestbookComments(w http.ResponseWriter, r *http.Request) { func (app *application) getGuestbookComments(w http.ResponseWriter, r *http.Request) {
@ -267,6 +226,7 @@ type commentCreateForm struct {
} }
func (app *application) getGuestbookCommentCreate(w http.ResponseWriter, r *http.Request) { func (app *application) getGuestbookCommentCreate(w http.ResponseWriter, r *http.Request) {
// TODO: This will be the embeddable form
slug := r.PathValue("id") slug := r.PathValue("id")
guestbook, err := app.guestbooks.Get(slugToShortId(slug)) guestbook, err := app.guestbooks.Get(slugToShortId(slug))
if err != nil { if err != nil {
@ -324,7 +284,7 @@ func (app *application) postGuestbookCommentCreate(w http.ResponseWriter, r *htt
app.serverError(w, r, err) app.serverError(w, r, err)
return return
} }
app.sessionManager.Put(r.Context(), "flash", "Comment successfully posted!") // app.sessionManager.Put(r.Context(), "flash", "Comment successfully posted!")
http.Redirect(w, r, fmt.Sprintf("/guestbooks/%s", guestbookSlug), http.StatusSeeOther) http.Redirect(w, r, fmt.Sprintf("/guestbooks/%s", guestbookSlug), http.StatusSeeOther)
} }

View File

@ -41,6 +41,21 @@ func (app *application) renderHTMX(w http.ResponseWriter, r *http.Request, statu
} }
} }
func (app *application) renderFullPage(w http.ResponseWriter, r *http.Request, status int, page string, data templateData) {
ts, ok := app.templateCache[page]
if !ok {
err := fmt.Errorf("the template %s does not exist", page)
app.serverError(w, r, err)
return
}
w.WriteHeader(status)
err := ts.ExecuteTemplate(w, "base", data)
if err != nil {
app.serverError(w, r, err)
}
}
func (app *application) render(w http.ResponseWriter, r *http.Request, status int, page string, data templateData) { func (app *application) render(w http.ResponseWriter, r *http.Request, status int, page string, data templateData) {
ts, ok := app.templateCache[page] ts, ok := app.templateCache[page]
if !ok { if !ok {

View File

@ -12,9 +12,10 @@ func (app *application) routes() http.Handler {
mux.Handle("GET /static/", http.StripPrefix("/static", fileServer)) mux.Handle("GET /static/", http.StripPrefix("/static", fileServer))
dynamic := alice.New(app.sessionManager.LoadAndSave, noSurf, app.authenticate) dynamic := alice.New(app.sessionManager.LoadAndSave, noSurf, app.authenticate)
standard := alice.New(app.recoverPanic, app.logRequest, commonHeaders)
mux.Handle("/{$}", dynamic.ThenFunc(app.home)) mux.Handle("/{$}", dynamic.ThenFunc(app.home))
mux.Handle("POST /guestbooks/{id}/comments/create", dynamic.ThenFunc(app.postGuestbookCommentCreate)) mux.Handle("POST /guestbooks/{id}/comments/create", standard.ThenFunc(app.postGuestbookCommentCreate))
mux.Handle("GET /guestbooks/{id}", dynamic.ThenFunc(app.getGuestbook)) mux.Handle("GET /guestbooks/{id}", dynamic.ThenFunc(app.getGuestbook))
mux.Handle("GET /users/register", dynamic.ThenFunc(app.getUserRegister)) mux.Handle("GET /users/register", dynamic.ThenFunc(app.getUserRegister))
mux.Handle("POST /users/register", dynamic.ThenFunc(app.postUserRegister)) mux.Handle("POST /users/register", dynamic.ThenFunc(app.postUserRegister))
@ -31,7 +32,6 @@ func (app *application) routes() http.Handler {
mux.Handle("POST /guestbooks/create", protected.ThenFunc(app.postGuestbookCreate)) mux.Handle("POST /guestbooks/create", protected.ThenFunc(app.postGuestbookCreate))
mux.Handle("GET /guestbooks/{id}/comments/create", protected.ThenFunc(app.getGuestbookCommentCreate)) mux.Handle("GET /guestbooks/{id}/comments/create", protected.ThenFunc(app.getGuestbookCommentCreate))
standard := alice.New(app.recoverPanic, app.logRequest, commonHeaders)
return standard.Then(mux) return standard.Then(mux)
} }

View File

@ -7,6 +7,7 @@ import (
"time" "time"
"git.32bit.cafe/32bitcafe/guestbook/internal/models" "git.32bit.cafe/32bitcafe/guestbook/internal/models"
"git.32bit.cafe/32bitcafe/guestbook/ui/views"
"github.com/justinas/nosurf" "github.com/justinas/nosurf"
) )
@ -77,6 +78,17 @@ func newTemplateCache() (map[string]*template.Template, error) {
return cache, nil return cache, nil
} }
func (app *application) newCommonData(r *http.Request) views.CommonData {
return views.CommonData {
CurrentYear: time.Now().Year(),
Flash: app.sessionManager.PopString(r.Context(), "flash"),
IsAuthenticated: app.isAuthenticated(r),
CSRFToken: nosurf.Token(r),
CurrentUser: app.getCurrentUser(r),
IsHtmx: r.Header.Get("Hx-Request") == "true",
}
}
func (app *application) newTemplateData(r *http.Request) templateData { func (app *application) newTemplateData(r *http.Request) templateData {
return templateData { return templateData {
CurrentYear: time.Now().Year(), CurrentYear: time.Now().Year(),

1
go.mod
View File

@ -10,6 +10,7 @@ require (
) )
require ( require (
github.com/a-h/templ v0.3.833 // indirect
github.com/gorilla/schema v1.4.1 // indirect github.com/gorilla/schema v1.4.1 // indirect
github.com/justinas/alice v1.2.0 // indirect github.com/justinas/alice v1.2.0 // indirect
github.com/justinas/nosurf v1.1.1 // indirect github.com/justinas/nosurf v1.1.1 // indirect

2
go.sum
View File

@ -1,3 +1,5 @@
github.com/a-h/templ v0.3.833 h1:L/KOk/0VvVTBegtE0fp2RJQiBm7/52Zxv5fqlEHiQUU=
github.com/a-h/templ v0.3.833/go.mod h1:cAu4AiZhtJfBjMY0HASlyzvkrtjnHWPeEsyGK2YYmfk=
github.com/alexedwards/scs/sqlite3store v0.0.0-20240316134038-7e11d57e8885 h1:+DCxWg/ojncqS+TGAuRUoV7OfG/S4doh0pcpAwEcow0= github.com/alexedwards/scs/sqlite3store v0.0.0-20240316134038-7e11d57e8885 h1:+DCxWg/ojncqS+TGAuRUoV7OfG/S4doh0pcpAwEcow0=
github.com/alexedwards/scs/sqlite3store v0.0.0-20240316134038-7e11d57e8885/go.mod h1:Iyk7S76cxGaiEX/mSYmTZzYehp4KfyylcLaV3OnToss= github.com/alexedwards/scs/sqlite3store v0.0.0-20240316134038-7e11d57e8885/go.mod h1:Iyk7S76cxGaiEX/mSYmTZzYehp4KfyylcLaV3OnToss=
github.com/alexedwards/scs/v2 v2.8.0 h1:h31yUYoycPuL0zt14c0gd+oqxfRwIj6SOjHdKRZxhEw= github.com/alexedwards/scs/v2 v2.8.0 h1:h31yUYoycPuL0zt14c0gd+oqxfRwIj6SOjHdKRZxhEw=

17
internal/forms/forms.go Normal file
View File

@ -0,0 +1,17 @@
package forms
import "git.32bit.cafe/32bitcafe/guestbook/internal/validator"
type UserRegistrationForm struct {
Name string `schema:"username"`
Email string `schema:"email"`
Password string `schema:"password"`
validator.Validator `schema:"-"`
}
type UserLoginForm struct {
Email string `schema:"email"`
Password string `schema:"password"`
validator.Validator `schema:"-"`
}

View File

@ -2,6 +2,7 @@ package models
import ( import (
"database/sql" "database/sql"
"strconv"
"time" "time"
) )
@ -15,6 +16,10 @@ type Guestbook struct {
IsActive bool IsActive bool
} }
func (gb Guestbook) Slug() string {
return strconv.FormatUint(gb.ShortId, 36)
}
type GuestbookModel struct { type GuestbookModel struct {
DB *sql.DB DB *sql.DB
} }

View File

@ -1,511 +1,78 @@
/* Set the global variables for everything. Change these to use your own fonts and colours. */
:root {
/* Set sans-serif & mono fonts */
--sans-font: -apple-system, BlinkMacSystemFont, "Avenir Next", Avenir,
"Nimbus Sans L", Roboto, Noto, "Segoe UI", Arial, Helvetica,
"Helvetica Neue", sans-serif;
--mono-font: Consolas, Menlo, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
/* Body font size. By default, effectively 18.4px, based on 16px as 'root em' */
--base-fontsize: 1.15rem;
/* Major third scale progression - see https://type-scale.com/ */
--header-scale: 1.25;
/* Line height is set to the "Golden ratio" for optimal legibility */
--line-height: 1.618;
/* Default (light) theme */
--bg: #fff;
--accent-bg: #f5f7ff;
--text: #212121;
--text-light: #585858;
--border: #d8dae1;
--accent: #0d47a1;
--accent-light: #90caf9;
--code: #d81b60;
--preformatted: #444;
--marked: #ffdd33;
--disabled: #efefef;
}
/* Dark theme */
@media (prefers-color-scheme: dark) {
:root {
--bg: #212121;
--accent-bg: #2b2b2b;
--text: #dcdcdc;
--text-light: #ababab;
--border: #666;
--accent: #ffb300;
--accent-light: #ffecb3;
--code: #f06292;
--preformatted: #ccc;
--disabled: #111;
}
img,
video {
opacity: 0.6;
}
}
html { html {
/* Set the font globally */ background: lightgray;
font-family: var(--sans-font);
} }
/* Make the body a nice central block */
body { body {
color: var(--text); max-width: 1024px;
background: var(--bg); margin: 1rem auto;
font-size: var(--base-fontsize); padding: 1rem;
line-height: var(--line-height); background: white;
display: flex; font-size: 1.2rem;
min-height: 100vh; line-height: 1.5;
flex-direction: column; font-family: Arial, Helvetica, sans-serif;
flex: 1;
margin: 0 auto;
max-width: 45rem;
padding: 0 0.5rem;
overflow-x: hidden;
word-break: break-word;
overflow-wrap: break-word;
} }
/* Make the header bg full width, but the content inline with body */
header { header {
background: var(--accent-bg);
border-bottom: 1px solid var(--border);
text-align: center; text-align: center;
padding: 2rem 0.5rem;
width: 100vw;
position: relative;
box-sizing: border-box;
left: 50%;
right: 50%;
margin-left: -50vw;
margin-right: -50vw;
} }
/* Remove margins for header text */ body > nav {
header h1, display: flex;
header p { justify-content: space-between;
margin: 0;
} }
/* Add a little padding to ensure spacing is correct between content and nav */ body > nav ul {
main { list-style: none;
padding-top: 1.5rem; margin: 0 1rem;
padding: 0;
} }
/* Fix line height when title wraps */ body > nav li {
h1,
h2,
h3 {
line-height: 1.1;
}
/* Format navigation */
nav {
font-size: 1rem;
line-height: 2;
padding: 1rem 0;
}
nav a {
margin: 1rem 1rem 0 0;
border: 1px solid var(--border);
border-radius: 5px;
color: var(--text) !important;
display: inline-block; display: inline-block;
padding: 0.1rem 1rem; padding: 0 0.5rem;
text-decoration: none;
transition: 0.4s;
} }
nav a:hover { nav form {
color: var(--accent) !important; display: inline-block;
border-color: var(--accent);
} }
nav a.current:hover { nav button {
text-decoration: none; border: none;
background: none;
font-family: unset;
font-size: unset;
color: blue;
text-decoration: underline;
cursor: pointer;
}
main {
padding: 1rem;
}
div#main {
display: flex;
flex-flow: row wrap;
}
div#main nav {
flex: 1 1 25%;
}
div#main > div {
flex: 10 1 40%;
}
main nav ul {
list-style: none;
margin: 1rem;
padding: 0;
} }
footer { footer {
margin-top: 4rem;
padding: 2rem 1rem 1.5rem 1rem;
color: var(--text-light);
font-size: 0.9rem;
text-align: center; text-align: center;
border-top: 1px solid var(--border);
} }
/* Format headers */ a {
h1 { color: blue;
font-size: calc(
var(--base-fontsize) * var(--header-scale) * var(--header-scale) *
var(--header-scale) * var(--header-scale)
);
margin-top: calc(var(--line-height) * 1.5rem);
}
h2 {
font-size: calc(
var(--base-fontsize) * var(--header-scale) * var(--header-scale) *
var(--header-scale)
);
margin-top: calc(var(--line-height) * 1.5rem);
}
h3 {
font-size: calc(
var(--base-fontsize) * var(--header-scale) * var(--header-scale)
);
margin-top: calc(var(--line-height) * 1.5rem);
}
h4 {
font-size: calc(var(--base-fontsize) * var(--header-scale));
margin-top: calc(var(--line-height) * 1.5rem);
}
h5 {
font-size: var(--base-fontsize);
margin-top: calc(var(--line-height) * 1.5rem);
}
h6 {
font-size: calc(var(--base-fontsize) / var(--header-scale));
margin-top: calc(var(--line-height) * 1.5rem);
}
/* Format links & buttons */
a,
a:visited {
color: var(--accent);
}
a:hover {
text-decoration: none;
}
a button,
button,
[role="button"],
input[type="submit"],
input[type="reset"],
input[type="button"] {
border: none;
border-radius: 5px;
background: var(--accent);
font-size: 1rem;
color: var(--bg);
padding: 0.7rem 0.9rem;
margin: 0.5rem 0;
transition: 0.4s;
}
a button[disabled],
button[disabled],
[role="button"][aria-disabled="true"],
input[type="submit"][disabled],
input[type="reset"][disabled],
input[type="button"][disabled],
input[type="checkbox"][disabled],
input[type="radio"][disabled],
select[disabled] {
cursor: default;
opacity: 0.5;
cursor: not-allowed;
}
input:disabled,
textarea:disabled,
select:disabled {
cursor: not-allowed;
background-color: var(--disabled);
}
input[type="range"] {
padding: 0;
}
/* Set the cursor to '?' while hovering over an abbreviation */
abbr {
cursor: help;
}
button:focus,
button:enabled:hover,
[role="button"]:focus,
[role="button"]:not([aria-disabled="true"]):hover,
input[type="submit"]:focus,
input[type="submit"]:enabled:hover,
input[type="reset"]:focus,
input[type="reset"]:enabled:hover,
input[type="button"]:focus,
input[type="button"]:enabled:hover,
input[type="checkbox"]:focus,
input[type="checkbox"]:enabled:hover,
input[type="radio"]:focus,
input[type="radio"]:enabled:hover {
filter: brightness(1.4);
cursor: pointer;
}
/* Format the expanding box */
details {
background: var(--accent-bg);
border: 1px solid var(--border);
border-radius: 5px;
margin-bottom: 1rem;
}
summary {
cursor: pointer;
font-weight: bold;
padding: 0.6rem 1rem;
}
details[open] {
padding: 0.6rem 1rem 0.75rem 1rem;
}
details[open] summary {
margin-bottom: 0.5rem;
padding: 0;
}
details[open] > *:last-child {
margin-bottom: 0;
}
/* Format tables */
table {
border-collapse: collapse;
width: 100%;
margin: 1.5rem 0;
}
td,
th {
border: 1px solid var(--border);
text-align: left;
padding: 0.5rem;
}
th {
background: var(--accent-bg);
font-weight: bold;
}
tr:nth-child(even) {
/* Set every other cell slightly darker. Improves readability. */
background: var(--accent-bg);
}
table caption {
font-weight: bold;
margin-bottom: 0.5rem;
}
/* Lists */
ol,
ul {
padding-left: 3rem;
}
/* Format forms */
textarea,
select,
input {
font-size: inherit;
font-family: inherit;
padding: 0.5rem;
margin-bottom: 0.5rem;
color: var(--text);
background: var(--bg);
border: 1px solid var(--border);
border-radius: 5px;
box-shadow: none;
box-sizing: border-box;
width: 60%;
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
}
/* Add arrow to */
select {
background-image: linear-gradient(45deg, transparent 49%, var(--text) 51%),
linear-gradient(135deg, var(--text) 51%, transparent 49%);
background-position: calc(100% - 20px), calc(100% - 15px);
background-size: 5px 5px, 5px 5px;
background-repeat: no-repeat;
}
select[multiple] {
background-image: none !important;
}
/* checkbox and radio button style */
input[type="checkbox"],
input[type="radio"] {
vertical-align: bottom;
position: relative;
}
input[type="radio"] {
border-radius: 100%;
}
input[type="checkbox"]:checked,
input[type="radio"]:checked {
background: var(--accent);
}
input[type="checkbox"]:checked::after {
/* Creates a rectangle with colored right and bottom borders which is rotated to look like a check mark */
content: " ";
width: 0.1em;
height: 0.25em;
border-radius: 0;
position: absolute;
top: 0.05em;
left: 0.18em;
background: transparent;
border-right: solid var(--bg) 0.08em;
border-bottom: solid var(--bg) 0.08em;
font-size: 1.8em;
transform: rotate(45deg);
}
input[type="radio"]:checked::after {
/* creates a colored circle for the checked radio button */
content: " ";
width: 0.25em;
height: 0.25em;
border-radius: 100%;
position: absolute;
top: 0.125em;
background: var(--bg);
left: 0.125em;
font-size: 32px;
}
/* Make the textarea wider than other inputs */
textarea {
width: 80%;
}
/* Makes input fields wider on smaller screens */
@media only screen and (max-width: 720px) {
textarea,
select,
input {
width: 100%;
}
}
/* Ensures the checkbox and radio inputs do not have a set width like other input fields */
input[type="checkbox"],
input[type="radio"] {
width: auto;
}
/* do not show border around file selector button */
input[type="file"] {
border: 0;
}
/* Without this any HTML using <fieldset> shows ugly borders and has additional padding/margin. (Issue #3) */
fieldset {
border: 0;
padding: 0;
margin: 0;
}
/* Misc body elements */
hr {
color: var(--border);
border-top: 1px;
margin: 1rem auto;
}
mark {
padding: 2px 5px;
border-radius: 4px;
background: var(--marked);
}
main img,
main video {
max-width: 100%;
height: auto;
border-radius: 5px;
}
figure {
margin: 0;
}
figcaption {
font-size: 0.9rem;
color: var(--text-light);
text-align: center;
margin-bottom: 1rem;
}
blockquote {
margin: 2rem 0 2rem 2rem;
padding: 0.4rem 0.8rem;
border-left: 0.35rem solid var(--accent);
opacity: 0.8;
font-style: italic;
}
cite {
font-size: 0.9rem;
color: var(--text-light);
font-style: normal;
}
/* Use mono font for code like elements */
code,
pre,
pre span,
kbd,
samp {
font-size: 1.075rem;
font-family: var(--mono-font);
color: var(--code);
}
kbd {
color: var(--preformatted);
border: 1px solid var(--preformatted);
border-bottom: 3px solid var(--preformatted);
border-radius: 5px;
padding: 0.1rem;
}
pre {
padding: 1rem 1.4rem;
max-width: 100%;
overflow: auto;
overflow-x: auto;
color: var(--preformatted);
background: var(--accent-bg);
border: 1px solid var(--border);
border-radius: 5px;
}
/* Fix embedded code within pre */
pre code {
color: var(--preformatted);
background: none;
margin: 0;
padding: 0;
} }

81
ui/views/common.templ Normal file
View File

@ -0,0 +1,81 @@
package views
import "git.32bit.cafe/32bitcafe/guestbook/internal/models"
import "strconv"
type CommonData struct {
CurrentYear int
Flash string
IsAuthenticated bool
CSRFToken string
CurrentUser *models.User
IsHtmx bool
}
func slugToShortId(slug string) uint64 {
id, _ := strconv.ParseUint(slug, 36, 64)
return id
}
templ commonHeader() {
<header>
<h1><a href="/">webweav.ing</a></h1>
</header>
}
templ topNav(data CommonData) {
<nav>
<div>
<ul>
<li><a href="/guestbooks">Guestbooks</a></li>
<li>RSS Feeds</li>
</ul>
</div>
<div>
if data.IsAuthenticated {
Welcome, { data.CurrentUser.Username }
<a href="/users/settings">Settings</a> |
<form action="/users/logout" method="post">
<input type="hidden" name="csrf_token" value={ data.CSRFToken }>
<button>Logout</button>
</form>
} else {
<a href="/users/register">Create an Account</a> |
<a href="/users/login">Login</a>
}
</div>
</nav>
}
templ commonFooter() {
<footer>
<p>Generated with Templ</p>
<p>A <a href="https://32bit.cafe">32-bit cafe</a> Project</p>
</footer>
}
templ base(title string, data CommonData) {
<!DOCTYPE html>
<html lang="en">
<head>
<title>{ title } - webweav.ing</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/static/css/style.css" rel="stylesheet">
<script src="/static/js/htmx.min.js"></script>
</head>
<body>
@commonHeader()
@topNav(data)
<main>
if data.Flash != "" {
<div class="flash">{ data.Flash }</div>
}
{ children... }
</main>
@commonFooter()
</body>
</html>
}

245
ui/views/common_templ.go Normal file
View File

@ -0,0 +1,245 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package views
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import "git.32bit.cafe/32bitcafe/guestbook/internal/models"
import "strconv"
type CommonData struct {
CurrentYear int
Flash string
IsAuthenticated bool
CSRFToken string
CurrentUser *models.User
IsHtmx bool
}
func slugToShortId(slug string) uint64 {
id, _ := strconv.ParseUint(slug, 36, 64)
return id
}
func commonHeader() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<header><h1><a href=\"/\">webweav.ing</a></h1></header>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func topNav(data CommonData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
if templ_7745c5c3_Var2 == nil {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<nav><div><ul><li><a href=\"/guestbooks\">Guestbooks</a></li><li>RSS Feeds</li></ul></div><div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.IsAuthenticated {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "Welcome, ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.CurrentUser.Username)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 36, Col: 52}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " <a href=\"/users/settings\">Settings</a> | <form action=\"/users/logout\" method=\"post\"><input type=\"hidden\" name=\"csrf_token\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 39, Col: 81}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\"> <button>Logout</button></form>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<a href=\"/users/register\">Create an Account</a> | <a href=\"/users/login\">Login</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</div></nav>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func commonFooter() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var5 := templ.GetChildren(ctx)
if templ_7745c5c3_Var5 == nil {
templ_7745c5c3_Var5 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<footer><p>Generated with Templ</p><p>A <a href=\"https://32bit.cafe\">32-bit cafe</a> Project</p></footer>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func base(title string, data CommonData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
if templ_7745c5c3_Var6 == nil {
templ_7745c5c3_Var6 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<!doctype html><html lang=\"en\"><head><title>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 62, Col: 26}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " - webweav.ing</title><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><link href=\"/static/css/style.css\" rel=\"stylesheet\"><script src=\"/static/js/htmx.min.js\"></script></head><body>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = commonHeader().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = topNav(data).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<main>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if data.Flash != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"flash\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(data.Flash)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/common.templ`, Line: 73, Col: 51}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templ_7745c5c3_Var6.Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</main>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = commonFooter().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</body></html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

15
ui/views/common_templ.txt Normal file
View File

@ -0,0 +1,15 @@
<header><h1><a href=\"/\">webweav.ing</a></h1></header>
<nav><div><ul><li><a href=\"/guestbooks\">Guestbooks</a></li><li>RSS Feeds</li></ul></div><div>
Welcome,
<a href=\"/users/settings\">Settings</a> | <form action=\"/users/logout\" method=\"post\"><input type=\"hidden\" name=\"csrf_token\" value=\"
\"> <button>Logout</button></form>
<a href=\"/users/register\">Create an Account</a> | <a href=\"/users/login\">Login</a>
</div></nav>
<footer><p>Generated with Templ</p><p>A <a href=\"https://32bit.cafe\">32-bit cafe</a> Project</p></footer>
<!doctype html><html lang=\"en\"><head><title>
- webweav.ing</title><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><link href=\"/static/css/style.css\" rel=\"stylesheet\"><script src=\"/static/js/htmx.min.js\"></script></head><body>
<main>
<div class=\"flash\">
</div>
</main>
</body></html>

116
ui/views/guestbooks.templ Normal file
View File

@ -0,0 +1,116 @@
package views
import "fmt"
import "git.32bit.cafe/32bitcafe/guestbook/internal/models"
templ gbCreateForm(csrf_token string) {
<input type="hidden" name="csrf_token" value={ csrf_token }>
<label for="siteurl">Site URL: </label>
<input type="text" name="siteurl" id="siteurl" required />
<button type="submit">Submit</button>
}
templ gbList(guestbooks []models.Guestbook) {
if len(guestbooks) == 0 {
<p>No Guestbooks yet</p>
} else {
<ul id="guestbooks" hx-get="/guestbooks" hx-trigger="newGuestbook from:body" hx-swap="outerHTML">
for _, gb := range guestbooks {
{{ gbUrl := fmt.Sprintf("/guestbooks/%s", gb.Slug()) }}
<li><a href={ templ.URL(gbUrl) }>
if len(gb.SiteUrl) == 0 {
Untitled
} else {
{ gb.SiteUrl }
}
</a></li>
}
</ul>
}
}
templ GuestbookList(title string, data CommonData, guestbooks []models.Guestbook) {
if data.IsHtmx {
@gbList(guestbooks)
} else {
@base(title, data) {
<h1>My Guestbooks</h1>
<div>
<button hx-get="/guestbooks/create" hx-target="closest div">New Guestbook</button>
</div>
@gbList(guestbooks)
}
}
}
templ GuestbookCreate(title string, data CommonData) {
if data.IsHtmx {
<form hx-post="/guestbooks/create" hx-target="closest div">
@gbCreateForm(data.CSRFToken)
</form>
} else {
@base(title, data) {
<form action="/guestbooks/create" method="post">
@gbCreateForm(data.CSRFToken)
</form>
}
}
}
templ sidebar(guestbook models.Guestbook) {
{{ gbUrl := fmt.Sprintf("/guestbooks/%s", guestbook.Slug()) }}
<nav>
<div>
<h3>Messages</h3>
<ul>
<li><a href={ templ.URL(gbUrl + "/comments") }>Manage messages</a></li>
<li><a href={ templ.URL(gbUrl + "/comments/queue") }>Review message queue</a></li>
<li><a href={ templ.URL(gbUrl + "/blocklist") }>Block users</a></li>
<li><a href={ templ.URL(gbUrl + "/comments/trash") }>Trash</a></li>
</ul>
</div>
<div>
<h3>Design</h3>
<ul>
<li><a href={ templ.URL(gbUrl + "/themes") }>Themes</a></li>
<li><a href={ templ.URL(gbUrl + "/customize") }>Custom CSS</a></li>
</ul>
</div>
<div>
<h3>Account</h3>
<ul>
<li><a href="/users/settings">Settings</a></li>
<li><a href="/users/privacy">Privacy</a></li>
<li><a href="/help">Help</a></li>
</ul>
</div>
</nav>
}
templ GuestbookView(title string, data CommonData, guestbook models.Guestbook, comments []models.GuestbookComment) {
@base(title, data) {
<div id="main">
@sidebar(guestbook)
<div>
<h1>Guestbook for { guestbook.SiteUrl }</h1>
{{ commentCreateUrl := fmt.Sprintf("/guestbooks/%s/comments/create", guestbook.Slug()) }}
<p>
<a href={ templ.URL(commentCreateUrl) }>New Comment</a>
</p>
if len(comments) == 0 {
<p>No comments yet!</p>
}
for _, c := range comments {
<div>
<strong>{ c.AuthorName }</strong>
{ c.Created.Format("01-02-2006 03:04PM") }
<p>
{ c.CommentText }
</p>
</div>
}
</div>
</div>
}
}

View File

@ -0,0 +1,481 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package views
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import "fmt"
import "git.32bit.cafe/32bitcafe/guestbook/internal/models"
func gbCreateForm(csrf_token string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<input type=\"hidden\" name=\"csrf_token\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(csrf_token)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 7, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"> <label for=\"siteurl\">Site URL: </label> <input type=\"text\" name=\"siteurl\" id=\"siteurl\" required> <button type=\"submit\">Submit</button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func gbList(guestbooks []models.Guestbook) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
if len(guestbooks) == 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<p>No Guestbooks yet</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<ul id=\"guestbooks\" hx-get=\"/guestbooks\" hx-trigger=\"newGuestbook from:body\" hx-swap=\"outerHTML\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, gb := range guestbooks {
gbUrl := fmt.Sprintf("/guestbooks/%s", gb.Slug())
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<li><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 templ.SafeURL = templ.URL(gbUrl)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(gb.SiteUrl) == 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "Untitled")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(gb.SiteUrl)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 24, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</a></li>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</ul>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
func GuestbookList(title string, data CommonData, guestbooks []models.Guestbook) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
if templ_7745c5c3_Var6 == nil {
templ_7745c5c3_Var6 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
if data.IsHtmx {
templ_7745c5c3_Err = gbList(guestbooks).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Var7 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<h1>My Guestbooks</h1><div><button hx-get=\"/guestbooks/create\" hx-target=\"closest div\">New Guestbook</button></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = gbList(guestbooks).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var7), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
func GuestbookCreate(title string, data CommonData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
if templ_7745c5c3_Var8 == nil {
templ_7745c5c3_Var8 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
if data.IsHtmx {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<form hx-post=\"/guestbooks/create\" hx-target=\"closest div\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = gbCreateForm(data.CSRFToken).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</form>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Var9 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<form action=\"/guestbooks/create\" method=\"post\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = gbCreateForm(data.CSRFToken).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</form>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
}
func sidebar(guestbook models.Guestbook) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var10 := templ.GetChildren(ctx)
if templ_7745c5c3_Var10 == nil {
templ_7745c5c3_Var10 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
gbUrl := fmt.Sprintf("/guestbooks/%s", guestbook.Slug())
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<nav><div><h3>Messages</h3><ul><li><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 templ.SafeURL = templ.URL(gbUrl + "/comments")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var11)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\">Manage messages</a></li><li><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 templ.SafeURL = templ.URL(gbUrl + "/comments/queue")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var12)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\">Review message queue</a></li><li><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 templ.SafeURL = templ.URL(gbUrl + "/blocklist")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var13)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">Block users</a></li><li><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 templ.SafeURL = templ.URL(gbUrl + "/comments/trash")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var14)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\">Trash</a></li></ul></div><div><h3>Design</h3><ul><li><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 templ.SafeURL = templ.URL(gbUrl + "/themes")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var15)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\">Themes</a></li><li><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 templ.SafeURL = templ.URL(gbUrl + "/customize")
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var16)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\">Custom CSS</a></li></ul></div><div><h3>Account</h3><ul><li><a href=\"/users/settings\">Settings</a></li><li><a href=\"/users/privacy\">Privacy</a></li><li><a href=\"/help\">Help</a></li></ul></div></nav>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func GuestbookView(title string, data CommonData, guestbook models.Guestbook, comments []models.GuestbookComment) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var17 := templ.GetChildren(ctx)
if templ_7745c5c3_Var17 == nil {
templ_7745c5c3_Var17 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var18 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<div id=\"main\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = sidebar(guestbook).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<div><h1>Guestbook for ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(guestbook.SiteUrl)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 96, Col: 53}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</h1>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
commentCreateUrl := fmt.Sprintf("/guestbooks/%s/comments/create", guestbook.Slug())
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<p><a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var20 templ.SafeURL = templ.URL(commentCreateUrl)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var20)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\">New Comment</a></p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(comments) == 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<p>No comments yet!</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
for _, c := range comments {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<div><strong>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorName)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 106, Col: 46}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</strong> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(c.Created.Format("01-02-2006 03:04PM"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 107, Col: 64}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var23 string
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(c.CommentText)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/guestbooks.templ`, Line: 109, Col: 43}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var18), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

View File

@ -0,0 +1,32 @@
<input type=\"hidden\" name=\"csrf_token\" value=\"
\"> <label for=\"siteurl\">Site URL: </label> <input type=\"text\" name=\"siteurl\" id=\"siteurl\" required> <button type=\"submit\">Submit</button>
<p>No Guestbooks yet</p>
<ul id=\"guestbooks\" hx-get=\"/guestbooks\" hx-trigger=\"newGuestbook from:body\" hx-swap=\"outerHTML\">
<li><a href=\"
\">
Untitled
</a></li>
</ul>
<h1>My Guestbooks</h1><div><button hx-get=\"/guestbooks/create\" hx-target=\"closest div\">New Guestbook</button></div>
<form hx-post=\"/guestbooks/create\" hx-target=\"closest div\">
</form>
<form action=\"/guestbooks/create\" method=\"post\">
</form>
<nav><div><h3>Messages</h3><ul><li><a href=\"
\">Manage messages</a></li><li><a href=\"
\">Review message queue</a></li><li><a href=\"
\">Block users</a></li><li><a href=\"
\">Trash</a></li></ul></div><div><h3>Design</h3><ul><li><a href=\"
\">Themes</a></li><li><a href=\"
\">Custom CSS</a></li></ul></div><div><h3>Account</h3><ul><li><a href=\"/users/settings\">Settings</a></li><li><a href=\"/users/privacy\">Privacy</a></li><li><a href=\"/help\">Help</a></li></ul></div></nav>
<div id=\"main\">
<div><h1>Guestbook for
</h1>
<p><a href=\"
\">New Comment</a></p>
<p>No comments yet!</p>
<div><strong>
</strong>
<p>
</p></div>
</div></div>

25
ui/views/home.templ Normal file
View File

@ -0,0 +1,25 @@
package views
templ loggedInHome() {
<h2>Tools</h2>
<p>
<a href="/guestbooks">Guestbooks</a>
</p>
}
templ loggedOutHome() {
<h2>Welcome</h2>
<p>
Welcome to webweav.ing, a collection of webmastery tools created by the <a href="https://32bit.cafe">32-Bit Cafe</a>.
</p>
}
templ Home(title string, data CommonData) {
@base(title, data) {
if data.IsAuthenticated {
@loggedInHome()
} else {
@loggedOutHome()
}
}
}

123
ui/views/home_templ.go Normal file
View File

@ -0,0 +1,123 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package views
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func loggedInHome() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<h2>Tools</h2><p><a href=\"/guestbooks\">Guestbooks</a></p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func loggedOutHome() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
if templ_7745c5c3_Var2 == nil {
templ_7745c5c3_Var2 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<h2>Welcome</h2><p>Welcome to webweav.ing, a collection of webmastery tools created by the <a href=\"https://32bit.cafe\">32-Bit Cafe</a>.</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func Home(title string, data CommonData) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var4 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
if data.IsAuthenticated {
templ_7745c5c3_Err = loggedInHome().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = loggedOutHome().Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
return nil
})
templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate

2
ui/views/home_templ.txt Normal file
View File

@ -0,0 +1,2 @@
<h2>Tools</h2><p><a href=\"/guestbooks\">Guestbooks</a></p>
<h2>Welcome</h2><p>Welcome to webweav.ing, a collection of webmastery tools created by the <a href=\"https://32bit.cafe\">32-Bit Cafe</a>.</p>

83
ui/views/users.templ Normal file
View File

@ -0,0 +1,83 @@
package views
import (
"git.32bit.cafe/32bitcafe/guestbook/internal/forms"
"git.32bit.cafe/32bitcafe/guestbook/internal/models"
)
templ UserLogin (title string, data CommonData, form forms.UserLoginForm) {
@base(title, data) {
<h1>Login</h1>
<form action="/users/login" method="POST" novalidate>
<input type="hidden" name="csrf_token" value={ data.CSRFToken }>
for _, error := range form.NonFieldErrors {
<div class="error">{ error }</div>
}
<div>
<label>Email: </label>
{{ error, exists := form.FieldErrors["email"] }}
if exists {
<label class="error">{ error }</label>
}
<input type="email" name="email" value={form.Email}>
</div>
<div>
<label>Password: </label>
{{ error, exists = form.FieldErrors["password"] }}
if exists {
<label class="error">{ error }</label>
}
<input type="password" name="password">
</div>
<div>
<input type="submit" value="login">
</div>
</form>
}
}
templ UserRegistration (title string, data CommonData, form forms.UserRegistrationForm) {
@base(title, data) {
<h1>User Registration</h1>
<form action="/users/register" method="post">
<input type="hidden" name="csrf_token" value={ data.CSRFToken }>
<div>
{{ error, exists := form.FieldErrors["name"] }}
<label for="username">Username: </label>
if exists {
<label class="error">{ error }</label>
}
<input type="text" name="username" id="username" value={form.Name} required />
</div>
<div>
{{ error, exists = form.FieldErrors["email"] }}
<label for="email">Email: </label>
if exists {
<label class="error">{ error }</label>
}
<input type="text" name="email" id="email" value={form.Email} required />
</div>
<div>
{{ error, exists = form.FieldErrors["password"] }}
<label for="password">Password: </label>
if exists {
<label class="error">{ error }</label>
}
<input type="password" name="password" id="password" />
</div>
<div>
<input type="submit" value="Register" />
</div>
</form>
}
}
templ UserProfile (title string, data CommonData, user models.User) {
@base(title, data) {
<h1>{ user.Username }</h1>
<p>{ user.Email }</p>
}
}
templ UserSettings () {
}

428
ui/views/users_templ.go Normal file
View File

@ -0,0 +1,428 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.833
package views
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"git.32bit.cafe/32bitcafe/guestbook/internal/forms"
"git.32bit.cafe/32bitcafe/guestbook/internal/models"
)
func UserLogin(title string, data CommonData, form forms.UserLoginForm) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<h1>Login</h1><form action=\"/users/login\" method=\"POST\" novalidate><input type=\"hidden\" name=\"csrf_token\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 12, Col: 73}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, error := range form.NonFieldErrors {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div class=\"error\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(error)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 14, Col: 42}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div><label>Email: </label>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
error, exists := form.FieldErrors["email"]
if exists {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<label class=\"error\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(error)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 20, Col: 48}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<input type=\"email\" name=\"email\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 22, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\"></div><div><label>Password: </label>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
error, exists = form.FieldErrors["password"]
if exists {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<label class=\"error\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(error)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 28, Col: 48}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<input type=\"password\" name=\"password\"></div><div><input type=\"submit\" value=\"login\"></div></form>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func UserRegistration(title string, data CommonData, form forms.UserRegistrationForm) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
if templ_7745c5c3_Var8 == nil {
templ_7745c5c3_Var8 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var9 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<h1>User Registration</h1><form action=\"/users/register\" method=\"post\"><input type=\"hidden\" name=\"csrf_token\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(data.CSRFToken)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 43, Col: 73}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\"><div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
error, exists := form.FieldErrors["name"]
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "<label for=\"username\">Username: </label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if exists {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<label class=\"error\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(error)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 48, Col: 48}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<input type=\"text\" name=\"username\" id=\"username\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(form.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 50, Col: 81}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "\" required></div><div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
error, exists = form.FieldErrors["email"]
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<label for=\"email\">Email: </label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if exists {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<label class=\"error\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(error)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 56, Col: 48}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<input type=\"text\" name=\"email\" id=\"email\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(form.Email)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 58, Col: 76}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\" required></div><div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
error, exists = form.FieldErrors["password"]
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<label for=\"password\">Password: </label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if exists {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<label class=\"error\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(error)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 64, Col: 48}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</label> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<input type=\"password\" name=\"password\" id=\"password\"></div><div><input type=\"submit\" value=\"Register\"></div></form>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func UserProfile(title string, data CommonData, user models.User) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var16 := templ.GetChildren(ctx)
if templ_7745c5c3_Var16 == nil {
templ_7745c5c3_Var16 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var17 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<h1>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(user.Username)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 77, Col: 27}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</h1><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/users.templ`, Line: 78, Col: 23}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
templ_7745c5c3_Err = base(title, data).Render(templ.WithChildren(ctx, templ_7745c5c3_Var17), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return nil
})
}
func UserSettings() templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var20 := templ.GetChildren(ctx)
if templ_7745c5c3_Var20 == nil {
templ_7745c5c3_Var20 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
return nil
})
}
var _ = templruntime.GeneratedTemplate

31
ui/views/users_templ.txt Normal file
View File

@ -0,0 +1,31 @@
<h1>Login</h1><form action=\"/users/login\" method=\"POST\" novalidate><input type=\"hidden\" name=\"csrf_token\" value=\"
\">
<div class=\"error\">
</div>
<div><label>Email: </label>
<label class=\"error\">
</label>
<input type=\"email\" name=\"email\" value=\"
\"></div><div><label>Password: </label>
<label class=\"error\">
</label>
<input type=\"password\" name=\"password\"></div><div><input type=\"submit\" value=\"login\"></div></form>
<h1>User Registration</h1><form action=\"/users/register\" method=\"post\"><input type=\"hidden\" name=\"csrf_token\" value=\"
\"><div>
<label for=\"username\">Username: </label>
<label class=\"error\">
</label>
<input type=\"text\" name=\"username\" id=\"username\" value=\"
\" required></div><div>
<label for=\"email\">Email: </label>
<label class=\"error\">
</label>
<input type=\"text\" name=\"email\" id=\"email\" value=\"
\" required></div><div>
<label for=\"password\">Password: </label>
<label class=\"error\">
</label>
<input type=\"password\" name=\"password\" id=\"password\"></div><div><input type=\"submit\" value=\"Register\"></div></form>
<h1>
</h1><p>
</p>