package todos import ( "context" "fmt" "strconv" "strings" . "ersteller-lib" "git.gorlug.de/code/golang/ersteller-lib/starter/ent" "git.gorlug.de/code/golang/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))) }