feat(pages): add admin page list with drag-and-drop reorder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f386403ced
commit
5eece96700
63
assets/js/controllers/pagelist.js
Normal file
63
assets/js/controllers/pagelist.js
Normal file
|
|
@ -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.",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -16,3 +17,4 @@ Stimulus.register("logout", LogoutController);
|
|||
Stimulus.register("first-run", FirstRunController);
|
||||
Stimulus.register("upload", UploadController);
|
||||
Stimulus.register("show-upload", ShowUploadController);
|
||||
Stimulus.register("pagelist", PagelistController);
|
||||
|
|
@ -13,6 +13,9 @@
|
|||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="/sites/{{.site.ID}}/categories">Categories</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="/sites/{{.site.ID}}/pages">Pages</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="/sites/{{.site.ID}}/uploads">Uploads</a>
|
||||
</li>
|
||||
|
|
|
|||
35
views/pages/index.html
Normal file
35
views/pages/index.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<main class="container">
|
||||
<div class="my-4 d-flex justify-content-between align-items-baseline">
|
||||
<div>
|
||||
<a href="/sites/{{ .site.ID }}/pages/new" class="btn btn-success">New Page</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ if .pages }}
|
||||
<table class="table" data-controller="pagelist" data-pagelist-site-id-value="{{ .site.ID }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 2rem;"></th>
|
||||
<th>Title</th>
|
||||
<th>Slug</th>
|
||||
<th>Nav</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-pagelist-target="list">
|
||||
{{ range .pages }}
|
||||
<tr draggable="true" data-page-id="{{ .ID }}"
|
||||
data-action="dragstart->pagelist#dragStart dragover->pagelist#dragOver drop->pagelist#drop dragend->pagelist#dragEnd">
|
||||
<td class="text-muted" style="cursor: grab;">☰</td>
|
||||
<td><a href="/sites/{{ $.site.ID }}/pages/{{ .ID }}">{{ .Title }}</a></td>
|
||||
<td><code>{{ .Slug }}</code></td>
|
||||
<td>{{ if .ShowInNav }}Yes{{ end }}</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ else }}
|
||||
<div class="h4 m-3 text-center">
|
||||
<div class="position-absolute top-50 start-50 translate-middle">No pages yet.</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</main>
|
||||
Loading…
Reference in a new issue