diff options
author | René 'Necoro' Neumann <necoro@necoro.eu> | 2024-10-17 16:37:23 +0200 |
---|---|---|
committer | René 'Necoro' Neumann <necoro@necoro.eu> | 2024-10-17 16:37:23 +0200 |
commit | b2447bc967df37b31282a97e32c581954bb8bcc9 (patch) | |
tree | 39758d1121fae6dc1d27e8a45035690421900d6c | |
parent | 789d21034e526a03d3e91d5d284a4888be938340 (diff) | |
download | gosten-b2447bc967df37b31282a97e32c581954bb8bcc9.tar.gz gosten-b2447bc967df37b31282a97e32c581954bb8bcc9.tar.bz2 gosten-b2447bc967df37b31282a97e32c581954bb8bcc9.zip |
Move from html/template to templ
Diffstat (limited to '')
-rw-r--r-- | csrf/csrf.go | 17 | ||||
-rw-r--r-- | form/builder.go | 96 | ||||
-rw-r--r-- | form/errors.go | 30 | ||||
-rw-r--r-- | form/field.templ | 42 | ||||
-rw-r--r-- | form/field_templ.go | 239 | ||||
-rw-r--r-- | form/form.go | 21 | ||||
-rw-r--r-- | form/reflect.go | 12 | ||||
-rw-r--r-- | go.mod | 1 | ||||
-rw-r--r-- | go.sum | 8 | ||||
-rw-r--r-- | pages/base.templ | 77 | ||||
-rw-r--r-- | pages/base_templ.go | 264 | ||||
-rw-r--r-- | pages/login.go | 10 | ||||
-rw-r--r-- | pages/login.templ (renamed from templ/login.tpl) | 16 | ||||
-rw-r--r-- | pages/login_templ.go | 74 | ||||
-rw-r--r-- | pages/page.go | 58 | ||||
-rw-r--r-- | pages/pages.go | 8 | ||||
-rw-r--r-- | pages/pages.templ | 46 | ||||
-rw-r--r-- | pages/pages_templ.go | 269 | ||||
-rw-r--r-- | templ/404.tpl | 10 | ||||
-rw-r--r-- | templ/base.tpl | 20 | ||||
-rw-r--r-- | templ/categories.tpl | 9 | ||||
-rw-r--r-- | templ/content.tpl | 36 | ||||
-rw-r--r-- | templ/form.tpl | 22 | ||||
-rw-r--r-- | templ/index.tpl | 3 | ||||
-rw-r--r-- | templ/navlinks.tpl | 8 | ||||
-rw-r--r-- | templ/recur.tpl | 9 | ||||
-rw-r--r-- | templ/template.go | 73 |
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 } @@ -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 @@ -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 -} |