Add first todos page
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
package todos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
. "ersteller-lib"
|
||||
"ersteller-lib/starter/ent"
|
||||
"ersteller-lib/starter/ent/todo"
|
||||
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
const TodosPath = "/todos"
|
||||
const TodosPathDe = "/todos"
|
||||
|
||||
// i18n texts
|
||||
var texts *Texts
|
||||
|
||||
type Texts struct {
|
||||
PageTitle I18nText
|
||||
PageDescription I18nText
|
||||
HeroTitle I18nText
|
||||
AddPlaceholder I18nText
|
||||
AddButton I18nText
|
||||
DeleteButton I18nText
|
||||
SaveButton I18nText
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
createPage CreateHtmxPageFunc
|
||||
db *ent.Client
|
||||
|
||||
ViewRoute HtmxRoute
|
||||
AddRoute HtmxRoute
|
||||
UpdateRoute HtmxRoute
|
||||
DeleteRoute HtmxRoute
|
||||
}
|
||||
|
||||
func NewPage(createPage CreateHtmxPageFunc, server HtmxServer, path *ActivePath, db *ent.Client) *Page {
|
||||
if texts == nil {
|
||||
createTexts()
|
||||
}
|
||||
p := &Page{createPage: createPage, db: db}
|
||||
p.ViewRoute = NewHtmxGetRoute(p.View, LanguagePaths{En: TodosPath, De: TodosPathDe}).SetActivePath(path)
|
||||
p.ViewRoute.Add(server)
|
||||
|
||||
p.AddRoute = NewHtmxPostRoute(p.Add, LanguagePaths{En: TodosPath + "/add", De: TodosPathDe + "/add"})
|
||||
p.AddRoute.Add(server)
|
||||
p.UpdateRoute = NewHtmxPostRoute(p.Update, LanguagePaths{En: TodosPath + "/update", De: TodosPathDe + "/update"})
|
||||
p.UpdateRoute.Add(server)
|
||||
p.DeleteRoute = NewHtmxPostRoute(p.Delete, LanguagePaths{En: TodosPath + "/delete", De: TodosPathDe + "/delete"})
|
||||
p.DeleteRoute.Add(server)
|
||||
return p
|
||||
}
|
||||
|
||||
func createTexts() {
|
||||
texts = &Texts{
|
||||
PageTitle: NewI18nText(map[Language]string{En: "Todos", De: "Todos"}),
|
||||
PageDescription: NewI18nText(map[Language]string{En: "Manage your todos", De: "Verwalte deine Todos"}),
|
||||
HeroTitle: NewI18nText(map[Language]string{En: "Todos", De: "Todos"}),
|
||||
AddPlaceholder: NewI18nText(map[Language]string{En: "New todo title", De: "Neuer Todo-Titel"}),
|
||||
AddButton: NewI18nText(map[Language]string{En: "Add", De: "Hinzufügen"}),
|
||||
DeleteButton: NewI18nText(map[Language]string{En: "Delete", De: "Löschen"}),
|
||||
SaveButton: NewI18nText(map[Language]string{En: "Save", De: "Speichern"}),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Page) getMetaData() PageWebsiteMetaData {
|
||||
return PageWebsiteMetaData{
|
||||
Title: texts.PageTitle,
|
||||
Lang: En,
|
||||
Description: texts.PageDescription,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Page) View(c HtmxContext) {
|
||||
language := c.GetLanguage()
|
||||
todos, _ := p.db.Todo.Query().Order(ent.Asc(todo.FieldCreatedAt)).All(context.Background())
|
||||
content := Group{
|
||||
Div(Class("hero-section"),
|
||||
H1(Class("hero-title"), Text(texts.HeroTitle.FromLang(language))),
|
||||
),
|
||||
Div(Class("content-section"),
|
||||
p.addForm(c),
|
||||
Div(ID("todos-list"), p.todosList(c, todos)),
|
||||
),
|
||||
}
|
||||
p.createPage(c, p.getMetaData(), content)
|
||||
}
|
||||
|
||||
func (p *Page) addForm(c HtmxContext) Node {
|
||||
lang := c.GetLanguage()
|
||||
return Form(Action(p.AddRoute.ToUrlFromContext(c, lang)), Method("post"),
|
||||
Div(Class("form-row"),
|
||||
Input(Type("text"), Name("title"), Placeholder(texts.AddPlaceholder.FromLang(lang))),
|
||||
Button(Type("submit"), Text(texts.AddButton.FromLang(lang))),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (p *Page) todosList(c HtmxContext, list []*ent.Todo) Node {
|
||||
lang := c.GetLanguage()
|
||||
items := make([]Node, 0, len(list))
|
||||
for _, t := range list {
|
||||
items = append(items, p.todoItem(c, lang, t))
|
||||
}
|
||||
return Ul(Class("todo-list"), Group(items))
|
||||
}
|
||||
|
||||
func (p *Page) todoItem(c HtmxContext, lang Language, t *ent.Todo) Node {
|
||||
// Simple inline edit form per item
|
||||
return Li(ID(p.itemDomID(t.ID)),
|
||||
Form(Action(p.UpdateRoute.ToUrlFromContext(c, lang)), Method("post"),
|
||||
Input(Type("hidden"), Name("id"), Value(fmt.Sprintf("%d", t.ID))),
|
||||
Input(Type("checkbox"), Name("completed"), If(t.Completed, Attr("checked", "checked"))),
|
||||
Input(Type("text"), Name("title"), Value(t.Title)),
|
||||
Button(Type("submit"), Text(texts.SaveButton.FromLang(lang))),
|
||||
),
|
||||
Form(Action(p.DeleteRoute.ToUrlFromContext(c, lang)), Method("post"),
|
||||
Input(Type("hidden"), Name("id"), Value(fmt.Sprintf("%d", t.ID))),
|
||||
Button(Type("submit"), Class("danger"), Text(texts.DeleteButton.FromLang(lang))),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (p *Page) itemDomID(id int) string { return fmt.Sprintf("todo-%d", id) }
|
||||
|
||||
// Handlers
|
||||
func (p *Page) Add(c HtmxContext) {
|
||||
title := strings.TrimSpace(c.GetFormValue("title"))
|
||||
if title == "" {
|
||||
p.renderList(c)
|
||||
return
|
||||
}
|
||||
_, _ = p.db.Todo.Create().SetTitle(title).Save(context.Background())
|
||||
p.renderList(c)
|
||||
}
|
||||
|
||||
func (p *Page) Update(c HtmxContext) {
|
||||
idStr := c.GetFormValue("id")
|
||||
id, _ := strconv.Atoi(idStr)
|
||||
if id == 0 {
|
||||
p.renderList(c)
|
||||
return
|
||||
}
|
||||
title := strings.TrimSpace(c.GetFormValue("title"))
|
||||
completed := c.GetFormValue("completed") == "on"
|
||||
_ = p.db.Todo.UpdateOneID(id).SetTitle(title).SetCompleted(completed).Exec(context.Background())
|
||||
p.renderList(c)
|
||||
}
|
||||
|
||||
func (p *Page) Delete(c HtmxContext) {
|
||||
idStr := c.GetFormValue("id")
|
||||
id, _ := strconv.Atoi(idStr)
|
||||
if id != 0 {
|
||||
_ = p.db.Todo.DeleteOneID(id).Exec(context.Background())
|
||||
}
|
||||
p.renderList(c)
|
||||
}
|
||||
|
||||
func (p *Page) renderList(c HtmxContext) {
|
||||
todos, _ := p.db.Todo.Query().Order(ent.Asc(todo.FieldCreatedAt)).All(context.Background())
|
||||
c.Render(Div(ID("todos-list"), p.todosList(c, todos)))
|
||||
}
|
||||
Reference in New Issue
Block a user