From 5eece96700a41bc267305d1f5dfa3d6176902b43 Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Sun, 22 Mar 2026 19:06:48 +1100 Subject: [PATCH] feat(pages): add admin page list with drag-and-drop reorder Co-Authored-By: Claude Sonnet 4.6 --- assets/js/controllers/pagelist.js | 63 +++++++++++++++++++++++++++++++ assets/js/main.js | 4 +- views/_common/nav.html | 3 ++ views/pages/index.html | 35 +++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 assets/js/controllers/pagelist.js create mode 100644 views/pages/index.html diff --git a/assets/js/controllers/pagelist.js b/assets/js/controllers/pagelist.js new file mode 100644 index 0000000..7da6872 --- /dev/null +++ b/assets/js/controllers/pagelist.js @@ -0,0 +1,63 @@ +import { Controller } from "@hotwired/stimulus" +import { showToast } from "../services/toast"; + +export default class PagelistController extends Controller { + static values = { + siteId: Number, + }; + + static targets = ["list"]; + + dragStart(ev) { + this.draggedRow = ev.currentTarget; + ev.currentTarget.classList.add("opacity-50"); + ev.dataTransfer.effectAllowed = "move"; + } + + dragOver(ev) { + ev.preventDefault(); + ev.dataTransfer.dropEffect = "move"; + } + + drop(ev) { + ev.preventDefault(); + const targetRow = ev.currentTarget; + if (this.draggedRow && this.draggedRow !== targetRow) { + const rows = [...this.listTarget.children]; + const draggedIdx = rows.indexOf(this.draggedRow); + const targetIdx = rows.indexOf(targetRow); + if (draggedIdx < targetIdx) { + targetRow.after(this.draggedRow); + } else { + targetRow.before(this.draggedRow); + } + this.saveOrder(); + } + } + + dragEnd(ev) { + ev.currentTarget.classList.remove("opacity-50"); + this.draggedRow = null; + } + + async saveOrder() { + const rows = [...this.listTarget.children]; + const pageIds = rows.map(row => parseInt(row.dataset.pageId, 10)); + + try { + await fetch(`/sites/${this.siteIdValue}/pages/reorder`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Accept": "application/json", + }, + body: JSON.stringify({ page_ids: pageIds }), + }); + } catch (error) { + showToast({ + title: "Error", + body: "Failed to reorder pages.", + }); + } + } +} diff --git a/assets/js/main.js b/assets/js/main.js index d76c353..28451fb 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -7,6 +7,7 @@ import LogoutController from "./controllers/logout"; import FirstRunController from "./controllers/firstrun"; import UploadController from "./controllers/upload"; import ShowUploadController from "./controllers/show_upload"; +import PagelistController from "./controllers/pagelist"; window.Stimulus = Application.start() Stimulus.register("toast", ToastController); @@ -15,4 +16,5 @@ Stimulus.register("postedit", PosteditController); Stimulus.register("logout", LogoutController); Stimulus.register("first-run", FirstRunController); Stimulus.register("upload", UploadController); -Stimulus.register("show-upload", ShowUploadController); \ No newline at end of file +Stimulus.register("show-upload", ShowUploadController); +Stimulus.register("pagelist", PagelistController); \ No newline at end of file diff --git a/views/_common/nav.html b/views/_common/nav.html index e8bce30..ed7a1a9 100644 --- a/views/_common/nav.html +++ b/views/_common/nav.html @@ -13,6 +13,9 @@ + diff --git a/views/pages/index.html b/views/pages/index.html new file mode 100644 index 0000000..3011c64 --- /dev/null +++ b/views/pages/index.html @@ -0,0 +1,35 @@ +
+
+
+ New Page +
+
+ + {{ if .pages }} + + + + + + + + + + + {{ range .pages }} + + + + + + + {{ end }} + +
TitleSlugNav
{{ .Title }}{{ .Slug }}{{ if .ShowInNav }}Yes{{ end }}
+ {{ else }} +
+
No pages yet.
+
+ {{ end }} +