diff --git a/pkg/invoices.go b/pkg/invoices.go index 7d68ae7..36a4907 100644 --- a/pkg/invoices.go +++ b/pkg/invoices.go @@ -929,3 +929,56 @@ func handleInvoiceAction(w http.ResponseWriter, r *http.Request, action string, http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest) } } + +type tagsForm struct { + Slug string + Tags *TagsField +} + +func newTagsForm(slug string, locale *Locale) *tagsForm { + return &tagsForm{ + Slug: slug, + Tags: &TagsField{ + Name: "tags-" + slug, + Label: pgettext("input", "Tags", locale), + }, + } +} + +func (form *tagsForm) Parse(r *http.Request) error { + if err := r.ParseForm(); err != nil { + return err + } + form.Tags.FillValue(r) + return nil +} + +func ServeEditInvoiceTags(w http.ResponseWriter, r *http.Request, params httprouter.Params) { + conn := getConn(r) + locale := getLocale(r) + form := newTagsForm(params[0].Value, locale) + if notFoundErrorOrPanic(conn.QueryRow(r.Context(), `select array_to_string(tags, ',') from invoice where slug = $1`, form.Slug).Scan(&form.Tags)) { + http.NotFound(w, r) + return + } + mustRenderStandaloneTemplate(w, r, "tags/edit.gohtml", form) +} + +func HandleUpdateInvoiceTags(w http.ResponseWriter, r *http.Request, params httprouter.Params) { + locale := getLocale(r) + conn := getConn(r) + form := newTagsForm(params[0].Value, locale) + if err := form.Parse(r); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if err := verifyCsrfTokenValid(r); err != nil { + http.Error(w, err.Error(), http.StatusForbidden) + return + } + slug := conn.MustGetText(r.Context(), "", "update invoice set tags = $1 where slug = $2 returning slug", form.Tags, form.Slug) + if slug == "" { + http.NotFound(w, r) + } + mustRenderStandaloneTemplate(w, r, "tags/view.gohtml", form) +} diff --git a/pkg/router.go b/pkg/router.go index 39c28ff..c007c4d 100644 --- a/pkg/router.go +++ b/pkg/router.go @@ -31,6 +31,8 @@ func NewRouter(db *Db) http.Handler { companyRouter.POST("/invoices/:slug", HandleNewInvoiceAction) companyRouter.GET("/invoices/:slug/edit", ServeEditInvoice) companyRouter.POST("/invoices/:slug/edit", HandleEditInvoiceAction) + companyRouter.PUT("/invoices/:slug/tags", HandleUpdateInvoiceTags) + companyRouter.GET("/invoices/:slug/tags/edit", ServeEditInvoiceTags) companyRouter.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { mustRenderMainTemplate(w, r, "dashboard.gohtml", nil) }) diff --git a/pkg/template.go b/pkg/template.go index f234dd2..63158e5 100644 --- a/pkg/template.go +++ b/pkg/template.go @@ -57,6 +57,10 @@ func mustRenderTemplate(wr io.Writer, r *http.Request, layout string, filename s field.Attributes = append(field.Attributes, template.HTMLAttr(attr)) return field }, + "addTagsAttr": func(attr string, field *TagsField) *TagsField { + field.Attributes = append(field.Attributes, template.HTMLAttr(attr)) + return field + }, "boolToInt": func(b bool) int { if b { return 1 @@ -115,6 +119,10 @@ func mustRenderMainTemplate(w io.Writer, r *http.Request, filename string, data } } +func mustRenderStandaloneTemplate(w io.Writer, r *http.Request, filename string, data interface{}) { + mustRenderTemplate(w, r, "standalone.gohtml", filename, data) +} + func mustRenderWebTemplate(w io.Writer, r *http.Request, filename string, data interface{}) { mustRenderTemplate(w, r, "web.gohtml", filename, data) } diff --git a/web/static/numerus.css b/web/static/numerus.css index a519101..42827dc 100644 --- a/web/static/numerus.css +++ b/web/static/numerus.css @@ -492,7 +492,7 @@ ul[role="menu"].action-menu li i[class^='ri-'] { border: none; } -#profile-menu button { +#profile-menu button, td[data-hx-get] { cursor: pointer; } diff --git a/web/static/numerus.js b/web/static/numerus.js index c366cfe..9916f5e 100644 --- a/web/static/numerus.js +++ b/web/static/numerus.js @@ -348,6 +348,7 @@ class Tags extends HTMLDivElement { if (this.search.value && this.search.value.trim() !== '') { this.createTag(); } + this.dispatchEvent(new CustomEvent("numerus-tags-out", {bubbles: true})) }; window.addEventListener('focusin', this.onFocusOutHandler); diff --git a/web/template/invoices/index.gohtml b/web/template/invoices/index.gohtml index 0033f6d..366d6df 100644 --- a/web/template/invoices/index.gohtml +++ b/web/template/invoices/index.gohtml @@ -27,7 +27,8 @@ {{ define "content" }} {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.InvoicesIndexPage*/ -}}
-
+ {{ with .Filters }} {{ template "select-field" .Customer }} {{ template "select-field" .InvoiceStatus }} @@ -88,7 +89,10 @@
- + {{- range $index, $tag := .Tags }} {{- if gt $index 0 }}, {{ end -}} {{ . }} diff --git a/web/template/standalone.gohtml b/web/template/standalone.gohtml new file mode 100644 index 0000000..7324026 --- /dev/null +++ b/web/template/standalone.gohtml @@ -0,0 +1 @@ +{{- template "content" . }} diff --git a/web/template/tags/edit.gohtml b/web/template/tags/edit.gohtml new file mode 100644 index 0000000..5421927 --- /dev/null +++ b/web/template/tags/edit.gohtml @@ -0,0 +1,20 @@ +{{ define "content" }} + {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.tagsForm*/ -}} + +
+ {{ csrfToken }} + {{ putMethod }} + {{ template "tags-field" .Tags | addTagsAttr "autofocus" }} +
+ + +{{- end }} diff --git a/web/template/tags/view.gohtml b/web/template/tags/view.gohtml new file mode 100644 index 0000000..b0f6217 --- /dev/null +++ b/web/template/tags/view.gohtml @@ -0,0 +1,9 @@ +{{ define "content" }} + {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.tagsForm*/ -}} + + {{- range $index, $tag := .Tags.Tags }} + {{- if gt $index 0 }}, {{ end -}} + {{ . }} + {{- end }} + +{{- end }}