feat(admin): sortable form-help blocks with two-panel UI

- Migration 005: add sort_order column to form_help_blocks
- Database: getAllFormHelpBlocks orders by sort_order; new reorderFormHelpBlocks()
- actions/form-help-reorder.php: HTMX POST handler, CSRF-validated, 204 response
- templates/admin/contenus.php: replace flat table with two-panel layout
  - Left: SortableJS 1.15.2 + htmx drag-and-drop ordered block cards
  - Right: static form structure reference showing fieldsets and their inputs
- admin.css: .fhb-* styles for layout, cards, ghost/chosen/drag states, anchors
- schema.sql: updated form_help_blocks DDL with sort_order column
This commit is contained in:
Pontoporeia
2026-04-29 21:44:32 +02:00
parent 5c39e856a3
commit 43702542eb
7 changed files with 481 additions and 34 deletions

View File

@@ -2039,20 +2039,39 @@ class Database {
}
/**
* Return all form help blocks as [ key => ['content' => ..., 'updated_at' => ...] ].
* Return all form help blocks ordered by sort_order, as [ key => ['content' => ..., 'updated_at' => ..., 'sort_order' => ...] ].
*/
public function getAllFormHelpBlocks(): array {
$stmt = $this->pdo->query(
"SELECT key, content, updated_at FROM form_help_blocks ORDER BY key"
"SELECT key, content, updated_at, sort_order FROM form_help_blocks ORDER BY sort_order, key"
);
$rows = $stmt->fetchAll();
$out = [];
foreach ($rows as $r) {
$out[$r['key']] = ['content' => $r['content'], 'updated_at' => $r['updated_at']];
$out[$r['key']] = [
'content' => $r['content'],
'updated_at' => $r['updated_at'],
'sort_order' => (int)$r['sort_order'],
];
}
return $out;
}
/**
* Persist a new sort order for all form help blocks.
* $keys must be an ordered array of known block keys.
*/
public function reorderFormHelpBlocks(array $keys): void {
$stmt = $this->pdo->prepare(
"UPDATE form_help_blocks SET sort_order = ? WHERE key = ?"
);
foreach ($keys as $i => $key) {
if (in_array($key, self::FORM_HELP_KEYS, true)) {
$stmt->execute([$i, $key]);
}
}
}
// ========================================================================
// SINGLETON PATTERN ENFORCEMENT
// ========================================================================