Show the profile form in a dialog using HTMx
Had to split the actual page content and the breadcrumbs because they do not belong in a dialog. However, i had to change all templates to do that.
This commit is contained in:
parent
82eb8a2733
commit
9e757cb9f4
|
@ -121,7 +121,9 @@ func HandleProfileForm(w http.ResponseWriter, r *http.Request, _ httprouter.Para
|
|||
return
|
||||
}
|
||||
if ok := form.Validate(); !ok {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
if !IsHTMxRequest(r) {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
}
|
||||
mustRenderProfileForm(w, r, form)
|
||||
return
|
||||
}
|
||||
|
@ -131,10 +133,15 @@ func HandleProfileForm(w http.ResponseWriter, r *http.Request, _ httprouter.Para
|
|||
if form.Password.Val != "" {
|
||||
conn.MustExec(r.Context(), "select change_password($1)", form.Password)
|
||||
}
|
||||
company := mustGetCompany(r)
|
||||
http.Redirect(w, r, companyURI(company, "/profile"), http.StatusSeeOther)
|
||||
if IsHTMxRequest(r) {
|
||||
w.Header().Set("HX-Trigger", "closeModal")
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
} else {
|
||||
company := mustGetCompany(r)
|
||||
http.Redirect(w, r, companyURI(company, "/profile"), http.StatusSeeOther)
|
||||
}
|
||||
}
|
||||
|
||||
func mustRenderProfileForm(w http.ResponseWriter, r *http.Request, form *profileForm) {
|
||||
mustRenderAppTemplate(w, r, "profile.gohtml", form)
|
||||
mustRenderModalTemplate(w, r, "profile.gohtml", form)
|
||||
}
|
||||
|
|
|
@ -87,3 +87,7 @@ func MethodOverrider(next http.Handler) http.Handler {
|
|||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func IsHTMxRequest(r *http.Request) bool {
|
||||
return r.Header.Get("HX-Request") == "true"
|
||||
}
|
||||
|
|
|
@ -99,6 +99,14 @@ func mustRenderAppTemplate(w io.Writer, r *http.Request, filename string, data i
|
|||
mustRenderTemplate(w, r, "app.gohtml", filename, data)
|
||||
}
|
||||
|
||||
func mustRenderModalTemplate(w io.Writer, r *http.Request, filename string, data interface{}) {
|
||||
layout := "app.gohtml"
|
||||
if IsHTMxRequest(r) {
|
||||
layout = "modal.gohtml"
|
||||
}
|
||||
mustRenderTemplate(w, r, layout, filename, data)
|
||||
}
|
||||
|
||||
func mustRenderWebTemplate(w io.Writer, r *http.Request, filename string, data interface{}) {
|
||||
mustRenderTemplate(w, r, "web.gohtml", filename, data)
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -170,13 +170,16 @@ html {
|
|||
}
|
||||
|
||||
body {
|
||||
background-color: var(--numerus--background-color);
|
||||
color: var(--numerus--text-color);
|
||||
font-size: 1.6rem;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body, dialog {
|
||||
background-color: var(--numerus--background-color);
|
||||
color: var(--numerus--text-color);
|
||||
}
|
||||
|
||||
img, picture, video, canvas, svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
|
@ -679,6 +682,20 @@ main > nav {
|
|||
background-color: var(--numerus--color--light-gray);
|
||||
}
|
||||
|
||||
/* Modal */
|
||||
dialog {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.modal .close-dialog {
|
||||
min-width: initial;
|
||||
border: 0;
|
||||
position: absolute;
|
||||
top: .5rem;
|
||||
right: .5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Remix Icon */
|
||||
|
||||
@font-face {
|
||||
|
|
|
@ -400,3 +400,29 @@ class Tags extends HTMLDivElement {
|
|||
|
||||
customElements.define('numerus-multiselect', Multiselect, {extends: 'div'});
|
||||
customElements.define('numerus-tags', Tags, {extends: 'div'});
|
||||
|
||||
|
||||
htmx.onLoad((target) => {
|
||||
if (target.tagName === 'DIALOG') {
|
||||
const details = document.querySelectorAll('details[open]');
|
||||
for (const detail of details) {
|
||||
detail.removeAttribute('open');
|
||||
}
|
||||
target.showModal();
|
||||
const button = target.querySelector('.close-dialog');
|
||||
if (button) {
|
||||
button.addEventListener('click', () => {
|
||||
htmx.trigger(target, 'closeModal');
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
htmx.on('closeModal', () => {
|
||||
const openDialog = document.querySelector('dialog[open]');
|
||||
if (!openDialog) {
|
||||
return;
|
||||
}
|
||||
openDialog.close();
|
||||
openDialog.remove();
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ template "title" . }} — Numerus</title>
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="/static/numerus.css">
|
||||
<script src="/static/htmx@1.8.6.min.js"></script>
|
||||
<script type="module" src="/static/numerus.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -14,9 +15,9 @@
|
|||
<summary>
|
||||
<i class="ri-eye-close-line ri-3x"></i>
|
||||
</summary>
|
||||
<ul role="menu" class="action-menu">
|
||||
<ul role="menu" class="action-menu" data-hx-push-url="false" data-hx-swap="beforeend">
|
||||
<li role="presentation">
|
||||
<a role="menuitem" href="{{ companyURI "/profile" }}">
|
||||
<a role="menuitem" href="{{ companyURI "/profile" }}" data-hx-boost="true">
|
||||
<i class="ri-account-circle-line"></i>
|
||||
{{( pgettext "Account" "menu" )}}
|
||||
</a>
|
||||
|
@ -48,6 +49,7 @@
|
|||
</ul>
|
||||
</nav>
|
||||
<main>
|
||||
{{- template "breadcrumbs" . }}
|
||||
{{- template "content" . }}
|
||||
</main>
|
||||
</body>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{{printf (pgettext "Edit Contact “%s”" "title") .BusinessName.Val }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.contactForm*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
|
@ -11,6 +11,10 @@
|
|||
<a>{{ .BusinessName.Val }}</a>
|
||||
</p>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.contactForm*/ -}}
|
||||
<section class="dialog-content">
|
||||
<h2>{{printf (pgettext "Edit Contact “%s”" "title") .BusinessName.Val }}</h2>
|
||||
<form method="POST">
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
{{( pgettext "Contacts" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.ContactsIndexPage*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
|
||||
|
@ -13,7 +14,9 @@
|
|||
href="{{ companyURI "/contacts/new" }}">{{( pgettext "New contact" "action" )}}</a>
|
||||
</p>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.ContactsIndexPage*/ -}}
|
||||
<table>
|
||||
<thead>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{{( pgettext "New Contact" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.contactForm*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
|
@ -11,6 +11,10 @@
|
|||
<a>{{( pgettext "New Contact" "title" )}}</a>
|
||||
</p>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.contactForm*/ -}}
|
||||
<section class="dialog-content">
|
||||
<h2>{{(pgettext "New Contact" "title")}}</h2>
|
||||
<form method="POST" action="{{ companyURI "/contacts" }}">
|
||||
|
|
|
@ -2,5 +2,8 @@
|
|||
{{( pgettext "Dashboard" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- end }}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{{ printf ( pgettext "Edit Invoice “%s”" "title" ) .Number }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editInvoicePage*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
|
@ -11,6 +11,10 @@
|
|||
<a>{{ .Number }}</a>
|
||||
</p>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editInvoicePage*/ -}}
|
||||
<section class="dialog-content">
|
||||
<h2>{{ printf (pgettext "Edit Invoice “%s”" "title") .Number }}</h2>
|
||||
<form method="POST" action="{{ companyURI "/invoices/" }}{{ .Slug }}">
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
{{( pgettext "Invoices" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.InvoicesIndexPage*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
|
||||
|
@ -20,7 +21,9 @@
|
|||
</p>
|
||||
</form>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.InvoicesIndexPage*/ -}}
|
||||
<table class="no-padding">
|
||||
<thead>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{{( pgettext "New Invoice" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newInvoicePage*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
|
@ -11,6 +11,10 @@
|
|||
<a>{{( pgettext "New Invoice" "title" )}}</a>
|
||||
</p>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newInvoicePage*/ -}}
|
||||
<section class="dialog-content">
|
||||
<h2>{{(pgettext "New Invoice" "title")}}</h2>
|
||||
<form method="POST" action="{{ companyURI "/invoices" }}">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{{( pgettext "Add Products to Invoice" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newInvoiceProductsPage*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
|
@ -15,6 +15,10 @@
|
|||
{{ end }}
|
||||
</p>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newInvoiceProductsPage*/ -}}
|
||||
<section class="dialog-content">
|
||||
<h2>{{(pgettext "Add Products to Invoice" "title")}}</h2>
|
||||
<form method="POST" action="{{ .Action }}">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{{ .Number | printf ( pgettext "Invoice %s" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.invoice*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
|
@ -19,7 +19,10 @@
|
|||
download="{{ .Number}}.pdf">{{( pgettext "Download invoice" "action" )}}</a>
|
||||
</p>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.invoice*/ -}}
|
||||
<link rel="stylesheet" type="text/css" href="/static/invoice.css">
|
||||
<article class="invoice">
|
||||
<header>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<dialog class="modal" data-hx-push-url="false" data-hx-swap="outerHTML">
|
||||
<button class="close-dialog" type="button" title="{{( pgettext "Close dialog" "action" )}}"><i class="ri-close-line ri-2x"></i></button>
|
||||
{{- template "content" . }}
|
||||
</dialog>
|
|
@ -2,7 +2,7 @@
|
|||
{{printf (pgettext "Edit Product “%s”" "title") .Name }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.productForm*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
|
@ -11,6 +11,10 @@
|
|||
<a>{{ .Name }}</a>
|
||||
</p>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.productForm*/ -}}
|
||||
<section class="dialog-content">
|
||||
<h2>{{printf (pgettext "Edit Product “%s”" "title") .Name }}</h2>
|
||||
<form method="POST">
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
{{( pgettext "Products" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.productsIndexPage*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
|
||||
|
@ -13,7 +14,9 @@
|
|||
href="{{ companyURI "/products/new" }}">{{( pgettext "New product" "action" )}}</a>
|
||||
</p>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.productsIndexPage*/ -}}
|
||||
<table>
|
||||
<thead>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{{( pgettext "New Product" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.productForm*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
|
@ -11,6 +11,10 @@
|
|||
<a>{{( pgettext "New Product" "title" )}}</a>
|
||||
</p>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.productForm*/ -}}
|
||||
<section class="dialog-content">
|
||||
<h2>{{(pgettext "New Product" "title")}}</h2>
|
||||
<form method="POST" action="{{ companyURI "/products" }}">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{{( pgettext "User Settings" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.profileForm*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
|
@ -10,9 +10,13 @@
|
|||
<a>{{( pgettext "User Settings" "title" )}}</a>
|
||||
</p>
|
||||
</nav>
|
||||
<section class="dialog-content">
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.profileForm*/ -}}
|
||||
<section class="dialog-content" id="profile-dialog-content" data-hx-target="this">
|
||||
<h2>{{(pgettext "User Settings" "title")}}</h2>
|
||||
<form method="POST">
|
||||
<form method="POST" action="{{ companyURI "/profile" }}" data-hx-boost="true" data-hx-select="#profile-dialog-content">
|
||||
{{ csrfToken }}
|
||||
<fieldset class="full-width">
|
||||
<legend>{{( pgettext "User Access Data" "title" )}}</legend>
|
||||
|
|
|
@ -2,16 +2,20 @@
|
|||
{{( pgettext "Tax Details" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{ define "breadcrumbs" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.TaxDetailsPage*/ -}}
|
||||
<nav>
|
||||
<p>
|
||||
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
|
||||
<a>{{( pgettext "Tax Details" "title" )}}</a>
|
||||
</p>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.TaxDetailsPage*/ -}}
|
||||
<section class="dialog-content">
|
||||
<h2>{{(pgettext "Tax Details" "title")}}</h2>
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.TaxDetailsPage*/ -}}
|
||||
{{ with .DetailsForm }}
|
||||
<form id="details" method="POST">
|
||||
{{ csrfToken }}
|
||||
|
@ -32,10 +36,10 @@
|
|||
|
||||
{{ template "select-field" .Currency }}
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset>
|
||||
<legend>{{( pgettext "Invoicing" "title" )}}</legend>
|
||||
|
||||
|
||||
{{ template "input-field" .InvoiceNumberFormat }}
|
||||
{{ template "input-field" .LegalDisclaimer }}
|
||||
</fieldset>
|
||||
|
@ -129,7 +133,8 @@
|
|||
<form method="POST" action="{{ companyURI "/payment-method"}}/{{ .Id }}">
|
||||
{{ csrfToken }}
|
||||
{{ deleteMethod }}
|
||||
<button class="icon" aria-label="{{( gettext "Delete payment method" )}}" type="submit"><i
|
||||
<button class="icon" aria-label="{{( gettext "Delete payment method" )}}"
|
||||
type="submit"><i
|
||||
class="ri-delete-back-2-line"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
|
@ -154,7 +159,8 @@
|
|||
<tr>
|
||||
<td colspan="2"></td>
|
||||
<td colspan="2">
|
||||
<button form="new-payment-method" type="submit">{{( pgettext "Add new payment method" "action" )}}</button>
|
||||
<button form="new-payment-method"
|
||||
type="submit">{{( pgettext "Add new payment method" "action" )}}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
|
Loading…
Reference in New Issue