Compare commits

..

2 Commits

Author SHA1 Message Date
jordi fita mas 41ce5af2ed Boost the main navigation links with HTMx
I am not sure if, at the end, all pages that now use
mustRenderAppTemplate will be replaced with mustRenderMainTemplate,
but for now i keep them separate to know which routes are already
“boosted”.
2023-03-23 10:55:02 +01:00
jordi fita mas 6e081a1846 Put the edit contact form into a dialog with HTMx
Had to change the data context for that template to include the Slug,
so that the <form> element can set the correct `action` instead of
using the current URI, as it is no longer “correct” (form-wise) when
using HTMx.
2023-03-23 10:46:14 +01:00
9 changed files with 63 additions and 33 deletions

View File

@ -24,7 +24,7 @@ func IndexContacts(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
page := &ContactsIndexPage{ page := &ContactsIndexPage{
Contacts: mustGetContactEntries(r.Context(), conn, company), Contacts: mustGetContactEntries(r.Context(), conn, company),
} }
mustRenderAppTemplate(w, r, "contacts/index.gohtml", page) mustRenderMainTemplate(w, r, "contacts/index.gohtml", page)
} }
func GetContactForm(w http.ResponseWriter, r *http.Request, params httprouter.Params) { func GetContactForm(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
@ -42,15 +42,26 @@ func GetContactForm(w http.ResponseWriter, r *http.Request, params httprouter.Pa
return return
} }
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
mustRenderEditContactForm(w, r, form) mustRenderEditContactForm(w, r, slug, form)
} }
func mustRenderNewContactForm(w http.ResponseWriter, r *http.Request, form *contactForm) { func mustRenderNewContactForm(w http.ResponseWriter, r *http.Request, form *contactForm) {
mustRenderModalTemplate(w, r, "contacts/new.gohtml", form) mustRenderModalTemplate(w, r, "contacts/new.gohtml", form)
} }
func mustRenderEditContactForm(w http.ResponseWriter, r *http.Request, form *contactForm) { type editContactPage struct {
mustRenderAppTemplate(w, r, "contacts/edit.gohtml", form) Slug string
ContactName string
Form *contactForm
}
func mustRenderEditContactForm(w http.ResponseWriter, r *http.Request, slug string, form *contactForm) {
page := &editContactPage{
Slug: slug,
ContactName: form.BusinessName.Val,
Form: form,
}
mustRenderModalTemplate(w, r, "contacts/edit.gohtml", page)
} }
func HandleAddContact(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { func HandleAddContact(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
@ -96,14 +107,20 @@ func HandleUpdateContact(w http.ResponseWriter, r *http.Request, params httprout
return return
} }
if !form.Validate(r.Context(), conn) { if !form.Validate(r.Context(), conn) {
mustRenderEditContactForm(w, r, form) mustRenderEditContactForm(w, r, params[0].Value, form)
return return
} }
slug := conn.MustGetText(r.Context(), "", "update contact set business_name = $1, vatin = ($11 || $2)::vatin, trade_name = $3, phone = parse_packed_phone_number($4, $11), email = $5, web = $6, address = $7, city = $8, province = $9, postal_code = $10, country_code = $11 where slug = $12 returning slug", form.BusinessName, form.VATIN, form.TradeName, form.Phone, form.Email, form.Web, form.Address, form.City, form.Province, form.PostalCode, form.Country, params[0].Value) slug := conn.MustGetText(r.Context(), "", "update contact set business_name = $1, vatin = ($11 || $2)::vatin, trade_name = $3, phone = parse_packed_phone_number($4, $11), email = $5, web = $6, address = $7, city = $8, province = $9, postal_code = $10, country_code = $11 where slug = $12 returning slug", form.BusinessName, form.VATIN, form.TradeName, form.Phone, form.Email, form.Web, form.Address, form.City, form.Province, form.PostalCode, form.Country, params[0].Value)
if slug == "" { if slug == "" {
http.NotFound(w, r) http.NotFound(w, r)
} }
http.Redirect(w, r, companyURI(mustGetCompany(r), "/contacts/"+slug), http.StatusSeeOther) if IsHTMxRequest(r) {
w.Header().Set("HX-Trigger", "closeModal")
w.Header().Set("HX-Refresh", "true")
w.WriteHeader(http.StatusNoContent)
} else {
http.Redirect(w, r, companyURI(mustGetCompany(r), "/contacts/"+slug), http.StatusSeeOther)
}
} }
func mustGetContactEntries(ctx context.Context, conn *Conn, company *Company) []*ContactEntry { func mustGetContactEntries(ctx context.Context, conn *Conn, company *Company) []*ContactEntry {

View File

@ -46,7 +46,7 @@ func IndexInvoices(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
Invoices: mustCollectInvoiceEntries(r.Context(), conn, mustGetCompany(r), locale, tag), Invoices: mustCollectInvoiceEntries(r.Context(), conn, mustGetCompany(r), locale, tag),
InvoiceStatuses: mustCollectInvoiceStatuses(r.Context(), conn, locale), InvoiceStatuses: mustCollectInvoiceStatuses(r.Context(), conn, locale),
} }
mustRenderAppTemplate(w, r, "invoices/index.gohtml", page) mustRenderMainTemplate(w, r, "invoices/index.gohtml", page)
} }
func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, company *Company, locale *Locale, tag string) []*InvoiceEntry { func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, company *Company, locale *Locale, tag string) []*InvoiceEntry {

View File

@ -26,7 +26,7 @@ func IndexProducts(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
page := &productsIndexPage{ page := &productsIndexPage{
Products: mustGetProductEntries(r.Context(), conn, company), Products: mustGetProductEntries(r.Context(), conn, company),
} }
mustRenderAppTemplate(w, r, "products/index.gohtml", page) mustRenderMainTemplate(w, r, "products/index.gohtml", page)
} }
func GetProductForm(w http.ResponseWriter, r *http.Request, params httprouter.Params) { func GetProductForm(w http.ResponseWriter, r *http.Request, params httprouter.Params) {

View File

@ -32,7 +32,7 @@ func NewRouter(db *Db) http.Handler {
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.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { companyRouter.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
mustRenderAppTemplate(w, r, "dashboard.gohtml", nil) mustRenderMainTemplate(w, r, "dashboard.gohtml", nil)
}) })
router := httprouter.New() router := httprouter.New()

