diff --git a/assets/dk.css b/assets/dk.css index a6c0206..471bc23 100644 --- a/assets/dk.css +++ b/assets/dk.css @@ -391,3 +391,7 @@ div.modal { justify-content: center; } } + +.mt-1 { + margin-top: 1rem; +} diff --git a/assets/scripts/confirm_modal.js b/assets/scripts/confirm_modal.js new file mode 100644 index 0000000..47e9c07 --- /dev/null +++ b/assets/scripts/confirm_modal.js @@ -0,0 +1,62 @@ +class ConfirmModal { + constructor(options = {}) { + this.modal = document.querySelector(options.modalSelector || "#confirm-modal") + this.message = this.modal.querySelector(options.messageSelector || "#msg") + this.confirmBtn = this.modal.querySelector(options.confirmSelector || "#confirm") + this.cancelBtn = this.modal.querySelector(options.cancelSelector || "#cancel") + this.linkSelector = options.linkSelector || '.confirm-link' + this.messageGenerator = options.messageGenerator || this.defaultMessageGenerator.bind(this) + this.currentUrl = null + + if (options.confirmText) this.confirmBtn.textContent = options.confirmText + if (options.cancelText) this.cancelBtn.textContent = options.cancelText + + this.init() + } + + init() { + document.querySelectorAll(this.linkSelector).forEach(link => { + link.addEventListener('click', (e) => { + e.preventDefault() + const message = this.messageGenerator(link) + this.show(link.href, message) + }) + }) + + this.confirmBtn.addEventListener('click', () => this.confirm()) + this.cancelBtn.addEventListener('click', () => this.hide()) + + this.modal.addEventListener('click', (e) => { + if (e.target === this.modal) this.hide() + }) + + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && this.modal.style.display === 'block') { + this.hide() + } + }) + } + + defaultMessageGenerator(link) { + return link.dataset.message || 'Are you sure you want to continue?' + } + + show(url, message) { + this.currentUrl = url + this.message.textContent = message + this.modal.style.display = 'block' + this.confirmBtn.focus() + } + + hide() { + this.modal.style.display = 'none' + this.currentUrl = null + } + + confirm() { + if (this.currentUrl) { + window.location.href = this.currentUrl + } + this.hide() + } +} diff --git a/internal/components/page.go b/internal/components/page.go index 87644bb..6620c66 100644 --- a/internal/components/page.go +++ b/internal/components/page.go @@ -10,6 +10,7 @@ import ( "dk/internal/csrf" "dk/internal/middleware" "dk/internal/router" + "dk/internal/session" "dk/internal/template" ) @@ -24,6 +25,8 @@ func RenderPage(ctx router.Ctx, title, tmplPath string, additionalData map[strin return fmt.Errorf("failed to load layout template: %w", err) } + sess := ctx.UserValue("session").(*session.Session) + var m runtime.MemStats runtime.ReadMemStats(&m) @@ -36,6 +39,8 @@ func RenderPage(ctx router.Ctx, title, tmplPath string, additionalData map[strin "_build": "dev", "user": auth.GetCurrentUser(ctx), "_memalloc": m.Alloc / 1024 / 1024, + "_errormsg": sess.GetFlashMessage("error"), + "_successmsg": sess.GetFlashMessage("success"), } maps.Copy(data, LeftAside(ctx)) diff --git a/internal/components/topnav.go b/internal/components/topnav.go deleted file mode 100644 index 37a7dcf..0000000 --- a/internal/components/topnav.go +++ /dev/null @@ -1,23 +0,0 @@ -package components - -import ( - "dk/internal/auth" - "dk/internal/csrf" - "dk/internal/router" - "fmt" -) - -// GenerateTopNav generates the top navigation HTML based on authentication status -func GenerateTopNav(ctx router.Ctx) string { - if auth.IsAuthenticated(ctx) { - return fmt.Sprintf(`
-