Compare commits

..

No commits in common. "f2a0cd7d947893f650d1a1fedc8f7e6a3f8f1946" and "e93a798223e1f263b43a9e161c1b06500b3a6954" have entirely different histories.

14 changed files with 94 additions and 40 deletions

View File

@ -48,7 +48,7 @@ func GetContactForm(w http.ResponseWriter, r *http.Request, params httprouter.Pa
}
func mustRenderNewContactForm(w http.ResponseWriter, r *http.Request, form *contactForm) {
mustRenderMainTemplate(w, r, "contacts/new.gohtml", form)
mustRenderModalTemplate(w, r, "contacts/new.gohtml", form)
}
type editContactPage struct {
@ -63,7 +63,7 @@ func mustRenderEditContactForm(w http.ResponseWriter, r *http.Request, slug stri
ContactName: form.BusinessName.Val,
Form: form,
}
mustRenderMainTemplate(w, r, "contacts/edit.gohtml", page)
mustRenderModalTemplate(w, r, "contacts/edit.gohtml", page)
}
func HandleAddContact(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
@ -87,7 +87,13 @@ func HandleAddContact(w http.ResponseWriter, r *http.Request, _ httprouter.Param
}
company := mustGetCompany(r)
conn.MustExec(r.Context(), "select add_contact($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)", company.Id, form.BusinessName, form.VATIN, form.TradeName, form.Phone, form.Email, form.Web, form.Address, form.City, form.Province, form.PostalCode, form.Country, form.Tags)
htmxRedirect(w, r, companyURI(company, "/contacts"))
if IsHTMxRequest(r) {
w.Header().Set(HxTrigger, "closeModal")
w.Header().Set(HxRefresh, "true")
w.WriteHeader(http.StatusNoContent)
} else {
http.Redirect(w, r, companyURI(company, "/contacts"), http.StatusSeeOther)
}
}
func HandleUpdateContact(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
@ -110,7 +116,13 @@ func HandleUpdateContact(w http.ResponseWriter, r *http.Request, params httprout
if slug == "" {
http.NotFound(w, r)
}
htmxRedirect(w, r, companyURI(mustGetCompany(r), "/contacts"))
if IsHTMxRequest(r) {
w.Header().Set(HxTrigger, "closeModal")
w.Header().Set(HxRefresh, "true")
w.WriteHeader(http.StatusNoContent)
} else {
http.Redirect(w, r, companyURI(mustGetCompany(r), "/contacts/"+slug), http.StatusSeeOther)
}
}
func mustCollectContactEntries(ctx context.Context, conn *Conn, company *Company, tag string) []*ContactEntry {

View File

@ -229,6 +229,7 @@ func ServeInvoice(w http.ResponseWriter, r *http.Request, params httprouter.Para
if invoiceToDuplicate := r.URL.Query().Get("duplicate"); invoiceToDuplicate != "" {
form.MustFillFromDatabase(r.Context(), conn, invoiceToDuplicate)
form.InvoiceStatus.Selected = []string{"created"}
form.Location.Val = r.URL.Query().Get("location")
}
form.Date.Val = time.Now().Format("2006-01-02")
w.WriteHeader(http.StatusOK)
@ -422,7 +423,7 @@ func mustRenderNewInvoiceForm(w http.ResponseWriter, r *http.Request, form *invo
locale := getLocale(r)
form.Customer.EmptyLabel = gettext("Select a customer to bill.", locale)
page := newNewInvoicePage(form, r)
mustRenderMainTemplate(w, r, "invoices/new.gohtml", page)
mustRenderModalTemplate(w, r, "invoices/new.gohtml", page)
}
func mustRenderNewInvoiceProductsForm(w http.ResponseWriter, r *http.Request, action string, form *invoiceForm) {
@ -433,7 +434,7 @@ func mustRenderNewInvoiceProductsForm(w http.ResponseWriter, r *http.Request, ac
Form: form,
Products: mustGetProductChoices(r.Context(), conn, company),
}
mustRenderMainTemplate(w, r, "invoices/products.gohtml", page)
mustRenderModalTemplate(w, r, "invoices/products.gohtml", page)
}
func mustGetProductChoices(ctx context.Context, conn *Conn, company *Company) []*productChoice {
@ -488,7 +489,7 @@ func HandleAddInvoice(w http.ResponseWriter, r *http.Request, _ httprouter.Param
return
}
slug := conn.MustGetText(r.Context(), "", "select add_invoice($1, $2, $3, $4, $5, $6, $7)", company.Id, form.Date, form.Customer, form.Notes, form.PaymentMethod, form.Tags, NewInvoiceProductArray(form.Products))
htmxRedirect(w, r, companyURI(company, "/invoices/"+slug))
closeModalAndRedirect(w, r, form.Location.Val, "/invoices/"+slug, "/invoices")
}
func HandleNewInvoiceAction(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
@ -555,6 +556,7 @@ type invoiceForm struct {
locale *Locale
company *Company
Number string
Location *InputField
InvoiceStatus *SelectField
Customer *SelectField
Date *InputField
@ -568,6 +570,10 @@ func newInvoiceForm(ctx context.Context, conn *Conn, locale *Locale, company *Co
return &invoiceForm{
locale: locale,
company: company,
Location: &InputField{
Name: "redirect",
Type: "hidden",
},
InvoiceStatus: &SelectField{
Name: "invoice_status",
Required: true,
@ -610,6 +616,7 @@ func (form *invoiceForm) Parse(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
form.Location.FillValue(r)
form.InvoiceStatus.FillValue(r)
form.Customer.FillValue(r)
form.Date.FillValue(r)
@ -925,10 +932,22 @@ func HandleUpdateInvoice(w http.ResponseWriter, r *http.Request, params httprout
http.NotFound(w, r)
return
}
htmxRedirect(w, r, companyURI(company, "/invoices/"+slug))
closeModalAndRedirect(w, r, form.Location.Val, "/invoices/"+slug, "/invoices")
}
}
func closeModalAndRedirect(w http.ResponseWriter, r *http.Request, selector string, viewUri string, indexUri string) {
company := mustGetCompany(r)
nextUri := companyURI(company, indexUri)
if IsHTMxRequest(r) {
w.Header().Set(HxTrigger, "closeModal")
if selector == "view" {
nextUri = companyURI(company, viewUri)
}
}
htmxRedirect(w, r, nextUri)
}
func htmxRedirect(w http.ResponseWriter, r *http.Request, uri string) {
if IsHTMxRequest(r) {
w.Header().Set(HxLocation, MustMarshalHTMxLocation(&HTMxLocation{
@ -951,6 +970,7 @@ func ServeEditInvoice(w http.ResponseWriter, r *http.Request, params httprouter.
http.NotFound(w, r)
return
}
form.Location.Val = r.URL.Query().Get("location")
w.WriteHeader(http.StatusOK)
mustRenderEditInvoiceForm(w, r, slug, form)
}
@ -971,7 +991,7 @@ func newEditInvoicePage(slug string, form *invoiceForm, r *http.Request) *editIn
func mustRenderEditInvoiceForm(w http.ResponseWriter, r *http.Request, slug string, form *invoiceForm) {
page := newEditInvoicePage(slug, form, r)
mustRenderMainTemplate(w, r, "invoices/edit.gohtml", page)
mustRenderModalTemplate(w, r, "invoices/edit.gohtml", page)
}
func HandleEditInvoiceAction(w http.ResponseWriter, r *http.Request, params httprouter.Params) {

View File

@ -59,7 +59,7 @@ func GetProductForm(w http.ResponseWriter, r *http.Request, params httprouter.Pa
}
func mustRenderNewProductForm(w http.ResponseWriter, r *http.Request, form *productForm) {
mustRenderMainTemplate(w, r, "products/new.gohtml", form)
mustRenderModalTemplate(w, r, "products/new.gohtml", form)
}
type editProductPage struct {
@ -74,7 +74,7 @@ func mustRenderEditProductForm(w http.ResponseWriter, r *http.Request, slug stri
ProductName: form.Name.Val,
Form: form,
}
mustRenderMainTemplate(w, r, "products/edit.gohtml", page)
mustRenderModalTemplate(w, r, "products/edit.gohtml", page)
}
func HandleAddProduct(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
@ -99,7 +99,13 @@ func HandleAddProduct(w http.ResponseWriter, r *http.Request, _ httprouter.Param
}
taxes := mustSliceAtoi(form.Tax.Selected)
conn.MustExec(r.Context(), "select add_product($1, $2, $3, $4, $5, $6)", company.Id, form.Name, form.Description, form.Price, taxes, form.Tags)
htmxRedirect(w, r, companyURI(company, "/products"))
if IsHTMxRequest(r) {
w.Header().Set(HxTrigger, "closeModal")
w.Header().Set(HxRefresh, "true")
w.WriteHeader(http.StatusNoContent)
} else {
http.Redirect(w, r, companyURI(company, "/products"), http.StatusSeeOther)
}
}
func sliceAtoi(s []string) ([]int, error) {
@ -145,7 +151,13 @@ func HandleUpdateProduct(w http.ResponseWriter, r *http.Request, params httprout
if ok := conn.MustGetBool(r.Context(), "select edit_product($1, $2, $3, $4, $5, $6)", slug, form.Name, form.Description, form.Price, taxes, form.Tags); !ok {
http.NotFound(w, r)
}
htmxRedirect(w, r, companyURI(company, "/products"))
if IsHTMxRequest(r) {
w.Header().Set(HxTrigger, "closeModal")
w.Header().Set(HxRefresh, "true")
w.WriteHeader(http.StatusNoContent)
} else {
http.Redirect(w, r, companyURI(company, "/products/"+slug), http.StatusSeeOther)
}
}
type productFilterForm struct {
@ -158,7 +170,7 @@ func newProductFilterForm(locale *Locale) *productFilterForm {
return &productFilterForm{
Name: &InputField{
Name: "number",
Label: pgettext("input", "Name", locale),
Label: pgettext("input", "Invoice Number", locale),
Type: "search",
},
Tags: &TagsField{

View File

@ -16,9 +16,9 @@
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editContactPage*/ -}}
<section class="dialog-content" id="edit-contact-dialog-content" data-hx-target="main">
<section class="dialog-content" id="edit-contact-dialog-content" data-hx-target="this">
<h2>{{printf (pgettext "Edit Contact “%s”" "title") .ContactName }}</h2>
<form method="POST" action="{{ companyURI "/contacts/" }}{{ .Slug }}" data-hx-boost="true">
<form method="POST" action="{{ companyURI "/contacts/" }}{{ .Slug }}" data-hx-boost="true" data-hx-select="#edit-contact-dialog-content">
{{ csrfToken }}
{{ putMethod }}

View File

@ -4,13 +4,14 @@
{{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.ContactsIndexPage*/ -}}
<nav data-hx-boost="true" data-hx-target="main">
<p>
<nav data-hx-boost="true">
<p data-hx-target="main">
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a>{{( pgettext "Contacts" "title" )}}</a>
</p>
<p>
<a class="primary button"
data-hx-push-url="false" data-hx-swap="beforeend"
href="{{ companyURI "/contacts/new" }}">{{( pgettext "New contact" "action" )}}</a>
</p>
</nav>
@ -33,7 +34,7 @@
{{- range $contact := . }}
<tr>
<td></td>
<td><a href="{{ companyURI "/contacts/"}}{{ .Slug }}" data-hx-target="main" data-hx-boost="true">{{ .Name }}</a></td>
<td><a href="{{ companyURI "/contacts/"}}{{ .Slug }}" data-hx-push-url="false" data-hx-swap="beforeend" data-hx-boost="true">{{ .Name }}</a></td>
<td><a href="mailto:{{ .Email }}">{{ .Email }}</a></td>
<td><a href="tel:{{ .Phone }}">{{ .Phone }}</a></td>
<td>

View File

@ -15,9 +15,9 @@
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.contactForm*/ -}}
<section class="dialog-content" id="new-contact-dialog-content" data-hx-target="main">
<section class="dialog-content" id="new-contact-dialog-content" data-hx-target="this">
<h2>{{(pgettext "New Contact" "title")}}</h2>
<form method="POST" action="{{ companyURI "/contacts" }}" data-hx-boost="true">
<form method="POST" action="{{ companyURI "/contacts" }}" data-hx-boost="true" data-hx-select="#new-contact-dialog-content">
{{ csrfToken }}
{{ template "input-field" .BusinessName | addInputAttr "autofocus" }}
{{ template "input-field" .VATIN }}

View File

@ -15,12 +15,13 @@
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editInvoicePage*/ -}}
<section class="dialog-content" id="invoice-dialog-content" data-hx-target="main">
<section class="dialog-content" id="invoice-dialog-content" data-hx-target="this">
<h2>{{ printf (pgettext "Edit Invoice “%s”" "title") .Number }}</h2>
<form method="POST" action="{{ companyURI "/invoices/" }}{{ .Slug }}" data-hx-boost="true">
<form method="POST" action="{{ companyURI "/invoices/" }}{{ .Slug }}" data-hx-boost="true" data-hx-select="#invoice-dialog-content">
{{ csrfToken }}
{{ with .Form -}}
{{ template "hidden-field" .Location }}
{{ template "select-field" .Customer }}
{{ template "hidden-field" .Date }}
{{ template "tags-field" .Tags }}

View File

@ -4,8 +4,8 @@
{{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.InvoicesIndexPage*/ -}}
<nav data-hx-target="main" data-hx-boost="true">
<p>
<nav>
<p data-hx-target="main" data-hx-boost="true">
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a>{{( pgettext "Invoices" "title" )}}</a>
</p>
@ -17,6 +17,7 @@
name="action" value="download"
>{{( pgettext "Download invoices" "action" )}}</button>
<a class="primary button"
data-hx-push-url="false" data-hx-swap="beforeend" data-hx-boost="true"
href="{{ companyURI "/invoices/new" }}">{{( pgettext "New invoice" "action" )}}</a>
</p>
</form>
@ -110,7 +111,7 @@
<ul role="menu" class="action-menu">
<li role="presentation">
<a role="menuitem" href="{{ companyURI "/invoices"}}/{{ .Slug }}/edit"
data-hx-target="main" data-hx-boost="true"
data-hx-push-url="false" data-hx-swap="beforeend" data-hx-boost="true"
>
<i class="ri-edit-line"></i>
{{( pgettext "Edit" "action" )}}
@ -118,7 +119,7 @@
</li>
<li role="presentation">
<a role="menuitem" href="{{ companyURI "/invoices/new"}}?duplicate={{ .Slug }}"
data-hx-target="main" data-hx-boost="true"
data-hx-push-url="false" data-hx-swap="beforeend" data-hx-boost="true"
>
<i class="ri-file-copy-line"></i>
{{( pgettext "Duplicate" "action" )}}

View File

@ -15,12 +15,13 @@
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newInvoicePage*/ -}}
<section class="dialog-content" id="invoice-dialog-content" data-hx-target="main">
<section class="dialog-content" id="invoice-dialog-content" data-hx-target="this">
<h2>{{(pgettext "New Invoice" "title")}}</h2>
<form method="POST" action="{{ companyURI "/invoices" }}" data-hx-boost="true">
<form method="POST" action="{{ companyURI "/invoices" }}" data-hx-boost="true" data-hx-select="#invoice-dialog-content">
{{ csrfToken }}
{{ with .Form -}}
{{ template "hidden-field" .Location }}
{{ template "hidden-select-field" .InvoiceStatus }}
{{ template "select-field" .Customer }}
{{ template "input-field" .Date }}

View File

@ -25,6 +25,7 @@
{{ csrfToken }}
{{- with .Form }}
{{ template "hidden-field" .Location }}
{{ template "hidden-select-field" .Customer }}
{{ template "hidden-field" .Date }}
{{ template "hidden-field" .Notes }}

View File

@ -12,11 +12,11 @@
</p>
<p>
<a class="button primary"
data-hx-target="main" data-hx-boost="true"
href="{{ companyURI "/invoices/new"}}?duplicate={{ .Slug }}">{{( pgettext "Duplicate" "action" )}}</a>
data-hx-push-url="false" data-hx-swap="beforeend" data-hx-boost="true"
href="{{ companyURI "/invoices/new"}}?duplicate={{ .Slug }}&amp;location=view">{{( pgettext "Duplicate" "action" )}}</a>
<a class="button primary"
data-hx-target="main" data-hx-boost="true"
href="{{ companyURI "/invoices/"}}{{ .Slug }}/edit">{{( pgettext "Edit" "action" )}}</a>
data-hx-push-url="false" data-hx-swap="beforeend" data-hx-boost="true"
href="{{ companyURI "/invoices/"}}{{ .Slug }}/edit?location=view">{{( pgettext "Edit" "action" )}}</a>
<a class="primary button"
href="{{ companyURI "/invoices/" }}{{ .Slug }}.pdf"
download="{{ .Number}}.pdf">{{( pgettext "Download invoice" "action" )}}</a>

View File

@ -16,9 +16,11 @@
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editProductPage*/ -}}
<section class="dialog-content" id="edit-product-dialog-content" data-hx-target="main">
<section class="dialog-content" id="edit-product-dialog-content" data-hx-target="this">
<h2>{{printf (pgettext "Edit Product “%s”" "title") .ProductName }}</h2>
<form method="POST" action="{{ companyURI "/products/" }}{{ .Slug }}" data-hx-boost="true">
<form method="POST" action="{{ companyURI "/products/" }}{{ .Slug }}"
data-hx-boost="true" data-hx-select="#edit-product-dialog-content"
>
{{ csrfToken }}
{{ putMethod }}

View File

@ -4,13 +4,14 @@
{{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.productsIndexPage*/ -}}
<nav data-hx-boost="true" data-hx-target="main">
<p>
<nav data-hx-boost="true">
<p data-hx-target="main">
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a>{{( pgettext "Products" "title" )}}</a>
</p>
<p>
<a class="primary button"
data-hx-push-url="false" data-hx-swap="beforeend"
href="{{ companyURI "/products/new" }}">{{( pgettext "New product" "action" )}}</a>
</p>
</nav>
@ -46,7 +47,7 @@
{{- range $product := . }}
<tr>
<td></td>
<td><a href="{{ companyURI "/products/"}}{{ .Slug }}" data-hx-target="main" data-hx-boost="true">{{ .Name }}</a></td>
<td><a href="{{ companyURI "/products/"}}{{ .Slug }}" data-hx-push-url="false" data-hx-swap="beforeend" data-hx-boost="true">{{ .Name }}</a></td>
<td>
{{- range $index, $tag := .Tags }}
{{- if gt $index 0 }}, {{ end -}}

View File

@ -15,9 +15,11 @@
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.productForm*/ -}}
<section class="dialog-content" id="new-product-dialog-content" data-hx-target="main">
<section class="dialog-content" id="new-product-dialog-content" data-hx-target="this">
<h2>{{(pgettext "New Product" "title")}}</h2>
<form method="POST" action="{{ companyURI "/products" }}" data-hx-boost="true">
<form method="POST" action="{{ companyURI "/products" }}"
data-hx-boost="true" data-hx-select="#new-product-dialog-content"
>
{{ csrfToken }}
{{ template "input-field" .Name | addInputAttr "autofocus" }}