Compare commits
3 Commits
33277454fa
...
d20573aa99
Author | SHA1 | Date |
---|---|---|
jordi fita mas | d20573aa99 | |
jordi fita mas | 1290fc7283 | |
jordi fita mas | 3b568b013f |
|
@ -929,3 +929,56 @@ func handleInvoiceAction(w http.ResponseWriter, r *http.Request, action string,
|
||||||
http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ func NewRouter(db *Db) http.Handler {
|
||||||
companyRouter.POST("/invoices/:slug", HandleNewInvoiceAction)
|
companyRouter.POST("/invoices/:slug", HandleNewInvoiceAction)
|
||||||
companyRouter.GET("/invoices/:slug/edit", ServeEditInvoice)
|
companyRouter.GET("/invoices/:slug/edit", ServeEditInvoice)
|
||||||
companyRouter.POST("/invoices/:slug/edit", HandleEditInvoiceAction)
|
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) {
|
companyRouter.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||||
mustRenderMainTemplate(w, r, "dashboard.gohtml", nil)
|
mustRenderMainTemplate(w, r, "dashboard.gohtml", nil)
|
||||||
})
|
})
|
||||||
|
|
|
@ -57,6 +57,10 @@ func mustRenderTemplate(wr io.Writer, r *http.Request, layout string, filename s
|
||||||
field.Attributes = append(field.Attributes, template.HTMLAttr(attr))
|
field.Attributes = append(field.Attributes, template.HTMLAttr(attr))
|
||||||
return field
|
return field
|
||||||
},
|
},
|
||||||
|
"addTagsAttr": func(attr string, field *TagsField) *TagsField {
|
||||||
|
field.Attributes = append(field.Attributes, template.HTMLAttr(attr))
|
||||||
|
return field
|
||||||
|
},
|
||||||
"boolToInt": func(b bool) int {
|
"boolToInt": func(b bool) int {
|
||||||
if b {
|
if b {
|
||||||
return 1
|
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{}) {
|
func mustRenderWebTemplate(w io.Writer, r *http.Request, filename string, data interface{}) {
|
||||||
mustRenderTemplate(w, r, "web.gohtml", filename, data)
|
mustRenderTemplate(w, r, "web.gohtml", filename, data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -492,7 +492,7 @@ ul[role="menu"].action-menu li i[class^='ri-'] {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile-menu button {
|
#profile-menu button, td[data-hx-get] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ class Multiselect extends HTMLDivElement {
|
||||||
}
|
}
|
||||||
window.addEventListener('focusin', this.onFocusOutHandler);
|
window.addEventListener('focusin', this.onFocusOutHandler);
|
||||||
document.addEventListener('click', this.onFocusOutHandler);
|
document.addEventListener('click', this.onFocusOutHandler);
|
||||||
|
|
||||||
this.rebuild()
|
this.rebuild()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,9 +345,10 @@ class Tags extends HTMLDivElement {
|
||||||
this.search.addEventListener('keydown', this.onSearchKeydownHandler);
|
this.search.addEventListener('keydown', this.onSearchKeydownHandler);
|
||||||
this.onFocusOutHandler = (e) => {
|
this.onFocusOutHandler = (e) => {
|
||||||
if (this.contains(e.target)) return;
|
if (this.contains(e.target)) return;
|
||||||
if (e.target.value && e.target.value.trim() !== '') {
|
if (this.search.value && this.search.value.trim() !== '') {
|
||||||
this.createTag();
|
this.createTag();
|
||||||
}
|
}
|
||||||
|
this.dispatchEvent(new CustomEvent("numerus-tags-out", {bubbles: true}))
|
||||||
};
|
};
|
||||||
window.addEventListener('focusin', this.onFocusOutHandler);
|
window.addEventListener('focusin', this.onFocusOutHandler);
|
||||||
|
|
||||||
|
@ -423,7 +424,8 @@ class Tags extends HTMLDivElement {
|
||||||
button.textContent = '×';
|
button.textContent = '×';
|
||||||
button.addEventListener('click', (e) => {
|
button.addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.removeTag(tagText)
|
this.removeTag(tagText);
|
||||||
|
this.search.focus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.InvoicesIndexPage*/ -}}
|
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.InvoicesIndexPage*/ -}}
|
||||||
<div aria-label="{{( pgettext "Filters" "title" )}}">
|
<div aria-label="{{( pgettext "Filters" "title" )}}">
|
||||||
<form method="GET" action="{{ companyURI "/invoices"}}" data-hx-target="main" data-hx-boost="true" data-hx-trigger="change,search,submit">
|
<form method="GET" action="{{ companyURI "/invoices"}}" data-hx-target="main" data-hx-boost="true"
|
||||||
|
data-hx-trigger="change,search,submit">
|
||||||
{{ with .Filters }}
|
{{ with .Filters }}
|
||||||
{{ template "select-field" .Customer }}
|
{{ template "select-field" .Customer }}
|
||||||
{{ template "select-field" .InvoiceStatus }}
|
{{ template "select-field" .InvoiceStatus }}
|
||||||
|
@ -88,7 +89,10 @@
|
||||||
</form>
|
</form>
|
||||||
</details>
|
</details>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td data-hx-get="{{companyURI "/invoices/"}}{{ .Slug }}/tags/edit"
|
||||||
|
data-hx-target="this"
|
||||||
|
data-hx-swap="outerHTML"
|
||||||
|
>
|
||||||
{{- range $index, $tag := .Tags }}
|
{{- range $index, $tag := .Tags }}
|
||||||
{{- if gt $index 0 }}, {{ end -}}
|
{{- if gt $index 0 }}, {{ end -}}
|
||||||
{{ . }}
|
{{ . }}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{{- template "content" . }}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{{ define "content" }}
|
||||||
|
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.tagsForm*/ -}}
|
||||||
|
<td data-hx-target="this" data-hx-swap="outerHTML">
|
||||||
|
<form action="{{companyURI "/invoices/"}}{{ .Slug }}/tags" method="POST"
|
||||||
|
data-hx-push-url="false" data-hx-boost="true" data-hx-trigger="numerus-tags-out,submit">
|
||||||
|
{{ csrfToken }}
|
||||||
|
{{ putMethod }}
|
||||||
|
{{ template "tags-field" .Tags | addTagsAttr "autofocus" }}
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
const edit = document.getElementById('{{.Tags.Name}}-field');
|
||||||
|
if (edit) {
|
||||||
|
edit.focus();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</td>
|
||||||
|
{{- end }}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{{ define "content" }}
|
||||||
|
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.tagsForm*/ -}}
|
||||||
|
<td data-hx-get="{{companyURI "/invoices/"}}{{ .Slug }}/tags/edit" data-hx-target="this" data-hx-swap="outerHTML">
|
||||||
|
{{- range $index, $tag := .Tags.Tags }}
|
||||||
|
{{- if gt $index 0 }}, {{ end -}}
|
||||||
|
{{ . }}
|
||||||
|
{{- end }}
|
||||||
|
</td>
|
||||||
|
{{- end }}
|
Loading…
Reference in New Issue