summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRené 'Necoro' Neumann <necoro@necoro.eu>2024-10-17 16:37:23 +0200
committerRené 'Necoro' Neumann <necoro@necoro.eu>2024-10-17 16:37:23 +0200
commitb2447bc967df37b31282a97e32c581954bb8bcc9 (patch)
tree39758d1121fae6dc1d27e8a45035690421900d6c
parent789d21034e526a03d3e91d5d284a4888be938340 (diff)
downloadgosten-b2447bc967df37b31282a97e32c581954bb8bcc9.tar.gz
gosten-b2447bc967df37b31282a97e32c581954bb8bcc9.tar.bz2
gosten-b2447bc967df37b31282a97e32c581954bb8bcc9.zip
Move from html/template to templ
Diffstat (limited to '')
-rw-r--r--csrf/csrf.go17
-rw-r--r--form/builder.go96
-rw-r--r--form/errors.go30
-rw-r--r--form/field.templ42
-rw-r--r--form/field_templ.go239
-rw-r--r--form/form.go21
-rw-r--r--form/reflect.go12
-rw-r--r--go.mod1
-rw-r--r--go.sum8
-rw-r--r--pages/base.templ77
-rw-r--r--pages/base_templ.go264
-rw-r--r--pages/login.go10
-rw-r--r--pages/login.templ (renamed from templ/login.tpl)16
-rw-r--r--pages/login_templ.go74
-rw-r--r--pages/page.go58
-rw-r--r--pages/pages.go8
-rw-r--r--pages/pages.templ46
-rw-r--r--pages/pages_templ.go269
-rw-r--r--templ/404.tpl10
-rw-r--r--templ/base.tpl20
-rw-r--r--templ/categories.tpl9
-rw-r--r--templ/content.tpl36
-rw-r--r--templ/form.tpl22
-rw-r--r--templ/index.tpl3
-rw-r--r--templ/navlinks.tpl8
-rw-r--r--templ/recur.tpl9
-rw-r--r--templ/template.go73
27 files changed, 1138 insertions, 340 deletions
diff --git a/csrf/csrf.go b/csrf/csrf.go
index 18fdb81..fd73c0d 100644
--- a/csrf/csrf.go
+++ b/csrf/csrf.go
@@ -4,6 +4,7 @@ import (
"html/template"
"net/http"
+ "github.com/a-h/templ"
"github.com/gorilla/csrf"
"github.com/gorilla/securecookie"
)
@@ -12,18 +13,22 @@ func Handler() func(http.Handler) http.Handler {
return csrf.Protect(
securecookie.GenerateRandomKey(32),
csrf.SameSite(csrf.SameSiteStrictMode),
- csrf.FieldName("csrf.csrffield"), // should match the structure in `Csrf`
+ csrf.FieldName("csrffield.field"), // should match the structure in `Csrf`
)
}
-// Csrf handles the CSRF data for a form.
+// CsrfField handles the CSRF data for a form.
// Include it verbatim and then use `{{.CsrfField}}` in templates.
-type Csrf struct {
- CsrfField template.HTML `form:"-" schema:"-"`
+type CsrfField struct {
+ field template.HTML `form:"-" schema:"-"`
}
-func (c *Csrf) SetCsrfField(r *http.Request) {
- c.CsrfField = csrf.TemplateField(r)
+func (c *CsrfField) SetCsrfField(r *http.Request) {
+ c.field = csrf.TemplateField(r)
+}
+
+func (c *CsrfField) Csrf() templ.Component {
+ return templ.Raw(c.field)
}
type Enabled interface {
diff --git a/form/builder.go b/form/builder.go
deleted file mode 100644
index 97e972f..0000000
--- a/form/builder.go
+++ /dev/null
@@ -1,96 +0,0 @@
-package form
-
-import (
- "errors"
- "fmt"
- "html/template"
- "strings"
-)
-
-type builder struct {
- tpl *template.Template
-}
-
-// Inputs will parse the provided struct into fields and then execute the
-// template with each field. The returned HTML is simply all of these results
-// appended one after another.
-//
-// Inputs' second argument - errs - will be used to render errors for
-// individual fields.
-func (b *builder) Inputs(v interface{}, errs ...error) (template.HTML, error) {
- tpl, err := b.tpl.Clone()
- if err != nil {
- return "", err
- }
- fields := fields(v)
- errors := fieldErrors(errs)
- var html template.HTML
- for _, field := range fields {
- var sb strings.Builder
- tpl.Funcs(template.FuncMap{
- "errors": func() []string {
- if errs, ok := errors[field.Name]; ok {
- return errs
- }
- return nil
- },
- })
- err := tpl.Execute(&sb, field)
- if err != nil {
- return "", err
- }
- html = html + template.HTML(sb.String())
- }
- return html, nil
-}
-
-// FuncMap returns a template.FuncMap that defines both the inputs_for and
-// inputs_and_errors_for functions for usage in the template package. The
-// latter is provided via a closure because variadic parameters and the
-// template package don't play very nicely and this just simplifies things
-// a lot for end users of the form package.
-func FuncMap(formTpl *template.Template) template.FuncMap {
- b := builder{tpl: formTpl}
- return template.FuncMap{
- "inputs_for": b.Inputs,
- "inputs_and_errors_for": func(v interface{}, errs []error) (template.HTML, error) {
- return b.Inputs(v, errs...)
- },
- }
-}
-
-// ParsingFuncMap is present to make it a little easier to build the input template.
-// In order to parse a template that uses the `errors` function, you need to have
-// that template defined when the template is parsed. We clearly don't know whether
-// a field has an error or not until it is parsed.
-func ParsingFuncMap() template.FuncMap {
- return template.FuncMap{
- "errors": func() []string {
- return nil
- },
- }
-}
-
-type FieldError struct {
- Field string
- Issue string
-}
-
-func (fe FieldError) Error() string {
- return fmt.Sprintf("%s: %v", fe.Field, fe.Issue)
-}
-
-// errors will build a map where each key is the field name, and each
-// value is a slice of strings representing errors with that field.
-func fieldErrors(errs []error) map[string][]string {
- ret := make(map[string][]string)
- for _, err := range errs {
- var fe FieldError
- if !errors.As(err, &fe) {
- fmt.Println(err, "isnt field error")
- continue
- }
- ret[fe.Field] = append(ret[fe.Field], fe.Issue)
- }
- return ret
-}
diff --git a/form/errors.go b/form/errors.go
new file mode 100644
index 0000000..52206c4
--- /dev/null
+++ b/form/errors.go
@@ -0,0 +1,30 @@
+package form
+
+import (
+ "errors"
+ "fmt"
+)
+
+type FieldError struct {
+ Field string
+ Issue string
+}
+
+func (fe FieldError) Error() string {
+ return fmt.Sprintf("%s: %v", fe.Field, fe.Issue)
+}
+
+// errors will build a map where each key is the field name, and each
+// value is a slice of strings representing errors with that field.
+func fieldErrors(errs []error) map[string][]string {
+ ret := make(map[string][]string)
+ for _, err := range errs {
+ var fe FieldError
+ if !errors.As(err, &fe) {
+ fmt.Println(err, "isnt field error")
+ continue
+ }
+ ret[fe.Field] = append(ret[fe.Field], fe.Issue)
+ }
+ return ret
+}
diff --git a/form/field.templ b/form/field.templ
new file mode 100644
index 0000000..e9ce33e
--- /dev/null
+++ b/form/field.templ
@@ -0,0 +1,42 @@
+package form
+
+import "fmt"
+
+func (f *field) isCheckbox() bool {
+ return f.Type == "checkbox"
+}
+
+func (f *field) optionAttributes() templ.Attributes {
+ attrs := make(map[string]any)
+ for _, o := range f.Options {
+ attrs[o] = true
+ }
+ return templ.Attributes(attrs)
+}
+
+templ (f *field) item(errors []string) {
+ <div class={"mb-3",
+ templ.KV("form-floating", !f.isCheckbox()),
+ templ.KV("form-check form-switch", f.isCheckbox())}>
+ <input
+ if f.ID != "" {id={f.ID}}
+ type={f.Type}
+ name={f.Name}
+ placeholder={f.Placeholder}
+ if f.Value != nil {value={fmt.Sprint(f.Value)}}
+ if f.isCheckbox() {
+ class="form-check-input"
+ } else {
+ class="form-control"
+ }
+ {f.optionAttributes()...}
+ >
+ <label if f.ID != "" {for={f.ID}}
+ if f.isCheckbox() {class="form-check-label"}>
+ {f.Label}
+ </label>
+ for _, e := range errors {
+ <p style="color:red">{e}</p>
+ }
+ </div>
+} \ No newline at end of file
diff --git a/form/field_templ.go b/form/field_templ.go
new file mode 100644
index 0000000..34266e8
--- /dev/null
+++ b/form/field_templ.go
@@ -0,0 +1,239 @@
+// Code generated by templ - DO NOT EDIT.
+
+// templ: version: v0.2.778
+package form
+
+//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"
+
+func (f *field) isCheckbox() bool {
+ return f.Type == "checkbox"
+}
+
+func (f *field) optionAttributes() templ.Attributes {
+ attrs := make(map[string]any)
+ for _, o := range f.Options {
+ attrs[o] = true
+ }
+ return templ.Attributes(attrs)
+}
+
+func (f *field) item(errors []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)
+ var templ_7745c5c3_Var2 = []any{"mb-3",
+ templ.KV("form-floating", !f.isCheckbox()),
+ templ.KV("form-check form-switch", f.isCheckbox())}
+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var2...)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var3 string
+ templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var2).String())
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `form/field.templ`, Line: 1, Col: 0}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><input")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if f.ID != "" {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" id=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var4 string
+ templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(f.ID)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `form/field.templ`, Line: 22, Col: 35}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" type=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var5 string
+ templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(f.Type)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `form/field.templ`, Line: 23, Col: 24}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" name=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var6 string
+ templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(f.Name)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `form/field.templ`, Line: 24, Col: 24}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" placeholder=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var7 string
+ templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(f.Placeholder)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `form/field.templ`, Line: 25, Col: 38}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if f.Value != nil {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" value=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var8 string
+ templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint(f.Value))
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `form/field.templ`, Line: 26, Col: 57}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ if f.isCheckbox() {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" class=\"form-check-input\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ } else {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" class=\"form-control\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ templ_7745c5c3_Err = templ.RenderAttributes(ctx, templ_7745c5c3_Buffer, f.optionAttributes())
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("> <label")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if f.ID != "" {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" for=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var9 string
+ templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(f.ID)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `form/field.templ`, Line: 34, Col: 39}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ if f.isCheckbox() {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" class=\"form-check-label\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var10 string
+ templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(f.Label)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `form/field.templ`, Line: 36, Col: 23}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</label> ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ for _, e := range errors {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<p style=\"color:red\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var11 string
+ templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(e)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `form/field.templ`, Line: 39, Col: 35}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+var _ = templruntime.GeneratedTemplate
diff --git a/form/form.go b/form/form.go
new file mode 100644
index 0000000..84758f5
--- /dev/null
+++ b/form/form.go
@@ -0,0 +1,21 @@
+package form
+
+import (
+ "context"
+ "io"
+
+ "github.com/a-h/templ"
+)
+
+func Form(v any, errs []error) templ.Component {
+ fields := fields(v)
+ errors := fieldErrors(errs)
+ return templ.ComponentFunc(func(ctx context.Context, w io.Writer) error {
+ for _, field := range fields {
+ if err := field.item(errors[field.Name]).Render(ctx, w); err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+}
diff --git a/form/reflect.go b/form/reflect.go
index 4dd4018..604c9e1 100644
--- a/form/reflect.go
+++ b/form/reflect.go
@@ -1,7 +1,6 @@
package form
import (
- "html/template"
"reflect"
"strings"
)
@@ -103,13 +102,6 @@ func (f *field) applyTags(tags map[string]string) {
if v, ok := tags["id"]; ok {
f.ID = v
}
- if v, ok := tags["footer"]; ok {
- // Probably shouldn't be HTML but whatever.
- f.Footer = template.HTML(v)
- }
- if v, ok := tags["class"]; ok {
- f.Class = v
- }
if v, ok := tags["options"]; ok {
f.Options = strings.Split(v, ",")
}
@@ -143,7 +135,5 @@ type field struct {
Type string
ID string
Options []string
- Value interface{}
- Footer template.HTML
- Class string
+ Value any
}
diff --git a/go.mod b/go.mod
index f1acb5c..f9a9cdb 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.23
toolchain go1.23.1
require (
+ github.com/a-h/templ v0.2.778
github.com/go-chi/chi/v5 v5.1.0
github.com/gorilla/csrf v1.7.2
github.com/gorilla/schema v1.4.1
diff --git a/go.sum b/go.sum
index 82440f9..07d430c 100644
--- a/go.sum
+++ b/go.sum
@@ -1,8 +1,12 @@
+github.com/a-h/templ v0.2.778 h1:VzhOuvWECrwOec4790lcLlZpP4Iptt5Q4K9aFxQmtaM=
+github.com/a-h/templ v0.2.778/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w=
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/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI=
@@ -28,8 +32,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
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=
-github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
diff --git a/pages/base.templ b/pages/base.templ
new file mode 100644
index 0000000..b78e9b5
--- /dev/null
+++ b/pages/base.templ
@@ -0,0 +1,77 @@
+package pages
+
+templ baseWithHeader(header templ.Component) {
+ <!DOCTYPE html>
+ <html lang="de">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link rel="icon" type="image/svg+xml" href="/static/favicon.svg" sizes="16x16 32x32 48x48">
+ <title>Kosten</title>
+ <link href="/static/custom.css" rel="stylesheet">
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
+ if header != nil {
+ @header
+ }
+ </head>
+ <body>
+ { children... }
+ </body>
+ </html>
+}
+
+func base() templ.Component {
+ return baseWithHeader(nil)
+}
+
+templ navlink(path, descr string) {
+ <li class="nav-item">
+ <a class={"nav-link", templ.KV("active", isCurrPath(ctx, path))}
+ href={templ.URL(path)}>{descr}</a>
+ </li>
+}
+
+templ navlinks() {
+ @navlink("/categories", "Kategorien")
+ @navlink("/recur", "Regelmäßig")
+}
+
+templ navbar() {
+ /* different inline svgs */
+ <svg xmlns="http://www.w3.org/2000/svg" class="d-none">
+ <symbol id="search" viewBox="0 0 16 16">
+ <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"></path>
+ </symbol>
+ </svg>
+
+ /* The Navbar */
+ <nav class="navbar sticky-top bg-dark-subtle navbar-expand-lg shadow mb-2">
+ <div class="container-fluid" >
+ <a class="navbar-brand" href="/">
+ <img src="/static/euro-money.svg" alt="Logo" width="32" height="32" class="d-inline-block"/> Kosten
+ </a>
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
+ <span class="navbar-toggler-icon"></span>
+ </button>
+ <div class="collapse navbar-collapse" id="navbarNav">
+ <ul class="navbar-nav nav-underline me-auto">
+ @navlinks()
+ </ul>
+ <form class="d-flex" role="search">
+ <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
+ <button class="btn btn-outline-dark" type="submit"><svg class="svg"><use xlink:href="#search"></use></svg></button>
+ </form>
+ </div>
+ </div>
+ </nav>
+}
+
+templ content() {
+ @base() {
+ @navbar()
+ <main class="container-lg">
+ {children...}
+ </main>
+ }
+} \ No newline at end of file
diff --git a/pages/base_templ.go b/pages/base_templ.go
new file mode 100644
index 0000000..112f76e
--- /dev/null
+++ b/pages/base_templ.go
@@ -0,0 +1,264 @@
+// Code generated by templ - DO NOT EDIT.
+
+// templ: version: v0.2.778
+package pages
+
+//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 baseWithHeader(header templ.Component) 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 = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"de\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><link rel=\"icon\" type=\"image/svg+xml\" href=\"/static/favicon.svg\" sizes=\"16x16 32x32 48x48\"><title>Kosten</title><link href=\"/static/custom.css\" rel=\"stylesheet\"><link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH\" crossorigin=\"anonymous\"><script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js\" integrity=\"sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz\" crossorigin=\"anonymous\"></script>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if header != nil {
+ templ_7745c5c3_Err = header.Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</head><body>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</body></html>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+func base() templ.Component {
+ return baseWithHeader(nil)
+}
+
+func navlink(path, descr 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_Var2 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var2 == nil {
+ templ_7745c5c3_Var2 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"nav-item\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var3 = []any{"nav-link", templ.KV("active", isCurrPath(ctx, path))}
+ templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<a class=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var4 string
+ templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var3).String())
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/base.templ`, Line: 1, Col: 0}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" href=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var5 templ.SafeURL = templ.URL(path)
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var5)))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var6 string
+ templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(descr)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/base.templ`, Line: 31, Col: 41}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></li>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+func navlinks() 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_Var7 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var7 == nil {
+ templ_7745c5c3_Var7 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ templ_7745c5c3_Err = navlink("/categories", "Kategorien").Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = navlink("/recur", "Regelmäßig").Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+func navbar() 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_Err = templ_7745c5c3_Buffer.WriteString("<svg xmlns=\"http://www.w3.org/2000/svg\" class=\"d-none\"><symbol id=\"search\" viewBox=\"0 0 16 16\"><path d=\"M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z\"></path></symbol></svg><nav class=\"navbar sticky-top bg-dark-subtle navbar-expand-lg shadow mb-2\"><div class=\"container-fluid\"><a class=\"navbar-brand\" href=\"/\"><img src=\"/static/euro-money.svg\" alt=\"Logo\" width=\"32\" height=\"32\" class=\"d-inline-block\"> Kosten</a> <button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\" data-bs-target=\"#navbarNav\" aria-controls=\"navbarNav\" aria-expanded=\"false\" aria-label=\"Toggle navigation\"><span class=\"navbar-toggler-icon\"></span></button><div class=\"collapse navbar-collapse\" id=\"navbarNav\"><ul class=\"navbar-nav nav-underline me-auto\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = navlinks().Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul><form class=\"d-flex\" role=\"search\"><input class=\"form-control me-2\" type=\"search\" placeholder=\"Search\" aria-label=\"Search\"> <button class=\"btn btn-outline-dark\" type=\"submit\"><svg class=\"svg\"><use xlink:href=\"#search\"></use></svg></button></form></div></div></nav>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+func content() 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_Var9 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var9 == nil {
+ templ_7745c5c3_Var9 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ templ_7745c5c3_Var10 := 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 = navbar().Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <main class=\"container-lg\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templ_7745c5c3_Var9.Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</main>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+ templ_7745c5c3_Err = base().Render(templ.WithChildren(ctx, templ_7745c5c3_Var10), templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+var _ = templruntime.GeneratedTemplate
diff --git a/pages/login.go b/pages/login.go
index 9d8f686..14b1ce1 100644
--- a/pages/login.go
+++ b/pages/login.go
@@ -56,7 +56,7 @@ type user struct {
Password string `form:"type=password;options=required"`
RememberMe bool `form:"type=checkbox;value=y;options=checked"`
Errors []error `form:"-"`
- csrf.Csrf
+ csrf.CsrfField
}
func Login() Page {
@@ -68,7 +68,7 @@ func Login() Page {
}
u := user{}
u.SetCsrfField(r)
- showLoginPage(w, u)
+ showLoginPage(r, w, u)
})
r.Post("/", handleLogin)
@@ -102,7 +102,7 @@ func handleLogin(w http.ResponseWriter, r *http.Request) {
if !ok {
u.Errors = []error{form.FieldError{Field: "Password", Issue: "Invalid"}}
- showLoginPage(w, u)
+ showLoginPage(r, w, u)
return
}
@@ -125,6 +125,6 @@ func handleLogin(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, next, http.StatusFound)
}
-func showLoginPage(w http.ResponseWriter, u user) {
- showTemplate(w, "login", u)
+func showLoginPage(r *http.Request, w http.ResponseWriter, u user) {
+ render(login(u), w, r)
}
diff --git a/templ/login.tpl b/pages/login.templ
index 7a9a049..fed611e 100644
--- a/templ/login.tpl
+++ b/pages/login.templ
@@ -1,10 +1,18 @@
-{{define "body"}}
+package pages
+
+import (
+ "gosten/form"
+)
+
+templ login(u user) {
+ @base() {
<main class="d-flex align-items-center min-vh-100">
<form action="/login" method="post" class="container mx-auto" style="max-width: 440px;">
<img src="/static/euro-money.svg" width="96" height="96" class="mb-4"/>
- {{inputs_and_errors_for . .Errors}}
- {{.CsrfField}}
+ @form.Form(u, u.Errors)
+ @u.Csrf()
<button class="btn btn-primary w-100" type="submit">Log In!</button>
</form>
</main>
-{{end}} \ No newline at end of file
+ }
+} \ No newline at end of file
diff --git a/pages/login_templ.go b/pages/login_templ.go
new file mode 100644
index 0000000..617e3b5
--- /dev/null
+++ b/pages/login_templ.go
@@ -0,0 +1,74 @@
+// Code generated by templ - DO NOT EDIT.
+
+// templ: version: v0.2.778
+package pages
+
+//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 (
+ "gosten/form"
+)
+
+func login(u 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_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 = templ_7745c5c3_Buffer.WriteString("<main class=\"d-flex align-items-center min-vh-100\"><form action=\"/login\" method=\"post\" class=\"container mx-auto\" style=\"max-width: 440px;\"><img src=\"/static/euro-money.svg\" width=\"96\" height=\"96\" class=\"mb-4\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = form.Form(u, u.Errors).Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = u.Csrf().Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<button class=\"btn btn-primary w-100\" type=\"submit\">Log In!</button></form></main>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+ templ_7745c5c3_Err = base().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+var _ = templruntime.GeneratedTemplate
diff --git a/pages/page.go b/pages/page.go
index 25c2331..6ce8cee 100644
--- a/pages/page.go
+++ b/pages/page.go
@@ -3,10 +3,10 @@ package pages
import (
"context"
"gosten/model"
- "gosten/templ"
"log"
"net/http"
+ "github.com/a-h/templ"
"github.com/go-chi/chi/v5"
)
@@ -20,39 +20,53 @@ type Page interface {
http.Handler
}
-type dataFunc func(r *http.Request, uid int32) any
+type dataFunc[T any] func(r *http.Request, uid int32) T
+type tplFunc[T any] func(T) templ.Component
-type simplePage struct {
- dataFn dataFunc
- template string
-}
-
-func (p simplePage) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- input := p.dataFn(r, userId(r))
- p.showTemplate(w, input)
-}
-
-func simpleByQuery[T any](tpl string, query func(ctx context.Context, id int32) (T, error)) Page {
- dataFn := func(r *http.Request, uid int32) any {
+func simpleByQuery[T any](tpl tplFunc[T], query func(ctx context.Context, id int32) (T, error)) Page {
+ dataFn := func(r *http.Request, uid int32) T {
d, _ := query(r.Context(), uid)
return d
}
return simple(tpl, dataFn)
}
-func simple(tpl string, dataFn dataFunc) Page {
- p := simplePage{dataFn, tpl}
+func simple[T any](tpl tplFunc[T], dataFn dataFunc[T]) Page {
r := chi.NewRouter()
- r.Get("/", p.ServeHTTP)
+ r.Get("/", func(w http.ResponseWriter, r *http.Request) {
+ input := dataFn(r, userId(r))
+ c := tpl(input)
+ render(c, w, r)
+ })
return r
}
-func showTemplate(w http.ResponseWriter, tpl string, data any) {
- if err := templ.Lookup(tpl).Execute(w, data); err != nil {
- log.Panicf("Executing '%s' with %+v: %v", tpl, data, err)
+type ctxPath struct{}
+
+func render(c templ.Component, w http.ResponseWriter, r *http.Request) {
+ ctx := context.WithValue(r.Context(), ctxPath{}, r.URL.Path)
+ if err := c.Render(ctx, w); err != nil {
+ log.Panic(err.Error())
}
}
-func (p simplePage) showTemplate(w http.ResponseWriter, data any) {
- showTemplate(w, p.template, data)
+func isCurrPath(ctx context.Context, path string) bool {
+ currPath := ctx.Value(ctxPath{}).(string)
+ if path[0] != '/' {
+ path = "/" + path
+ }
+
+ if currPath == "/" {
+ return path == "/"
+ }
+
+ if currPath[len(currPath)-1] == '/' {
+ currPath = currPath[:len(currPath)-1]
+ }
+
+ if path[len(path)-1] == '/' {
+ path = path[:len(path)-1]
+ }
+
+ return currPath == path
}
diff --git a/pages/pages.go b/pages/pages.go
index e965bdd..eb7a3f6 100644
--- a/pages/pages.go
+++ b/pages/pages.go
@@ -5,22 +5,22 @@ import (
)
func Init() Page {
- return simple("index", func(r *http.Request, uid int32) any {
+ return simple(index, func(r *http.Request, uid int32) string {
u, _ := Q.GetUserById(r.Context(), uid)
return u.Name
})
}
func Recur() Page {
- return simpleByQuery("recur", Q.GetRecurExpenses)
+ return simpleByQuery(recur, Q.GetRecurExpenses)
}
func Categories() Page {
- return simpleByQuery("categories", Q.GetCategoriesOrdered)
+ return simpleByQuery(categories, Q.GetCategoriesOrdered)
}
func NotFound() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
- showTemplate(w, "404", r.RequestURI)
+ render(notfound(r.RequestURI), w, r)
}
}
diff --git a/pages/pages.templ b/pages/pages.templ
new file mode 100644
index 0000000..b9b7489
--- /dev/null
+++ b/pages/pages.templ
@@ -0,0 +1,46 @@
+package pages
+
+import "gosten/model"
+
+templ notfound(uri string) {
+ @content() {
+ <div class="alert alert-danger d-flex align-items-center" role="alert">
+ <svg class="me-2" role="img" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
+ <path d="M9.05.435c-.58-.58-1.52-.58-2.1 0L.436 6.95c-.58.58-.58 1.519 0 2.098l6.516 6.516c.58.58 1.519.58 2.098 0l6.516-6.516c.58-.58.58-1.519 0-2.098zM8 4c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995A.905.905 0 0 1 8 4m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
+ </svg>
+ <div>
+ Seite <span class="fst-italic">{uri}</span> nicht gefunden!
+ </div>
+ </div>
+ }
+}
+
+templ index(user string) {
+ @content() {
+ Logged in with user: {user}
+ }
+}
+
+templ recur(rows []model.GetRecurExpensesRow) {
+ @content() {
+ <ul class="list-group">
+ for _, r := range rows {
+ <li class="list-group-item">
+ {r.Description.String}
+ </li>
+ }
+ </ul>
+ }
+}
+
+templ categories(rows []model.GetCategoriesOrderedRow) {
+ @content() {
+ <ul class="list-group">
+ for _, r := range rows {
+ <li class="list-group-item">
+ {r.Name}
+ </li>
+ }
+ </ul>
+ }
+} \ No newline at end of file
diff --git a/pages/pages_templ.go b/pages/pages_templ.go
new file mode 100644
index 0000000..263d903
--- /dev/null
+++ b/pages/pages_templ.go
@@ -0,0 +1,269 @@
+// Code generated by templ - DO NOT EDIT.
+
+// templ: version: v0.2.778
+package pages
+
+//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 "gosten/model"
+
+func notfound(uri 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_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 = templ_7745c5c3_Buffer.WriteString("<div class=\"alert alert-danger d-flex align-items-center\" role=\"alert\"><svg class=\"me-2\" role=\"img\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" viewBox=\"0 0 16 16\"><path d=\"M9.05.435c-.58-.58-1.52-.58-2.1 0L.436 6.95c-.58.58-.58 1.519 0 2.098l6.516 6.516c.58.58 1.519.58 2.098 0l6.516-6.516c.58-.58.58-1.519 0-2.098zM8 4c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995A.905.905 0 0 1 8 4m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2\"></path></svg><div>Seite <span class=\"fst-italic\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var3 string
+ templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(uri)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/pages.templ`, Line: 12, Col: 47}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span> nicht gefunden!</div></div>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+ templ_7745c5c3_Err = content().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+func index(user 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_Var4 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var4 == nil {
+ templ_7745c5c3_Var4 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ templ_7745c5c3_Var5 := 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 = templ_7745c5c3_Buffer.WriteString("Logged in with user: ")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var6 string
+ templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(user)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/pages.templ`, Line: 20, Col: 30}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+ templ_7745c5c3_Err = content().Render(templ.WithChildren(ctx, templ_7745c5c3_Var5), templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+func recur(rows []model.GetRecurExpensesRow) 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_Var7 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var7 == nil {
+ templ_7745c5c3_Var7 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ templ_7745c5c3_Var8 := 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 = templ_7745c5c3_Buffer.WriteString("<ul class=\"list-group\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ for _, r := range rows {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"list-group-item\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var9 string
+ templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(r.Description.String)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/pages.templ`, Line: 29, Col: 29}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</li>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+ templ_7745c5c3_Err = content().Render(templ.WithChildren(ctx, templ_7745c5c3_Var8), templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+func categories(rows []model.GetCategoriesOrderedRow) 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)
+ templ_7745c5c3_Var11 := 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 = templ_7745c5c3_Buffer.WriteString("<ul class=\"list-group\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ for _, r := range rows {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"list-group-item\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var12 string
+ templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name)
+ if templ_7745c5c3_Err != nil {
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/pages.templ`, Line: 41, Col: 15}
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</li>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+ templ_7745c5c3_Err = content().Render(templ.WithChildren(ctx, templ_7745c5c3_Var11), templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+var _ = templruntime.GeneratedTemplate
diff --git a/templ/404.tpl b/templ/404.tpl
deleted file mode 100644
index b0a3658..0000000
--- a/templ/404.tpl
+++ /dev/null
@@ -1,10 +0,0 @@
-{{define "main"}}
- <div class="alert alert-danger d-flex align-items-center" role="alert">
- <svg class="me-2" role="img" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
- <path d="M9.05.435c-.58-.58-1.52-.58-2.1 0L.436 6.95c-.58.58-.58 1.519 0 2.098l6.516 6.516c.58.58 1.519.58 2.098 0l6.516-6.516c.58-.58.58-1.519 0-2.098zM8 4c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995A.905.905 0 0 1 8 4m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
- </svg>
- <div>
- Seite <span class="fst-italic">{{.}}</span> nicht gefunden!
- </div>
- </div>
-{{end}} \ No newline at end of file
diff --git a/templ/base.tpl b/templ/base.tpl
deleted file mode 100644
index 7e42630..0000000
--- a/templ/base.tpl
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html lang="de">
-<head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link rel="icon" type="image/svg+xml" href="/static/favicon.svg" sizes="16x16 32x32 48x48">
- <title>{{block "title" .}}Kosten{{end}}</title>
- <link href="/static/custom.css" rel="stylesheet">
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
- {{block "head" .}}{{end}}
-</head>
-<body>
-{{block "body" .}}
- Dummy Text
-{{end}}
-{{block "js" .}}
-{{end}}
-</body>
-</html> \ No newline at end of file
diff --git a/templ/categories.tpl b/templ/categories.tpl
deleted file mode 100644
index 662e1e1..0000000
--- a/templ/categories.tpl
+++ /dev/null
@@ -1,9 +0,0 @@
-{{define "main"}}
-<ul class="list-group">
- {{range .}}
- <li class="list-group-item">
- {{.Name}}
- </li>
- {{end}}
-</ul>
-{{end}} \ No newline at end of file
diff --git a/templ/content.tpl b/templ/content.tpl
deleted file mode 100644
index 47a7909..0000000
--- a/templ/content.tpl
+++ /dev/null
@@ -1,36 +0,0 @@
-{{define "body"}}
- {{/* different inline svgs */}}
- <svg xmlns="http://www.w3.org/2000/svg" class="d-none">
- <symbol id="search" viewBox="0 0 16 16">
- <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"></path>
- </symbol>
- </svg>
-
- {{/* The Navbar */}}
- <nav class="navbar sticky-top bg-dark-subtle navbar-expand-lg shadow mb-2">
- <div class="container-fluid" >
- <a class="navbar-brand" href="/">
- <img src="/static/euro-money.svg" alt="Logo" width="32" height="32" class="d-inline-block"/> Kosten
- </a>
- <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
- <span class="navbar-toggler-icon"></span>
- </button>
- <div class="collapse navbar-collapse" id="navbarNav">
- <ul class="navbar-nav nav-underline me-auto">
- {{template "navlinks"}}
- </ul>
- <form class="d-flex" role="search">
- <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
- <button class="btn btn-outline-dark" type="submit"><svg class="svg"><use xlink:href="#search"></use></svg></button>
- </form>
- </div>
- </div>
- </nav>
-
- {{/* The Main Content */}}
- <main class="container-lg">
- {{block "main" .}}
- Dummy Content
- {{end}}
- </main>
-{{end}} \ No newline at end of file
diff --git a/templ/form.tpl b/templ/form.tpl
deleted file mode 100644
index 4d20557..0000000
--- a/templ/form.tpl
+++ /dev/null
@@ -1,22 +0,0 @@
-{{define "formItem"}}
- {{- $cb := eq .Type "checkbox" -}}
- <div class="{{if $cb}}form-check form-switch{{else}}form-floating{{end}} mb-3">
- <input
- {{with .ID}}id="{{.}}"{{end}}
- type="{{.Type}}"
- name="{{.Name}}"
- placeholder="{{.Placeholder}}"
- {{with .Value}}value="{{.}}"{{end}}
- {{with .Class}}class="{{.}}"{{end}}
- class="{{if $cb}}form-check-input{{else}}form-control{{end}}"
- {{range .Options}} {{.}} {{end}}
- >
- <label {{with .ID}}for="{{.}}"{{end}}
- {{- if $cb}}class="form-check-label"{{end}}>{{.Label}}</label>
-
- {{range errors}}
- <p style="color:red">{{.}}</p>
- {{end}}
- {{with .Footer}}<p>{{.}}</p>{{end}}
- </div>
-{{end}} \ No newline at end of file
diff --git a/templ/index.tpl b/templ/index.tpl
deleted file mode 100644
index 76016bb..0000000
--- a/templ/index.tpl
+++ /dev/null
@@ -1,3 +0,0 @@
-{{define "main"}}
- Logged in with user: {{.}}
-{{end}} \ No newline at end of file
diff --git a/templ/navlinks.tpl b/templ/navlinks.tpl
deleted file mode 100644
index cdd54d7..0000000
--- a/templ/navlinks.tpl
+++ /dev/null
@@ -1,8 +0,0 @@
-{{define "navlinks"}}
- <li class="nav-item">
- <a class="nav-link" href="/categories">Kategorien</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="/recur">Regelmäßig</a>
- </li>
-{{end}} \ No newline at end of file
diff --git a/templ/recur.tpl b/templ/recur.tpl
deleted file mode 100644
index 316b768..0000000
--- a/templ/recur.tpl
+++ /dev/null
@@ -1,9 +0,0 @@
-{{define "main"}}
-<ul class="list-group">
- {{range .}}
- <li class="list-group-item">
- {{.Description.String}}
- </li>
- {{end}}
-</ul>
-{{end}} \ No newline at end of file
diff --git a/templ/template.go b/templ/template.go
deleted file mode 100644
index 9cefadc..0000000
--- a/templ/template.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package templ
-
-import (
- "embed"
- "html/template"
- "io/fs"
- "os"
- "sync"
-
- "gosten/form"
-)
-
-//go:embed *.tpl
-var fsEmbed embed.FS
-
-var templates = make(map[string]*template.Template)
-var muTpl sync.RWMutex
-
-var baseTpl *template.Template
-
-var isLive = sync.OnceValue(checkLive)
-
-func init() {
- loadBase(fsEmbed)
-}
-
-func checkLive() bool {
- return os.Getenv("GOSTEN_LIVE") != ""
-}
-
-func loadBase(fs fs.FS) {
- baseTpl = template.Must(template.New("base.tpl").
- Funcs(form.ParsingFuncMap()).
- ParseFS(fs, "base.tpl", "form.tpl", "navlinks.tpl", "content.tpl"))
- baseTpl.Funcs(form.FuncMap(baseTpl.Lookup("formItem")))
-}
-
-func Lookup(name string) *template.Template {
- if isLive() {
- fs := os.DirFS("templ/")
- loadBase(fs)
- return getTemplate(name, fs)
- }
-
- muTpl.RLock()
- tpl := templates[name]
- muTpl.RUnlock()
-
- if tpl == nil {
- return parse(name)
- }
- return tpl
-}
-
-func parse(name string) *template.Template {
- muTpl.Lock()
- defer muTpl.Unlock()
-
- if tpl := templates[name]; tpl != nil {
- // might've been created by another goroutine
- return tpl
- }
-
- t := getTemplate(name, fsEmbed)
- templates[name] = t
- return t
-}
-
-func getTemplate(name string, fs fs.FS) *template.Template {
- b := template.Must(baseTpl.Clone())
- t := template.Must(b.ParseFS(fs, name+".tpl"))
- return t
-}