View File

@ -100,11 +100,19 @@ func mustRenderAppTemplate(w io.Writer, r *http.Request, filename string, data i
} }
func mustRenderModalTemplate(w io.Writer, r *http.Request, filename string, data interface{}) { func mustRenderModalTemplate(w io.Writer, r *http.Request, filename string, data interface{}) {
layout := "app.gohtml"
if IsHTMxRequest(r) { if IsHTMxRequest(r) {
layout = "modal.gohtml" mustRenderTemplate(w, r, "modal.gohtml", filename, data)
} else {
mustRenderAppTemplate(w, r, filename, data)
}
}
func mustRenderMainTemplate(w io.Writer, r *http.Request, filename string, data interface{}) {
if IsHTMxRequest(r) {
mustRenderTemplate(w, r, "main.gohtml", filename, data)
} else {
mustRenderAppTemplate(w, r, filename, data)
} }
mustRenderTemplate(w, r, layout, 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{}) {

View File

@ -40,7 +40,7 @@
</ul> </ul>
</details> </details>
</header> </header>
<nav aria-label="{{( pgettext "Main" "title" )}}"> <nav aria-label="{{( pgettext "Main" "title" )}}" data-hx-target="main" data-hx-boost="true">
<ul> <ul>
<li><a href="{{ companyURI "/" }}">{{( pgettext "Dashboard" "nav" )}}</a></li> <li><a href="{{ companyURI "/" }}">{{( pgettext "Dashboard" "nav" )}}</a></li>
<li><a href="{{ companyURI "/invoices" }}">{{( pgettext "Invoices" "nav" )}}</a></li> <li><a href="{{ companyURI "/invoices" }}">{{( pgettext "Invoices" "nav" )}}</a></li>

View File

@ -1,37 +1,40 @@
{{ define "title" -}} {{ define "title" -}}
{{printf (pgettext "Edit Contact “%s”" "title") .BusinessName.Val }} {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editContactPage*/ -}}
{{printf (pgettext "Edit Contact “%s”" "title") .ContactName }}
{{- end }} {{- end }}
{{ define "breadcrumbs" -}} {{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.contactForm*/ -}} {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editContactPage*/ -}}
<nav> <nav>
<p> <p>
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> / <a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a href="{{ companyURI "/contacts"}}">{{( pgettext "Contacts" "title" )}}</a> / <a href="{{ companyURI "/contacts"}}">{{( pgettext "Contacts" "title" )}}</a> /
<a>{{ .BusinessName.Val }}</a> <a>{{ .ContactName }}</a>
</p> </p>
</nav> </nav>
{{- end }} {{- end }}
{{ define "content" }} {{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.contactForm*/ -}} {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editContactPage*/ -}}
<section class="dialog-content"> <section class="dialog-content" id="edit-contact-dialog-content" data-hx-target="this">
<h2>{{printf (pgettext "Edit Contact “%s”" "title") .BusinessName.Val }}</h2> <h2>{{printf (pgettext "Edit Contact “%s”" "title") .ContactName }}</h2>
<form method="POST"> <form method="POST" action="{{ companyURI "/contacts/" }}{{ .Slug }}" data-hx-boost="true" data-hx-select="#edit-contact-dialog-content">
{{ csrfToken }} {{ csrfToken }}
{{ putMethod }} {{ putMethod }}
{{ template "input-field" .BusinessName }} {{ with .Form }}
{{ template "input-field" .VATIN }} {{ template "input-field" .BusinessName }}
{{ template "input-field" .TradeName }} {{ template "input-field" .VATIN }}
{{ template "input-field" .Phone }} {{ template "input-field" .TradeName }}
{{ template "input-field" .Email }} {{ template "input-field" .Phone }}
{{ template "input-field" .Web }} {{ template "input-field" .Email }}
{{ template "input-field" .Address | addInputAttr `class="width-2x"` }} {{ template "input-field" .Web }}
{{ template "input-field" .City }} {{ template "input-field" .Address | addInputAttr `class="width-2x"` }}
{{ template "input-field" .Province }} {{ template "input-field" .City }}
{{ template "input-field" .PostalCode }} {{ template "input-field" .Province }}
{{ template "select-field" .Country | addSelectAttr `class="width-fixed"` }} {{ template "input-field" .PostalCode }}
{{ template "select-field" .Country | addSelectAttr `class="width-fixed"` }}
{{ end }}
<fieldset> <fieldset>
<button class="primary" type="submit">{{( pgettext "Update contact" "action" )}}</button> <button class="primary" type="submit">{{( pgettext "Update contact" "action" )}}</button>

View File

@ -28,12 +28,12 @@
<th>{{( pgettext "Phone" "title" )}}</th> <th>{{( pgettext "Phone" "title" )}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody data-hx-push-url="false" data-hx-swap="beforeend">
{{ with .Contacts }} {{ with .Contacts }}
{{- range $contact := . }} {{- range $contact := . }}
<tr> <tr>
<td></td> <td></td>
<td><a href="{{ companyURI "/contacts/"}}{{ .Slug }}">{{ .Name }}</a></td> <td><a href="{{ companyURI "/contacts/"}}{{ .Slug }}" data-hx-boost="true">{{ .Name }}</a></td>
<td><a href="mailto:{{ .Email }}">{{ .Email }}</a></td> <td><a href="mailto:{{ .Email }}">{{ .Email }}</a></td>
<td><a href="tel:{{ .Phone }}">{{ .Phone }}</a></td> <td><a href="tel:{{ .Phone }}">{{ .Phone }}</a></td>
</tr> </tr>

2
web/template/main.gohtml Normal file
View File

@ -0,0 +1,2 @@
{{- template "breadcrumbs" . }}
{{- template "content" . }}