From 3be64bd10d65ae63cce38666318afccd4e52fff5 Mon Sep 17 00:00:00 2001 From: Simon Martens Date: Tue, 18 Feb 2025 18:39:30 +0100 Subject: [PATCH] Some changes, path specific data --- helpers/functions/templates.go | 19 ++++++ pagemodels/default.go | 10 +-- pagemodels/page.go | 10 +-- pages/404.go | 29 +++------ pages/almanach.go | 22 +++---- pages/person.go | 22 ++----- pages/personen.go | 18 ++---- pages/reihe.go | 14 +---- pages/reihen.go | 31 ++++----- templating/engine.go | 73 +++++++++++++++++++++ views/assets/style.css | 2 +- views/layouts/components/_footer.gohtml | 2 +- views/layouts/components/_menu.gohtml | 80 +++++++++++++++++++----- views/layouts/default/root.gohtml | 6 +- views/routes/components/_alphabet.gohtml | 13 ++++ views/routes/errors/500/body.gohtml | 4 ++ views/routes/personen/body.gohtml | 6 +- views/routes/reihen/body.gohtml | 8 +-- views/transform/site.css | 15 +++-- 19 files changed, 237 insertions(+), 147 deletions(-) create mode 100644 views/routes/components/_alphabet.gohtml create mode 100644 views/routes/errors/500/body.gohtml diff --git a/helpers/functions/templates.go b/helpers/functions/templates.go index d735aa8..5dc2cfd 100644 --- a/helpers/functions/templates.go +++ b/helpers/functions/templates.go @@ -1,5 +1,24 @@ package functions +import "fmt" + func Arr(els ...any) []any { return els } + +func Dict(values ...interface{}) (map[string]interface{}, error) { + // Must have even number of args: key, value, key, value, ... + if len(values)%2 != 0 { + return nil, fmt.Errorf("invalid dict call: must have even number of args") + } + + m := make(map[string]interface{}, len(values)/2) + for i := 0; i < len(values); i += 2 { + key, ok := values[i].(string) + if !ok { + return nil, fmt.Errorf("dict keys must be strings") + } + m[key] = values[i+1] + } + return m, nil +} diff --git a/pagemodels/default.go b/pagemodels/default.go index b3a3892..5a0f615 100644 --- a/pagemodels/default.go +++ b/pagemodels/default.go @@ -1,9 +1,6 @@ package pagemodels import ( - "net/http" - "strings" - "github.com/Theodor-Springmann-Stiftung/musenalm/templating" "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/tools/router" @@ -74,12 +71,7 @@ func (p *DefaultPage) Setup(router *router.Router[*core.RequestEvent], app core. data["keywords"] = p.Keywords() data["text"] = p.Text() - var builder strings.Builder - err := engine.Render(&builder, p.Template, data, p.Layout) - if err != nil { - return e.HTML(http.StatusInternalServerError, err.Error()) - } - return e.HTML(http.StatusOK, builder.String()) + return engine.Response200(e, p.Template, data, p.Layout) }) return nil } diff --git a/pagemodels/page.go b/pagemodels/page.go index ed443f6..2a9e2a1 100644 --- a/pagemodels/page.go +++ b/pagemodels/page.go @@ -1,9 +1,6 @@ package pagemodels import ( - "net/http" - "strings" - "github.com/Theodor-Springmann-Stiftung/musenalm/templating" "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/tools/router" @@ -33,12 +30,7 @@ func (p *Page) Down(app core.App) error { func (p *Page) Setup(router *router.Router[*core.RequestEvent], app core.App, engine *templating.Engine) error { router.GET(p.Name, func(e *core.RequestEvent) error { - var builder strings.Builder - err := engine.Render(&builder, p.Template, nil, p.Layout) - if err != nil { - return e.HTML(http.StatusInternalServerError, err.Error()) - } - return e.HTML(http.StatusOK, builder.String()) + return engine.Response200(e, p.Template, nil, p.Layout) }) return nil } diff --git a/pages/404.go b/pages/404.go index 3f4398e..e5b8ab3 100644 --- a/pages/404.go +++ b/pages/404.go @@ -1,9 +1,6 @@ package pages import ( - "net/http" - "strings" - "github.com/Theodor-Springmann-Stiftung/musenalm/app" "github.com/Theodor-Springmann-Stiftung/musenalm/pagemodels" "github.com/Theodor-Springmann-Stiftung/musenalm/templating" @@ -12,9 +9,10 @@ import ( ) const URL_ERROR_404 = "/errors/404/" +const URL_ERROR_500 = "/errors/500/" func init() { - rp := &Error404Page{ + rp := &ErrorPage{ Page: pagemodels.Page{ Name: URL_ERROR_404, }, @@ -22,27 +20,20 @@ func init() { app.Register(rp) } -type Error404Page struct { +type ErrorPage struct { pagemodels.Page } -func (p *Error404Page) Setup(router *router.Router[*core.RequestEvent], app core.App, engine *templating.Engine) error { +func (p *ErrorPage) Setup(router *router.Router[*core.RequestEvent], app core.App, engine *templating.Engine) error { router.GET(URL_ERROR_404, func(e *core.RequestEvent) error { - return Error404(e, engine, nil) + return engine.Response404(e, nil, nil) + }) + router.GET(URL_ERROR_500, func(e *core.RequestEvent) error { + return engine.Response500(e, nil, nil) }) return nil } -func Error404(e *core.RequestEvent, engine *templating.Engine, err error) error { - data := make(map[string]interface{}) - var sb strings.Builder - if err != nil { - e.App.Logger().Error("404 error fetching URL!", "error", err, "request", e.Request.URL) - data["Error"] = err.Error() - } - err = engine.Render(&sb, URL_ERROR_404, data, "default") - if err != nil { - return e.String(http.StatusInternalServerError, err.Error()) - } - return e.HTML(http.StatusNotFound, sb.String()) +func Error404(e *core.RequestEvent, engine *templating.Engine, err error, data map[string]interface{}) error { + return engine.Response404(e, err, data) } diff --git a/pages/almanach.go b/pages/almanach.go index fcce372..be3df54 100644 --- a/pages/almanach.go +++ b/pages/almanach.go @@ -1,9 +1,6 @@ package pages import ( - "net/http" - "strings" - "github.com/Theodor-Springmann-Stiftung/musenalm/app" "github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels" "github.com/Theodor-Springmann-Stiftung/musenalm/pagemodels" @@ -36,13 +33,13 @@ func (p *AlmanachPage) Setup(router *router.Router[*core.RequestEvent], app core data := make(map[string]interface{}) entry, err := dbmodels.EntryForMusenalmID(app, id) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["entry"] = entry series, srelations, _, err := dbmodels.SeriesForEntries(app, []*dbmodels.Entry{entry}) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } s := map[string]*dbmodels.Series{} @@ -55,26 +52,26 @@ func (p *AlmanachPage) Setup(router *router.Router[*core.RequestEvent], app core places, err := dbmodels.PlacesForEntry(app, entry) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["places"] = places contents, err := dbmodels.ContentsForEntry(app, entry) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["contents"] = contents agents, arelations, err := dbmodels.AgentsForEntries(app, []*dbmodels.Entry{entry}) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["arelations"] = arelations if len(contents) > 0 { cagents, crelations, err := dbmodels.AgentsForContents(app, contents) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["crelations"] = crelations for k, v := range cagents { @@ -90,10 +87,5 @@ func (p *AlmanachPage) Setup(router *router.Router[*core.RequestEvent], app core } func (p *AlmanachPage) Get(request *core.RequestEvent, engine *templating.Engine, data map[string]interface{}) error { - var builder strings.Builder - err := engine.Render(&builder, TEMPLATE_ALMANACH, data) - if err != nil { - return Error404(request, engine, err) - } - return request.HTML(http.StatusOK, builder.String()) + return engine.Response200(request, TEMPLATE_ALMANACH, data) } diff --git a/pages/person.go b/pages/person.go index 42cdd36..e051cc0 100644 --- a/pages/person.go +++ b/pages/person.go @@ -1,8 +1,6 @@ package pages import ( - "strings" - "github.com/Theodor-Springmann-Stiftung/musenalm/app" "github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels" "github.com/Theodor-Springmann-Stiftung/musenalm/pagemodels" @@ -37,13 +35,13 @@ func (p *PersonPage) Setup(router *router.Router[*core.RequestEvent], app core.A agent, err := dbmodels.AgentForId(app, person) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["a"] = agent series, relations, entries, err := dbmodels.SeriesForAgent(app, person) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } dbmodels.SortSeriessesByTitle(series) @@ -53,19 +51,19 @@ func (p *PersonPage) Setup(router *router.Router[*core.RequestEvent], app core.A contents, err := dbmodels.ContentsForAgent(app, person) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } agents, crelations, err := dbmodels.AgentsForContents(app, contents) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["agents"] = agents data["crelations"] = crelations centries, err := dbmodels.EntriesForContents(app, contents) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["centries"] = centries @@ -79,13 +77,5 @@ func (p *PersonPage) Setup(router *router.Router[*core.RequestEvent], app core.A } func (p *PersonPage) Get(request *core.RequestEvent, engine *templating.Engine, data map[string]interface{}) error { - var builder strings.Builder - err := engine.Render(&builder, TEMPLATE_PERSON, data) - if err != nil { - return Error404(request, engine, err) - } - - request.Response.Header().Set("Content-Type", "text/html") - request.Response.Write([]byte(builder.String())) - return nil + return engine.Response200(request, TEMPLATE_PERSON, data) } diff --git a/pages/personen.go b/pages/personen.go index 71204ae..941f515 100644 --- a/pages/personen.go +++ b/pages/personen.go @@ -1,9 +1,6 @@ package pages import ( - "net/http" - "strings" - "github.com/Theodor-Springmann-Stiftung/musenalm/app" "github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels" "github.com/Theodor-Springmann-Stiftung/musenalm/pagemodels" @@ -90,7 +87,7 @@ func (p *PersonenPage) FilterRequest(app core.App, engine *templating.Engine, e } if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } dbmodels.SortAgentsByName(agents) data["agents"] = agents @@ -106,7 +103,7 @@ func (p *PersonenPage) SearchRequest(app core.App, engine *templating.Engine, e agents, altagents, err := dbmodels.BasicSearchAgents(app, search) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } dbmodels.SortAgentsByName(agents) @@ -129,7 +126,7 @@ func (p *PersonenPage) LetterRequest(app core.App, engine *templating.Engine, e agents, err := dbmodels.AgentsForLetter(app, letter) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } dbmodels.SortAgentsByName(agents) data["agents"] = agents @@ -140,13 +137,8 @@ func (p *PersonenPage) LetterRequest(app core.App, engine *templating.Engine, e func (p *PersonenPage) Get(request *core.RequestEvent, engine *templating.Engine, data map[string]interface{}) error { err := p.CommonData(request.App, data) if err != nil { - return Error404(request, engine, err) + return Error404(request, engine, err, data) } - var builder strings.Builder - err = engine.Render(&builder, URL_PERSONEN, data) - if err != nil { - return Error404(request, engine, err) - } - return request.HTML(http.StatusOK, builder.String()) + return engine.Response200(request, URL_PERSONEN, data) } diff --git a/pages/reihe.go b/pages/reihe.go index 7ebbb33..75a70f0 100644 --- a/pages/reihe.go +++ b/pages/reihe.go @@ -1,9 +1,6 @@ package pages import ( - "net/http" - "strings" - "github.com/Theodor-Springmann-Stiftung/musenalm/app" "github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels" "github.com/Theodor-Springmann-Stiftung/musenalm/pagemodels" @@ -36,13 +33,13 @@ func (p *ReihePage) Setup(router *router.Router[*core.RequestEvent], app core.Ap data := make(map[string]interface{}) reihe, err := dbmodels.SeriesForId(app, id) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["series"] = reihe rmap, emap, err := dbmodels.EntriesForSeriesses(app, []*dbmodels.Series{reihe}) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["relations"] = rmap @@ -55,10 +52,5 @@ func (p *ReihePage) Setup(router *router.Router[*core.RequestEvent], app core.Ap } func (p *ReihePage) Get(request *core.RequestEvent, engine *templating.Engine, data map[string]interface{}) error { - var builder strings.Builder - err := engine.Render(&builder, TEMPLATE_REIHE, data) - if err != nil { - return Error404(request, engine, err) - } - return request.HTML(http.StatusOK, builder.String()) + return engine.Response200(request, TEMPLATE_REIHE, data) } diff --git a/pages/reihen.go b/pages/reihen.go index a0f2ecf..400c3ac 100644 --- a/pages/reihen.go +++ b/pages/reihen.go @@ -1,9 +1,7 @@ package pages import ( - "net/http" "strconv" - "strings" "github.com/Theodor-Springmann-Stiftung/musenalm/app" "github.com/Theodor-Springmann-Stiftung/musenalm/dbmodels" @@ -67,12 +65,12 @@ func (p *ReihenPage) YearRequest(app core.App, engine *templating.Engine, e *cor y, err := strconv.Atoi(year) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } series, relations, entries, err := dbmodels.SeriesForYear(app, y) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["entries"] = entries data["relations"] = relations @@ -91,7 +89,7 @@ func (p *ReihenPage) LetterRequest(app core.App, engine *templating.Engine, e *c series, err := dbmodels.SeriesForLetter(app, letter) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } // INFO: We sort again since the query can't sort german umlauts correctly dbmodels.SortSeriessesByTitle(series) @@ -99,7 +97,7 @@ func (p *ReihenPage) LetterRequest(app core.App, engine *templating.Engine, e *c rmap, bmap, err := dbmodels.EntriesForSeriesses(app, series) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["entries"] = bmap data["relations"] = rmap @@ -114,13 +112,13 @@ func (p *ReihenPage) PersonRequest(app core.App, engine *templating.Engine, e *c agent, err := dbmodels.AgentForId(app, person) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["a"] = agent series, relations, entries, err := dbmodels.SeriesForAgent(app, person) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } dbmodels.SortSeriessesByTitle(series) data["series"] = series @@ -137,13 +135,13 @@ func (p *ReihenPage) PlaceRequest(app core.App, engine *templating.Engine, e *co pl, err := dbmodels.PlaceForId(app, place) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["p"] = pl series, relations, entries, err := dbmodels.SeriesForPlace(app, place) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["series"] = series data["relations"] = relations @@ -158,7 +156,7 @@ func (p *ReihenPage) SearchRequest(app core.App, engine *templating.Engine, e *c data[PARAM_SEARCH] = search series, altseries, err := dbmodels.BasicSearchSeries(app, search) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } dbmodels.SortSeriessesByTitle(series) dbmodels.SortSeriessesByTitle(altseries) @@ -167,7 +165,7 @@ func (p *ReihenPage) SearchRequest(app core.App, engine *templating.Engine, e *c rmap, bmap, err := dbmodels.EntriesForSeriesses(app, series) if err != nil { - return Error404(e, engine, err) + return Error404(e, engine, err, data) } data["entries"] = bmap data["relations"] = rmap @@ -207,13 +205,8 @@ func (p *ReihenPage) CommonData(app core.App, data map[string]interface{}) error func (p *ReihenPage) Get(request *core.RequestEvent, engine *templating.Engine, data map[string]interface{}) error { err := p.CommonData(request.App, data) if err != nil { - return Error404(request, engine, err) + return Error404(request, engine, err, data) } - var builder strings.Builder - err = engine.Render(&builder, URL_REIHEN, data) - if err != nil { - return err - } - return request.HTML(http.StatusOK, builder.String()) + return engine.Response200(request, URL_REIHEN, data) } diff --git a/templating/engine.go b/templating/engine.go index ab13679..fa1c927 100644 --- a/templating/engine.go +++ b/templating/engine.go @@ -1,12 +1,16 @@ package templating import ( + "errors" "html/template" "io" "io/fs" + "net/http" + "strings" "sync" "github.com/Theodor-Springmann-Stiftung/musenalm/helpers/functions" + "github.com/pocketbase/pocketbase/core" ) const ( @@ -45,6 +49,8 @@ func (e *Engine) funcs() error { e.mu.Unlock() e.AddFunc("Safe", functions.Safe) e.AddFunc("Arr", functions.Arr) + e.AddFunc("HasPrefix", strings.HasPrefix) + e.AddFunc("Dict", functions.Dict) return nil } @@ -150,3 +156,70 @@ func (e *Engine) Render(out io.Writer, path string, ld map[string]interface{}, l return nil } + +func (e *Engine) Response404(request *core.RequestEvent, err error, data map[string]interface{}) error { + if data == nil { + data = make(map[string]interface{}) + } + + var sb strings.Builder + if err != nil { + request.App.Logger().Error("404 error fetching URL!", "error", err, "request", request.Request.URL) + data["Error"] = err.Error() + } + + data["page"] = requestData(request) + + err2 := e.Render(&sb, "/errors/404/", data) + if err != nil { + return e.Response500(request, errors.Join(err, err2), data) + } + + return request.HTML(http.StatusNotFound, sb.String()) +} + +func (e *Engine) Response500(request *core.RequestEvent, err error, data map[string]interface{}) error { + if data == nil { + data = make(map[string]interface{}) + } + + var sb strings.Builder + if err != nil { + request.App.Logger().Error("500 error fetching URL!", "error", err, "request", request.Request.URL) + data["Error"] = err.Error() + } + + data["page"] = requestData(request) + + err2 := e.Render(&sb, "/errors/500/", data) + if err != nil { + return request.String(http.StatusInternalServerError, errors.Join(err, err2).Error()) + } + + return request.HTML(http.StatusInternalServerError, sb.String()) +} + +func (e *Engine) Response200(request *core.RequestEvent, path string, ld map[string]interface{}, layout ...string) error { + if ld == nil { + ld = make(map[string]interface{}) + } + + ld["page"] = requestData(request) + + var builder strings.Builder + err := e.Render(&builder, path, ld, layout...) + if err != nil { + return e.Response500(request, err, ld) + } + + return request.HTML(http.StatusOK, builder.String()) +} + +func requestData(request *core.RequestEvent) map[string]interface{} { + data := make(map[string]interface{}) + data["Path"] = request.Request.URL.Path + data["Query"] = request.Request.URL.Query() + data["Method"] = request.Request.Method + data["Host"] = request.Request.Host + return data +} diff --git a/views/assets/style.css b/views/assets/style.css index 16c91c7..37f7fac 100644 --- a/views/assets/style.css +++ b/views/assets/style.css @@ -1 +1 @@ -/*! tailwindcss v4.0.5 | MIT License | https://tailwindcss.com */@layer theme{:root,:host{--font-sans:"Source Sans 3","Merriweather Sans",ui-sans-serif;--font-serif:"Merriweather",ui-serif;--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-slate-50:oklch(.984 .003 247.858);--color-slate-200:oklch(.929 .013 255.508);--color-slate-600:oklch(.446 .043 257.281);--color-slate-700:oklch(.372 .044 257.287);--color-slate-800:oklch(.279 .041 260.031);--color-slate-900:oklch(.208 .042 265.755);--color-gray-200:oklch(.928 .006 264.531);--color-zinc-200:oklch(.92 .004 286.32);--color-zinc-300:oklch(.871 .006 286.286);--spacing:.25rem;--breakpoint-xl:80rem;--font-weight-bold:700;--ease-in:cubic-bezier(.4,0,1,1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-font-feature-settings:var(--font-sans--font-feature-settings);--default-font-variation-settings:var(--font-sans--font-variation-settings);--default-mono-font-family:var(--font-mono);--default-mono-font-feature-settings:var(--font-mono--font-feature-settings);--default-mono-font-variation-settings:var(--font-mono--font-variation-settings)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1;color:color-mix(in oklab,currentColor 50%,transparent)}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*,:after,:before,::backdrop{border-color:var(--color-gray-200,currentColor)}::file-selector-button{border-color:var(--color-gray-200,currentColor)}}@layer components{html{font-size:15.5px}body{background-color:var(--color-slate-50)}h1,h2,h3,h4{font-family:var(--font-serif)}a{-webkit-hyphens:none;hyphens:none;color:var(--color-slate-700);text-decoration-line:underline;text-decoration-style:dotted}@media (hover:hover){a:hover{color:var(--color-slate-900);text-decoration-style:solid}}ul{margin-block:calc(var(--spacing)*2)}li{margin-left:calc(var(--spacing)*14);list-style-type:disc}nav>*{border-bottom-style:var(--tw-border-style);border-color:#0000;border-bottom-width:4px}@media (hover:hover){nav>:hover{border-color:var(--color-zinc-200)!important}}nav a[aria-current=page]{color:var(--color-slate-800);border-color:var(--color-zinc-300)!important}main{flex-grow:1;flex-shrink:0}}@layer utilities{.collapse{visibility:collapse}.visible{visibility:visible}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.mt-6{margin-top:calc(var(--spacing)*6)}.mt-8{margin-top:calc(var(--spacing)*8)}.mt-12{margin-top:calc(var(--spacing)*12)}.mb-1\.5{margin-bottom:calc(var(--spacing)*1.5)}.block{display:block}.contents{display:contents}.flex{display:flex}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.table{display:table}.h-1{height:calc(var(--spacing)*1)}.h-2{height:calc(var(--spacing)*2)}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-full{width:100%}.max-w-\(--breakpoint-xl\){max-width:var(--breakpoint-xl)}.translate-2{--tw-translate-x:calc(var(--spacing)*2);--tw-translate-y:calc(var(--spacing)*2);translate:var(--tw-translate-x)var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x)var(--tw-rotate-y)var(--tw-rotate-z)var(--tw-skew-x)var(--tw-skew-y)}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.justify-between{justify-content:space-between}.justify-end{justify-content:flex-end}.gap-x-6{column-gap:calc(var(--spacing)*6)}.self-end{align-self:flex-end}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-zinc-300{border-color:var(--color-zinc-300)}.bg-slate-200{background-color:var(--color-slate-200)}.px-3{padding-inline:calc(var(--spacing)*3)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.pt-3{padding-top:calc(var(--spacing)*3)}.pb-1\.5{padding-bottom:calc(var(--spacing)*1.5)}.align-bottom{vertical-align:bottom}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.font-serif{font-family:var(--font-serif)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.text-wrap{text-wrap:wrap}.text-slate-600{color:var(--color-slate-600)}.italic{font-style:italic}.overline{text-decoration-line:overline}.underline{text-decoration-line:underline}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-in{--tw-ease:var(--ease-in);transition-timing-function:var(--ease-in)}.\[a-zA-Z\:\\-\\\.\]{a-zA-Z:\-\.}@media (hover:hover){.hover\:cursor-pointer:hover{cursor:pointer}.hover\:text-slate-900:hover{color:var(--color-slate-900)}}.\[\&_td\]\:\!align-top td{vertical-align:top!important}.\[\&\>\*\]\:-mb-1\.5>*{margin-bottom:calc(var(--spacing)*-1.5)}.\[\&\>\*\]\:px-1\.5>*{padding-inline:calc(var(--spacing)*1.5)}.\[\&\>\*\]\:pt-1>*{padding-top:calc(var(--spacing)*1)}.\[\&\>a\]\:no-underline>a{text-decoration-line:none}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false;initial-value:rotateX(0)}@property --tw-rotate-y{syntax:"*";inherits:false;initial-value:rotateY(0)}@property --tw-rotate-z{syntax:"*";inherits:false;initial-value:rotateZ(0)}@property --tw-skew-x{syntax:"*";inherits:false;initial-value:skewX(0)}@property --tw-skew-y{syntax:"*";inherits:false;initial-value:skewY(0)}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false} +/*! tailwindcss v4.0.5 | MIT License | https://tailwindcss.com */@layer theme{:root,:host{--font-sans:"Source Sans 3","Merriweather Sans",ui-sans-serif;--font-serif:"Merriweather",ui-serif;--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-slate-50:oklch(.984 .003 247.858);--color-slate-100:oklch(.968 .007 247.896);--color-slate-200:oklch(.929 .013 255.508);--color-slate-600:oklch(.446 .043 257.281);--color-slate-700:oklch(.372 .044 257.287);--color-slate-800:oklch(.279 .041 260.031);--color-slate-900:oklch(.208 .042 265.755);--color-gray-200:oklch(.928 .006 264.531);--color-zinc-200:oklch(.92 .004 286.32);--color-zinc-300:oklch(.871 .006 286.286);--spacing:.25rem;--breakpoint-xl:80rem;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--font-weight-bold:700;--ease-in:cubic-bezier(.4,0,1,1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-font-feature-settings:var(--font-sans--font-feature-settings);--default-font-variation-settings:var(--font-sans--font-variation-settings);--default-mono-font-family:var(--font-mono);--default-mono-font-feature-settings:var(--font-mono--font-feature-settings);--default-mono-font-variation-settings:var(--font-mono--font-variation-settings)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}body{line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1;color:color-mix(in oklab,currentColor 50%,transparent)}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*,:after,:before,::backdrop{border-color:var(--color-gray-200,currentColor)}::file-selector-button{border-color:var(--color-gray-200,currentColor)}}@layer components{html{font-size:16.5px}body{background-color:var(--color-slate-50)}h1,h2,h3,h4{font-family:var(--font-serif)}a{-webkit-hyphens:none;hyphens:none;color:var(--color-slate-700);text-decoration-line:underline;text-decoration-style:dotted}@media (hover:hover){a:hover{color:var(--color-slate-900);text-decoration-style:solid}}ul{margin-block:calc(var(--spacing)*2)}li{margin-left:calc(var(--spacing)*14);list-style-type:disc}@media (hover:hover){nav>a:hover{border-color:var(--color-zinc-200)!important}}nav>*{border-bottom-style:var(--tw-border-style);border-color:#0000;border-bottom-width:4px}nav>button[aria-current=true]{background-color:var(--color-slate-200)!important}nav a[aria-current=page]{color:var(--color-slate-800);border-color:var(--color-zinc-300)!important}main{flex-grow:1;flex-shrink:0}}@layer utilities{.collapse{visibility:collapse}.visible{visibility:visible}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.mt-6{margin-top:calc(var(--spacing)*6)}.mt-8{margin-top:calc(var(--spacing)*8)}.mt-12{margin-top:calc(var(--spacing)*12)}.mb-1\.5{margin-bottom:calc(var(--spacing)*1.5)}.block{display:block}.contents{display:contents}.flex{display:flex}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.table{display:table}.h-1{height:calc(var(--spacing)*1)}.h-2{height:calc(var(--spacing)*2)}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-full{width:100%}.max-w-\(--breakpoint-xl\){max-width:var(--breakpoint-xl)}.translate-2{--tw-translate-x:calc(var(--spacing)*2);--tw-translate-y:calc(var(--spacing)*2);translate:var(--tw-translate-x)var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x)var(--tw-rotate-y)var(--tw-rotate-z)var(--tw-skew-x)var(--tw-skew-y)}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.justify-between{justify-content:space-between}.justify-end{justify-content:flex-end}.gap-x-4{column-gap:calc(var(--spacing)*4)}.self-end{align-self:flex-end}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-zinc-300{border-color:var(--color-zinc-300)}.bg-slate-100{background-color:var(--color-slate-100)}.px-3{padding-inline:calc(var(--spacing)*3)}.py-3\.5{padding-block:calc(var(--spacing)*3.5)}.pt-3{padding-top:calc(var(--spacing)*3)}.pt-3\.5{padding-top:calc(var(--spacing)*3.5)}.\!pr-2\.5{padding-right:calc(var(--spacing)*2.5)!important}.pr-2\.5{padding-right:calc(var(--spacing)*2.5)}.pb-1\.5{padding-bottom:calc(var(--spacing)*1.5)}.align-bottom{vertical-align:bottom}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.font-serif{font-family:var(--font-serif)}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.text-wrap{text-wrap:wrap}.text-slate-600{color:var(--color-slate-600)}.italic{font-style:italic}.overline{text-decoration-line:overline}.underline{text-decoration-line:underline}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-in{--tw-ease:var(--ease-in);transition-timing-function:var(--ease-in)}.\[a-zA-Z\:\\-\\\.\]{a-zA-Z:\-\.}@media (hover:hover){.hover\:cursor-pointer:hover{cursor:pointer}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.hover\:text-slate-900:hover{color:var(--color-slate-900)}}.\[\&_td\]\:\!align-top td{vertical-align:top!important}.\[\&\>\*\]\:-mb-1\.5>*{margin-bottom:calc(var(--spacing)*-1.5)}.\[\&\>\*\]\:px-1\.5>*{padding-inline:calc(var(--spacing)*1.5)}.\[\&\>\*\]\:pt-1>*{padding-top:calc(var(--spacing)*1)}.\[\&\>a\]\:no-underline>a{text-decoration-line:none}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false;initial-value:rotateX(0)}@property --tw-rotate-y{syntax:"*";inherits:false;initial-value:rotateY(0)}@property --tw-rotate-z{syntax:"*";inherits:false;initial-value:rotateZ(0)}@property --tw-skew-x{syntax:"*";inherits:false;initial-value:skewX(0)}@property --tw-skew-y{syntax:"*";inherits:false;initial-value:skewY(0)}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false} diff --git a/views/layouts/components/_footer.gohtml b/views/layouts/components/_footer.gohtml index cd522eb..649ad73 100644 --- a/views/layouts/components/_footer.gohtml +++ b/views/layouts/components/_footer.gohtml @@ -1 +1 @@ -
Datenschutz, Impressum.
+
Datenschutz, Impressum.
diff --git a/views/layouts/components/_menu.gohtml b/views/layouts/components/_menu.gohtml index c427e66..1bae2ea 100644 --- a/views/layouts/components/_menu.gohtml +++ b/views/layouts/components/_menu.gohtml @@ -1,3 +1,4 @@ +{{ $model := . }}
@@ -8,15 +9,37 @@