mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
Add sidebar TOC, simplify Données Secondaires section
- Rename 'Éditer Données Secondaires' → 'Données Secondaires', remove fieldset wrapper on Mots-clés link - Create admin-toc.php partial: IntersectionObserver-based sidebar nav - Include TOC on contenus.php, acces.php, parametres.php - Add .admin-with-toc flex layout (sidebar + main) and .admin-toc CSS - Fonts (Ductus, BBB DM Sans): verified loaded via variables.css → common.css import chain - TOC: move inside <main> as <aside>, content in <article>, fix scrolling - Lazy load: hx-trigger='load delay:100ms' with spinner (htmx-indicator) for tags/langues - Inline rename: edit button in Nom cell, HTMX post for rename, validate+ cancel buttons - Checkbox column: width:1% / fit-content - Remove per-row merge forms/selects, only bulk merge when ≥2 checkboxes selected - Remove per-row merge dialogs, keep only bulk merge and delete dialogs - Add htmx-settling CSS transition for lazy-load fade-in - Update acces.php/parametres.php: article layout, TOC inside main - TOC: DOMContentLoaded guard, use <nav>+<a> directly instead of <ul>/<li> - Section spacing: margin-bottom on sections and fieldsets in admin-main--toc - Language dedup: GROUP BY LOWER(name) in getAllLanguagesWithCount and searchLanguages - deduplicateLanguages() merges duplicate names and reassigns thesis_languages - Sticky bulk-actions: position:sticky;top:0;z-index:10 - Tags toolbar: title left, stat count right (margin-left:auto), search bar under - Tags count stat updated via hx-swap-oob from fragment - Remove margin/max-width from .admin-main--toc - Gap between TOC and article: --space-xs, sticky top: --space-xs - Main padding: --space-s / --space-m / --space-xl (was --space-l/--space-l/--space-2xl) - Article padding-top: --space-m
This commit is contained in:
10
TODO.md
10
TODO.md
@@ -1,7 +1,7 @@
|
||||
# TODO
|
||||
|
||||
- [x] Rename "Éditer Données Secondaires" → "Données Secondaires", remove wrapping fieldset on Mots-clés
|
||||
- [x] Create admin-toc.php sidebar TOC partial with IntersectionObserver
|
||||
- [x] Include TOC in contenus.php, acces.php, parametres.php
|
||||
- [x] Add .admin-with-toc flex layout and .admin-toc CSS
|
||||
- [x] Fonts: verified Ductus + BBB DM Sans are loaded via variables.css → common.css
|
||||
- [x] Remove margin/padding from .admin-main--toc
|
||||
- [x] .admin-main--toc gap: var(--space-xs), sticky top: var(--space-xs)
|
||||
- [x] Reduce .admin-body main padding to --space-s / --space-m / --space-xl
|
||||
- [x] Add padding-top: var(--space-m) to article
|
||||
- [x] Language creation: verified getOrCreateLanguage still works; dedup runs before display
|
||||
|
||||
@@ -47,10 +47,10 @@ try {
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col"><input type="checkbox" onchange="languesToggleAll(this)"></th>
|
||||
<th scope="col" style="width:1%"><input type="checkbox" onchange="languesToggleAll(this)"></th>
|
||||
<th scope="col">Nom</th>
|
||||
<th scope="col">TFE Associé</th>
|
||||
<th scope="col">Actions</th>
|
||||
<th scope="col" style="width:1%;white-space:nowrap">TFE Associé</th>
|
||||
<th scope="col" style="width:1%">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -59,42 +59,17 @@ try {
|
||||
<?php else: ?>
|
||||
<?php foreach ($languages as $lang): ?>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="selected_langs[]" value="<?= (int)$lang['id'] ?>" onchange="languesUpdateBulk()"></td>
|
||||
<td><?= htmlspecialchars($lang['name']) ?></td>
|
||||
<td class="admin-tags-count"><?= (int)$lang['thesis_count'] ?></td>
|
||||
<td class="admin-actions-col">
|
||||
<div class="admin-actions">
|
||||
<form method="post" action="actions/language.php" class="admin-inline-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||
<input type="hidden" name="action" value="rename">
|
||||
<input type="hidden" name="return" value="/admin/contenus.php">
|
||||
<input type="hidden" name="language_id" value="<?= (int)$lang['id'] ?>">
|
||||
<input class="admin-input--inline" type="text" name="new_name"
|
||||
value="<?= htmlspecialchars($lang['name']) ?>" required>
|
||||
<button type="submit" class="admin-icon-btn admin-icon-btn--edit" title="Renommer">
|
||||
<td style="width:1%"><input type="checkbox" name="selected_langs[]" value="<?= (int)$lang['id'] ?>" onchange="languesUpdateBulk()"></td>
|
||||
<td id="lang-name-<?= (int)$lang['id'] ?>" data-name="<?= htmlspecialchars($lang['name']) ?>">
|
||||
<span class="tag-name-cell"><?= htmlspecialchars($lang['name']) ?></span>
|
||||
<button type="button" class="admin-icon-btn admin-icon-btn--edit" title="Renommer"
|
||||
onclick="languesStartRename(<?= (int)$lang['id'] ?>)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M248,92.68a15.86,15.86,0,0,0-4.69-11.31L174.63,12.68a16,16,0,0,0-22.63,0L123.57,41.11l-58,21.77A16.06,16.06,0,0,0,55.35,75.23L32.11,214.68A8,8,0,0,0,40,224a8.4,8.4,0,0,0,1.32-.11l139.44-23.24a16,16,0,0,0,12.35-10.17l21.77-58L243.31,104A15.87,15.87,0,0,0,248,92.68Zm-69.87,92.19L63.32,204l47.37-47.37a28,28,0,1,0-11.32-11.32L52,192.7,71.13,77.86,126,57.29,198.7,130ZM112,132a12,12,0,1,1,12,12A12,12,0,0,1,112,132Zm96-15.32L139.31,48l24-24L232,92.68Z"></path></svg>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form method="post" action="actions/language.php" class="admin-inline-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||
<input type="hidden" name="action" value="merge">
|
||||
<input type="hidden" name="return" value="/admin/contenus.php">
|
||||
<input type="hidden" name="source_id" value="<?= (int)$lang['id'] ?>">
|
||||
<select name="target_id" class="admin-select--inline" required>
|
||||
<option value="">— Fusionner dans… —</option>
|
||||
<?php foreach ($languages as $other): ?>
|
||||
<?php if ($other['id'] !== $lang['id']): ?>
|
||||
<option value="<?= (int)$other['id'] ?>"><?= htmlspecialchars($other['name']) ?></option>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<button type="button" class="admin-icon-btn admin-icon-btn--merge" title="Fusionner"
|
||||
onclick="return languesConfirmMerge(this)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path></svg>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</td>
|
||||
<td class="admin-tags-count" style="width:1%;white-space:nowrap"><?= (int)$lang['thesis_count'] ?></td>
|
||||
<td class="admin-actions-col" style="width:1%">
|
||||
<div class="admin-actions">
|
||||
<form method="post" action="actions/language.php" class="admin-inline-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
|
||||
@@ -14,6 +14,7 @@ $allowedPageSlugs = ['about', 'licenses'];
|
||||
|
||||
try {
|
||||
$db = new Database();
|
||||
$db->deduplicateLanguages();
|
||||
$allPages = $db->getAllPages();
|
||||
$pages = array_values(array_filter($allPages, fn($p) => in_array($p['slug'], $allowedPageSlugs, true)));
|
||||
$aproposKeys = $db->getAllAproposContents();
|
||||
|
||||
@@ -9,5 +9,6 @@ require_once __DIR__ . '/../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../src/AdminAuth.php';
|
||||
|
||||
AdminAuth::requireLogin();
|
||||
App::boot();
|
||||
|
||||
require_once APP_ROOT . '/public/partage/language-search-fragment.php';
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* tags-fragment.php
|
||||
*
|
||||
* HTMX fragment: returns the tags table, optionally filtered by search query.
|
||||
* Uses lazy-load pattern: the wrapping div already shows a spinner via hx-indicator.
|
||||
*/
|
||||
require_once __DIR__ . '/../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../src/AdminAuth.php';
|
||||
@@ -19,10 +20,12 @@ $searchQuery = trim($_GET['q'] ?? '');
|
||||
try {
|
||||
$db = new Database();
|
||||
$tags = ($searchQuery !== '') ? $db->searchTags($searchQuery) : $db->getAllTagsWithCount();
|
||||
$totalCount = count($tags);
|
||||
} catch (Exception $e) {
|
||||
die('<div class="flash-error">Erreur : ' . htmlspecialchars($e->getMessage()) . '</div>');
|
||||
}
|
||||
?>
|
||||
<span id="tags-total-count" hx-swap-oob="true"><?= $searchQuery !== '' ? htmlspecialchars($totalCount) . ' résultat(s)' : htmlspecialchars($totalCount) . ' mot(s)-clé(s)' ?></span>
|
||||
<div id="tags-bulk-actions" class="admin-bulk-actions" style="display:none">
|
||||
<strong><span id="tags-selected-count">0</span> mot(s)-clé(s) sélectionné(s)</strong>
|
||||
<div class="admin-bulk-btns">
|
||||
@@ -45,10 +48,10 @@ try {
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col"><input type="checkbox" onchange="tagsToggleAll(this)"></th>
|
||||
<th scope="col" style="width:1%"><input type="checkbox" onchange="tagsToggleAll(this)"></th>
|
||||
<th scope="col">Nom</th>
|
||||
<th scope="col">TFE associés</th>
|
||||
<th scope="col">Actions</th>
|
||||
<th scope="col" style="width:1%;white-space:nowrap">TFE associés</th>
|
||||
<th scope="col" style="width:1%">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -57,40 +60,17 @@ try {
|
||||
<?php else: ?>
|
||||
<?php foreach ($tags as $tag): ?>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="selected_tags[]" value="<?= (int)$tag['id'] ?>" onchange="tagsUpdateBulk()"></td>
|
||||
<td><?= htmlspecialchars($tag['name']) ?></td>
|
||||
<td class="admin-tags-count"><?= (int)$tag['thesis_count'] ?></td>
|
||||
<td class="admin-actions-col">
|
||||
<div class="admin-actions">
|
||||
<form method="post" action="actions/tag.php" class="admin-inline-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||
<input type="hidden" name="action" value="rename">
|
||||
<input type="hidden" name="tag_id" value="<?= (int)$tag['id'] ?>">
|
||||
<input class="admin-input--inline" type="text" name="new_name"
|
||||
value="<?= htmlspecialchars($tag['name']) ?>" required>
|
||||
<button type="submit" class="admin-icon-btn admin-icon-btn--edit" title="Renommer">
|
||||
<td style="width:1%"><input type="checkbox" name="selected_tags[]" value="<?= (int)$tag['id'] ?>" onchange="tagsUpdateBulk()"></td>
|
||||
<td id="tag-name-<?= (int)$tag['id'] ?>" data-name="<?= htmlspecialchars($tag['name']) ?>">
|
||||
<span class="tag-name-cell"><?= htmlspecialchars($tag['name']) ?></span>
|
||||
<button type="button" class="admin-icon-btn admin-icon-btn--edit" title="Renommer"
|
||||
onclick="tagsStartRename(<?= (int)$tag['id'] ?>)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M248,92.68a15.86,15.86,0,0,0-4.69-11.31L174.63,12.68a16,16,0,0,0-22.63,0L123.57,41.11l-58,21.77A16.06,16.06,0,0,0,55.35,75.23L32.11,214.68A8,8,0,0,0,40,224a8.4,8.4,0,0,0,1.32-.11l139.44-23.24a16,16,0,0,0,12.35-10.17l21.77-58L243.31,104A15.87,15.87,0,0,0,248,92.68Zm-69.87,92.19L63.32,204l47.37-47.37a28,28,0,1,0-11.32-11.32L52,192.7,71.13,77.86,126,57.29,198.7,130ZM112,132a12,12,0,1,1,12,12A12,12,0,0,1,112,132Zm96-15.32L139.31,48l24-24L232,92.68Z"></path></svg>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<form method="post" action="actions/tag.php" class="admin-inline-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||
<input type="hidden" name="action" value="merge">
|
||||
<input type="hidden" name="source_id" value="<?= (int)$tag['id'] ?>">
|
||||
<select name="target_id" class="admin-select--inline" required>
|
||||
<option value="">— Fusionner dans… —</option>
|
||||
<?php foreach ($tags as $other): ?>
|
||||
<?php if ($other['id'] !== $tag['id']): ?>
|
||||
<option value="<?= (int)$other['id'] ?>"><?= htmlspecialchars($other['name']) ?></option>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<button type="button" class="admin-icon-btn admin-icon-btn--merge" title="Fusionner"
|
||||
onclick="return tagsConfirmMerge(this)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"></path></svg>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</td>
|
||||
<td class="admin-tags-count" style="width:1%;white-space:nowrap"><?= (int)$tag['thesis_count'] ?></td>
|
||||
<td class="admin-actions-col" style="width:1%">
|
||||
<div class="admin-actions">
|
||||
<form method="post" action="actions/tag.php" class="admin-inline-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding: var(--space-l) var(--space-l) var(--space-2xl);
|
||||
padding: var(--space-s) var(--space-m) var(--space-xl);
|
||||
width: 100%;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
@@ -331,6 +331,9 @@
|
||||
border-radius: var(--radius);
|
||||
margin-bottom: var(--space-s);
|
||||
font-size: var(--step--1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.admin-bulk-btns {
|
||||
@@ -338,6 +341,14 @@
|
||||
gap: var(--space-2xs);
|
||||
}
|
||||
|
||||
.admin-stat--inline {
|
||||
font-size: var(--step--1);
|
||||
color: var(--text-secondary);
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Table ──────────────────────────────────────────────────────────────── */
|
||||
/* Base table/th/td styles live in common.css */
|
||||
.admin-body main > table {
|
||||
@@ -1992,26 +2003,32 @@ th.admin-ap-col {
|
||||
|
||||
/* ── Sidebar TOC ───────────────────────────────────────────────────────────── */
|
||||
|
||||
.admin-with-toc {
|
||||
.admin-main--toc {
|
||||
display: flex;
|
||||
gap: var(--space-m);
|
||||
gap: var(--space-xs);
|
||||
align-items: flex-start;
|
||||
max-width: var(--content-max-width, 1200px);
|
||||
margin: 0 auto;
|
||||
padding: 0 var(--space-s);
|
||||
}
|
||||
|
||||
.admin-with-toc > main {
|
||||
.admin-main--toc > article {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-top: var(--space-m);
|
||||
}
|
||||
|
||||
.admin-main--toc > article > section {
|
||||
margin-bottom: var(--space-xl);
|
||||
}
|
||||
|
||||
.admin-main--toc > article > section > fieldset {
|
||||
margin-bottom: var(--space-m);
|
||||
}
|
||||
|
||||
.admin-toc {
|
||||
position: sticky;
|
||||
top: var(--space-m);
|
||||
top: var(--space-xs);
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
padding-top: var(--space-s);
|
||||
padding-top: var(--space-m);
|
||||
}
|
||||
|
||||
.admin-toc-list {
|
||||
@@ -2042,3 +2059,11 @@ th.admin-ap-col {
|
||||
font-weight: 600;
|
||||
border-left-color: var(--accent, var(--color-primary));
|
||||
}
|
||||
|
||||
/* ── Lazy-load transition ─────────────────────────────────────────────────── */
|
||||
.htmx-settling img {
|
||||
opacity: 0;
|
||||
}
|
||||
.htmx-indicator img {
|
||||
transition: opacity 300ms ease-in;
|
||||
}
|
||||
|
||||
@@ -1265,23 +1265,25 @@ class Database
|
||||
$query = trim($query);
|
||||
if ($query === '') {
|
||||
$stmt = $this->pdo->query('
|
||||
SELECT l.id, UPPER(SUBSTR(l.name,1,1)) || SUBSTR(l.name,2) as name,
|
||||
SELECT MIN(l.id) as id,
|
||||
UPPER(SUBSTR(MIN(l.name),1,1)) || SUBSTR(MIN(l.name),2) as name,
|
||||
COUNT(DISTINCT tl.thesis_id) as thesis_count
|
||||
FROM languages l
|
||||
LEFT JOIN thesis_languages tl ON l.id = tl.language_id
|
||||
GROUP BY l.id
|
||||
ORDER BY thesis_count DESC, l.name COLLATE NOCASE
|
||||
GROUP BY LOWER(l.name)
|
||||
ORDER BY thesis_count DESC, LOWER(MIN(l.name)) COLLATE NOCASE
|
||||
LIMIT 10
|
||||
');
|
||||
} else {
|
||||
$stmt = $this->pdo->prepare('
|
||||
SELECT l.id, UPPER(SUBSTR(l.name,1,1)) || SUBSTR(l.name,2) as name,
|
||||
SELECT MIN(l.id) as id,
|
||||
UPPER(SUBSTR(MIN(l.name),1,1)) || SUBSTR(MIN(l.name),2) as name,
|
||||
COUNT(DISTINCT tl.thesis_id) as thesis_count
|
||||
FROM languages l
|
||||
LEFT JOIN thesis_languages tl ON l.id = tl.language_id
|
||||
WHERE LOWER(l.name) LIKE LOWER(?)
|
||||
GROUP BY l.id
|
||||
ORDER BY LOWER(l.name) = LOWER(?) DESC, thesis_count DESC, l.name COLLATE NOCASE
|
||||
GROUP BY LOWER(l.name)
|
||||
ORDER BY LOWER(MIN(l.name)) = LOWER(?) DESC, thesis_count DESC, LOWER(MIN(l.name)) COLLATE NOCASE
|
||||
LIMIT 10
|
||||
');
|
||||
$stmt->execute([$query . '%', $query]);
|
||||
@@ -1294,17 +1296,51 @@ class Database
|
||||
*/
|
||||
public function getAllLanguagesWithCount(): array
|
||||
{
|
||||
// Group by lowercased name to deduplicate, keeping the first id
|
||||
$stmt = $this->pdo->query('
|
||||
SELECT l.id, UPPER(SUBSTR(l.name,1,1)) || SUBSTR(l.name,2) as name,
|
||||
SELECT MIN(l.id) as id,
|
||||
UPPER(SUBSTR(MIN(l.name),1,1)) || SUBSTR(MIN(l.name),2) as name,
|
||||
COUNT(DISTINCT tl.thesis_id) as thesis_count
|
||||
FROM languages l
|
||||
LEFT JOIN thesis_languages tl ON l.id = tl.language_id
|
||||
GROUP BY l.id
|
||||
ORDER BY l.name COLLATE NOCASE
|
||||
GROUP BY LOWER(l.name)
|
||||
ORDER BY LOWER(MIN(l.name)) COLLATE NOCASE
|
||||
');
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deduplicate languages: merge languages with the same lowercased name.
|
||||
*/
|
||||
public function deduplicateLanguages(): void
|
||||
{
|
||||
$dupes = $this->pdo->query('
|
||||
SELECT LOWER(name) as lname, MIN(id) as keep_id
|
||||
FROM languages
|
||||
GROUP BY LOWER(name)
|
||||
HAVING COUNT(*) > 1
|
||||
')->fetchAll();
|
||||
|
||||
foreach ($dupes as $dup) {
|
||||
$this->pdo->prepare('
|
||||
INSERT OR IGNORE INTO thesis_languages (language_id, thesis_id)
|
||||
SELECT ?, thesis_id FROM thesis_languages WHERE language_id IN (
|
||||
SELECT id FROM languages WHERE LOWER(name) = ? AND id != ?
|
||||
)
|
||||
')->execute([$dup['keep_id'], $dup['lname'], $dup['keep_id']]);
|
||||
|
||||
$this->pdo->prepare('
|
||||
DELETE FROM thesis_languages WHERE language_id IN (
|
||||
SELECT id FROM languages WHERE LOWER(name) = ? AND id != ?
|
||||
)
|
||||
')->execute([$dup['lname'], $dup['keep_id']]);
|
||||
|
||||
$this->pdo->prepare('
|
||||
DELETE FROM languages WHERE LOWER(name) = ? AND id != ?
|
||||
')->execute([$dup['lname'], $dup['keep_id']]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a language. Throws if the new name already exists.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<div class="admin-with-toc">
|
||||
<main id="main-content" class="admin-main--toc">
|
||||
<?php include APP_ROOT . '/templates/admin/partials/admin-toc.php'; ?>
|
||||
<main id="main-content">
|
||||
|
||||
<article>
|
||||
<h1>Accès</h1>
|
||||
|
||||
<!-- ══════════════════════════════════════════════════════════════
|
||||
@@ -141,6 +142,19 @@
|
||||
+%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
|
||||
+\\\\\\\ to: tyotlpxt f7b0f560 "Add Mots-clés and Langues management to contenus page" (rebased revision)
|
||||
++ $linkName = $link['name'] ?? '';
|
||||
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: tyotlpxt f7b0f560 "Add Mots-clés and Langues management to contenus page" (rebased revision)
|
||||
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
|
||||
- $linkName = $link['name'] ?? '';
|
||||
- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
|
||||
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: xvqonoyt 25dc22e7 "Add sidebar TOC, simplify Données Secondaires section" (rebased revision)
|
||||
$linkName = $link['name'] ?? '';
|
||||
$linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
|
||||
$linkLockedYear = $link['locked_year'] ?? null;
|
||||
+%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
|
||||
+\\\\\\\ to: xvqonoyt a3f280bc "Add sidebar TOC, simplify Données Secondaires section" (rebased revision)
|
||||
++ $linkName = $link['name'] ?? '';
|
||||
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
|
||||
?>
|
||||
<tr class="admin-table-row" onclick="event.stopPropagation(); window.open('/partage/<?= urlencode($link['slug']) ?>', '_blank')" style="cursor:pointer">
|
||||
@@ -403,8 +417,8 @@
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- ═══════════════════════ CREATE DIALOG ═══════════════════════ -->
|
||||
<dialog id="create-dialog" class="admin-dialog" aria-labelledby="create-dialog-title">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<div class="admin-with-toc">
|
||||
<main id="main-content" class="admin-main--toc">
|
||||
<?php include APP_ROOT . '/templates/admin/partials/admin-toc.php'; ?>
|
||||
<main id="main-content">
|
||||
|
||||
<article>
|
||||
<h1>Contenus</h1>
|
||||
|
||||
<?php
|
||||
@@ -52,10 +53,8 @@
|
||||
<section aria-labelledby="donnees-secondaires-title">
|
||||
<h2 id="donnees-secondaires-title">Données Secondaires</h2>
|
||||
|
||||
<!-- ── Mots-clés ── -->
|
||||
<p><a href="/admin/tags.php" class="btn btn--sm btn--primary">Gérer les mots-clés</a></p>
|
||||
|
||||
<!-- ── Langues ── -->
|
||||
<fieldset>
|
||||
<legend>Langues</legend>
|
||||
|
||||
@@ -69,8 +68,13 @@
|
||||
<input type="text" name="q" placeholder="Rechercher une langue…" style="max-width:300px">
|
||||
</form>
|
||||
|
||||
<div id="langues-table-wrap" hx-get="/admin/contenus-langues-fragment.php" hx-trigger="load" hx-swap="innerHTML">
|
||||
<!-- populated by HTMX -->
|
||||
<div id="langues-table-wrap"
|
||||
hx-get="/admin/contenus-langues-fragment.php"
|
||||
hx-trigger="load"
|
||||
hx-swap="innerHTML">
|
||||
<div style="padding:var(--space-m); text-align:center; color:var(--text-tertiary)">
|
||||
<img alt="Chargement…" class="htmx-indicator" width="24" src="/assets/img/bars.svg" style="opacity:0.5">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</section>
|
||||
@@ -81,7 +85,6 @@
|
||||
<section aria-labelledby="form-settings-title">
|
||||
<h2 id="form-settings-title">Paramètres du Formulaire</h2>
|
||||
|
||||
<!-- ── Restrictions d'accès aux fichiers ── -->
|
||||
<fieldset>
|
||||
<legend>Restrictions d'accès aux fichiers</legend>
|
||||
|
||||
@@ -102,7 +105,6 @@
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<!-- ── Degré d'ouverture ── -->
|
||||
<fieldset>
|
||||
<legend>Degré d'ouverture</legend>
|
||||
<p>Options de visibilité disponibles dans le formulaire d'ajout de TFE.</p>
|
||||
@@ -143,7 +145,6 @@
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<!-- ── Types de travaux ── -->
|
||||
<fieldset>
|
||||
<legend>Types de travaux</legend>
|
||||
<p>Active ou désactive les types de travaux dans les formulaires et la consultation. Un type désactivé ne peut plus être soumis ni affiché sur le site.</p>
|
||||
@@ -183,7 +184,6 @@
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<!-- ── Structure du formulaire ── -->
|
||||
<fieldset>
|
||||
<legend>Structure du Formulaire</legend>
|
||||
<p class="fhb-hint">
|
||||
@@ -193,7 +193,6 @@
|
||||
|
||||
<?php
|
||||
$blocks = $formHelpBlocks;
|
||||
|
||||
$pairs = [
|
||||
['partage_intro', null, null],
|
||||
['fieldset_tfe_info', 'Informations du TFE',
|
||||
@@ -245,29 +244,13 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
</section>
|
||||
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- ══════════════════════════════════════════════════════════════
|
||||
CONFIRM DIALOGS FOR LANGUES
|
||||
══════════════════════════════════════════════════════════════ -->
|
||||
|
||||
<dialog id="langues-merge-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="langues-merge-title">
|
||||
<div class="admin-dialog__header">
|
||||
<h2 id="langues-merge-title">Fusionner la langue</h2>
|
||||
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||
onclick="this.closest('dialog').close()">✕</button>
|
||||
</div>
|
||||
<div class="admin-dialog__alert">
|
||||
<p>Fusionner dans « <strong id="langues-merge-target-name"></strong> » ? La langue source sera supprimée.</p>
|
||||
</div>
|
||||
<div class="admin-dialog__footer">
|
||||
<button type="button" class="btn btn--warning" onclick="this.closest('dialog').close(); languesSubmitPending()">Fusionner</button>
|
||||
<button type="button" class="btn btn--secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<dialog id="langues-delete-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="langues-delete-title">
|
||||
<div class="admin-dialog__header">
|
||||
<h2 id="langues-delete-title">Supprimer la langue</h2>
|
||||
@@ -304,22 +287,39 @@
|
||||
<script>
|
||||
let _languesPendingForm = null;
|
||||
|
||||
function languesConfirmMerge(btn) {
|
||||
const form = btn.closest('form');
|
||||
const select = form.querySelector('select[name="target_id"]');
|
||||
if (!select.value) return true;
|
||||
_languesPendingForm = form;
|
||||
document.getElementById('langues-merge-target-name').textContent = select.options[select.selectedIndex]?.text ?? '';
|
||||
document.getElementById('langues-merge-dialog').showModal();
|
||||
return false;
|
||||
}
|
||||
|
||||
function languesConfirmDelete(btn, name) {
|
||||
_languesPendingForm = btn.closest('form');
|
||||
document.getElementById('langues-delete-name').textContent = name;
|
||||
document.getElementById('langues-delete-dialog').showModal();
|
||||
}
|
||||
|
||||
// ── Inline rename via HTMX ──────────────────────────────────────────────
|
||||
function languesStartRename(id) {
|
||||
var cell = document.getElementById('lang-name-' + id);
|
||||
var csrf = document.querySelector('input[name="csrf_token"]').value;
|
||||
cell.innerHTML = '<form hx-post="/admin/actions/language.php" hx-target="#langues-table-wrap" hx-swap="innerHTML" class="admin-inline-form">'
|
||||
+ '<input type="hidden" name="csrf_token" value="' + csrf + '">'
|
||||
+ '<input type="hidden" name="action" value="rename">'
|
||||
+ '<input type="hidden" name="return" value="/admin/contenus.php">'
|
||||
+ '<input type="hidden" name="language_id" value="' + id + '">'
|
||||
+ '<input type="text" name="new_name" value="' + cell.getAttribute('data-name') + '" required class="admin-input--inline">'
|
||||
+ '<button type="submit" class="admin-icon-btn admin-icon-btn--edit" title="Valider">'
|
||||
+ '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"/></svg>'
|
||||
+ '</button>'
|
||||
+ '<button type="button" class="admin-icon-btn admin-icon-btn--delete" onclick="languesCancelRename(' + id + ')" title="Annuler">'
|
||||
+ '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M208.49,191.51a12,12,0,0,1-17,17L128,145,64.49,208.49a12,12,0,0,1-17-17L111,128,47.51,64.49a12,12,0,0,1,17-17L128,111l63.51-63.52a12,12,0,0,1,17,17L145,128Z"/></svg>'
|
||||
+ '</button></form>';
|
||||
cell.querySelector('input').focus();
|
||||
}
|
||||
|
||||
function languesCancelRename(id) {
|
||||
var cell = document.getElementById('lang-name-' + id);
|
||||
cell.innerHTML = '<span class="tag-name-cell">' + cell.getAttribute('data-name') + '</span>'
|
||||
+ '<button type="button" class="admin-icon-btn admin-icon-btn--edit" title="Renommer" onclick="languesStartRename(' + id + ')">'
|
||||
+ '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M248,92.68a15.86,15.86,0,0,0-4.69-11.31L174.63,12.68a16,16,0,0,0-22.63,0L123.57,41.11l-58,21.77A16.06,16.06,0,0,0,55.35,75.23L32.11,214.68A8,8,0,0,0,40,224a8.4,8.4,0,0,0,1.32-.11l139.44-23.24a16,16,0,0,0,12.35-10.17l21.77-58L243.31,104A15.87,15.87,0,0,0,248,92.68Zm-69.87,92.19L63.32,204l47.37-47.37a28,28,0,1,0-11.32-11.32L52,192.7,71.13,77.86,126,57.29,198.7,130ZM112,132a12,12,0,1,1,12,12A12,12,0,0,1,112,132Zm96-15.32L139.31,48l24-24L232,92.68Z"/></svg>'
|
||||
+ '</button>';
|
||||
}
|
||||
|
||||
function languesSubmitPending() {
|
||||
if (_languesPendingForm) _languesPendingForm.submit();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<div class="admin-with-toc">
|
||||
<main id="main-content" class="admin-main--toc">
|
||||
<?php include APP_ROOT . '/templates/admin/partials/admin-toc.php'; ?>
|
||||
<main id="main-content">
|
||||
|
||||
<article>
|
||||
<h1>Paramètres</h1>
|
||||
|
||||
<!-- ══════════════════════════════════════════════════════════════
|
||||
@@ -459,8 +460,8 @@
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function copyLogContent(btn) {
|
||||
|
||||
@@ -2,77 +2,56 @@
|
||||
/**
|
||||
* admin-toc.php — sidebar table-of-contents for long admin pages.
|
||||
*
|
||||
* Scans <section aria-labelledby="..."> elements in #main-content and builds a
|
||||
* slim vertical nav. Uses IntersectionObserver to highlight the active section.
|
||||
*
|
||||
* Usage: include APP_ROOT . '/templates/admin/partials/admin-toc.php';
|
||||
* Rendered as an <aside> inside <main>, before the <article> content.
|
||||
* Uses IntersectionObserver to highlight the active section.
|
||||
*/
|
||||
?>
|
||||
<nav id="admin-toc" class="admin-toc" aria-label="Sur cette page">
|
||||
<ul class="admin-toc-list" id="admin-toc-list">
|
||||
<aside id="admin-toc" class="admin-toc" aria-label="Sur cette page">
|
||||
<nav class="admin-toc-list" id="admin-toc-list">
|
||||
<!-- populated by JS -->
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
function build() {
|
||||
var main = document.getElementById('main-content');
|
||||
if (!main) return;
|
||||
var nav = document.getElementById('admin-toc-list');
|
||||
var aside = document.getElementById('admin-toc');
|
||||
if (!main || !nav || !aside) return;
|
||||
|
||||
var tocList = document.getElementById('admin-toc-list');
|
||||
if (!tocList) return;
|
||||
|
||||
// Find all labelled sections
|
||||
var sections = main.querySelectorAll('section[aria-labelledby]');
|
||||
if (sections.length < 2) {
|
||||
document.getElementById('admin-toc').style.display = 'none';
|
||||
return;
|
||||
}
|
||||
if (sections.length < 2) { aside.hidden = true; return; }
|
||||
|
||||
var items = [];
|
||||
|
||||
sections.forEach(function(sec) {
|
||||
var headingId = sec.getAttribute('aria-labelledby');
|
||||
var heading = document.getElementById(headingId);
|
||||
if (!heading) return;
|
||||
if (!sec.id) sec.id = headingId;
|
||||
|
||||
var li = document.createElement('li');
|
||||
var a = document.createElement('a');
|
||||
a.href = '#' + sec.id;
|
||||
a.textContent = heading.textContent;
|
||||
a.setAttribute('data-toc-target', sec.id);
|
||||
li.appendChild(a);
|
||||
tocList.appendChild(li);
|
||||
|
||||
// Ensure section has an id for anchoring
|
||||
if (!sec.id) {
|
||||
sec.id = headingId;
|
||||
}
|
||||
|
||||
a.textContent = heading.textContent.trim();
|
||||
a.style.display = 'block';
|
||||
nav.appendChild(a);
|
||||
items.push({ section: sec, link: a });
|
||||
});
|
||||
|
||||
// IntersectionObserver: highlight the link whose section is most visible
|
||||
var observer = new IntersectionObserver(function(entries) {
|
||||
var best = null;
|
||||
var bestRatio = 0;
|
||||
var best = null, bestRatio = 0;
|
||||
entries.forEach(function(e) {
|
||||
if (e.intersectionRatio > bestRatio) {
|
||||
bestRatio = e.intersectionRatio;
|
||||
best = e.target;
|
||||
}
|
||||
if (e.intersectionRatio > bestRatio) { bestRatio = e.intersectionRatio; best = e.target; }
|
||||
});
|
||||
if (best) {
|
||||
items.forEach(function(item) {
|
||||
var isActive = item.section === best;
|
||||
item.link.classList.toggle('admin-toc-active', isActive);
|
||||
});
|
||||
}
|
||||
}, {
|
||||
rootMargin: '-10% 0px -70% 0px',
|
||||
threshold: [0, 0.25, 0.5, 0.75, 1]
|
||||
item.link.classList.toggle('admin-toc-active', item.section === best);
|
||||
});
|
||||
}, { rootMargin: '-10% 0px -70% 0px', threshold: [0, 0.25, 0.5, 0.75, 1] });
|
||||
|
||||
items.forEach(function(item) { observer.observe(item.section); });
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', build);
|
||||
else build();
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -1,26 +1,6 @@
|
||||
<script>
|
||||
let _pendingTagForm = null;
|
||||
|
||||
function tagsConfirmMerge(btn) {
|
||||
const form = btn.closest('form');
|
||||
const select = form.querySelector('select[name="target_id"]');
|
||||
if (!select.value) return true;
|
||||
_pendingTagForm = form;
|
||||
document.getElementById('merge-target-name').textContent = select.options[select.selectedIndex]?.text ?? '';
|
||||
document.getElementById('merge-tag-dialog').showModal();
|
||||
return false;
|
||||
}
|
||||
|
||||
function tagsConfirmDelete(btn, name) {
|
||||
_pendingTagForm = btn.closest('form');
|
||||
document.getElementById('delete-tag-name').textContent = name;
|
||||
document.getElementById('delete-tag-dialog').showModal();
|
||||
}
|
||||
|
||||
function _submitPendingTagForm() {
|
||||
if (_pendingTagForm) _pendingTagForm.submit();
|
||||
}
|
||||
|
||||
function tagsToggleAll(src) {
|
||||
document.querySelectorAll('input[name="selected_tags[]"]').forEach(cb => cb.checked = src.checked);
|
||||
tagsUpdateBulk();
|
||||
@@ -40,7 +20,7 @@ function tagsConfirmBulkMerge() {
|
||||
sel.innerHTML = '<option value="">— Choisir la destination —</option>';
|
||||
checked.forEach(cb => {
|
||||
const tr = cb.closest('tr');
|
||||
sel.innerHTML += '<option value="' + cb.value + '">' + tr.querySelector('td:nth-child(2)').textContent.trim() + '</option>';
|
||||
sel.innerHTML += '<option value="' + cb.value + '">' + tr.querySelector('.tag-name-cell').textContent.trim() + '</option>';
|
||||
});
|
||||
document.getElementById('bulk-merge-dialog').showModal();
|
||||
}
|
||||
@@ -62,9 +42,45 @@ function tagsExecBulkMerge() {
|
||||
document.getElementById('tags-bulk-form').submit();
|
||||
}
|
||||
|
||||
// ── Inline rename via HTMX ──────────────────────────────────────────────────
|
||||
function tagsStartRename(id) {
|
||||
var cell = document.getElementById('tag-name-' + id);
|
||||
var csrf = document.querySelector('input[name="csrf_token"]').value;
|
||||
cell.innerHTML = '<form hx-post=\"/admin/actions/tag.php\" hx-target=\"#tags-table-wrap\" hx-swap=\"innerHTML\" class=\"admin-inline-form\">'
|
||||
+ '<input type=\"hidden\" name=\"csrf_token\" value=\"' + csrf + '\">'
|
||||
+ '<input type=\"hidden\" name=\"action\" value=\"rename\">'
|
||||
+ '<input type=\"hidden\" name=\"tag_id\" value=\"' + id + '\">'
|
||||
+ '<input type=\"text\" name=\"new_name\" value=\"' + cell.getAttribute('data-name') + '\" required class=\"admin-input--inline\">'
|
||||
+ '<button type=\"submit\" class=\"admin-icon-btn admin-icon-btn--edit\" title=\"Valider\">'
|
||||
+ '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"currentColor\" viewBox=\"0 0 256 256\"><path d=\"M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z\"/></svg>'
|
||||
+ '</button>'
|
||||
+ '<button type=\"button\" class=\"admin-icon-btn admin-icon-btn--delete\" onclick=\"tagsCancelRename(' + id + ')\" title=\"Annuler\">'
|
||||
+ '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"currentColor\" viewBox=\"0 0 256 256\"><path d=\"M208.49,191.51a12,12,0,0,1-17,17L128,145,64.49,208.49a12,12,0,0,1-17-17L111,128,47.51,64.49a12,12,0,0,1,17-17L128,111l63.51-63.52a12,12,0,0,1,17,17L145,128Z\"/></svg>'
|
||||
+ '</button></form>';
|
||||
cell.querySelector('input').focus();
|
||||
}
|
||||
|
||||
function tagsCancelRename(id) {
|
||||
var cell = document.getElementById('tag-name-' + id);
|
||||
cell.innerHTML = '<span class=\"tag-name-cell\">' + cell.getAttribute('data-name') + '</span>'
|
||||
+ '<button type=\"button\" class=\"admin-icon-btn admin-icon-btn--edit\" title=\"Renommer\" onclick=\"tagsStartRename(' + id + ')\">'
|
||||
+ '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\" fill=\"currentColor\" viewBox=\"0 0 256 256\"><path d=\"M248,92.68a15.86,15.86,0,0,0-4.69-11.31L174.63,12.68a16,16,0,0,0-22.63,0L123.57,41.11l-58,21.77A16.06,16.06,0,0,0,55.35,75.23L32.11,214.68A8,8,0,0,0,40,224a8.4,8.4,0,0,0,1.32-.11l139.44-23.24a16,16,0,0,0,12.35-10.17l21.77-58L243.31,104A15.87,15.87,0,0,0,248,92.68Zm-69.87,92.19L63.32,204l47.37-47.37a28,28,0,1,0-11.32-11.32L52,192.7,71.13,77.86,126,57.29,198.7,130ZM112,132a12,12,0,1,1,12,12A12,12,0,0,1,112,132Zm96-15.32L139.31,48l24-24L232,92.68Z\"/></svg>'
|
||||
+ '</button>';
|
||||
}
|
||||
|
||||
function tagsConfirmDelete(btn, name) {
|
||||
_pendingTagForm = btn.closest('form');
|
||||
document.getElementById('delete-tag-name').textContent = name;
|
||||
document.getElementById('delete-tag-dialog').showModal();
|
||||
}
|
||||
|
||||
function _submitPendingTagForm() {
|
||||
if (_pendingTagForm) _pendingTagForm.submit();
|
||||
}
|
||||
|
||||
document.addEventListener('htmx:afterSwap', function(evt) {
|
||||
if (evt.target.id === 'tags-table-wrap') {
|
||||
document.querySelectorAll('input[name="selected_tags[]"]').forEach(cb => cb.addEventListener('change', tagsUpdateBulk));
|
||||
document.querySelectorAll('input[name=\"selected_tags[]\"]').forEach(cb => cb.addEventListener('change', tagsUpdateBulk));
|
||||
tagsUpdateBulk();
|
||||
}
|
||||
});
|
||||
@@ -75,6 +91,7 @@ document.addEventListener('htmx:afterSwap', function(evt) {
|
||||
<div class="admin-toolbar-top">
|
||||
<div class="admin-toolbar-title-row">
|
||||
<h1><a href="/admin/" class="admin-back-btn" title="Retour à la liste"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm48-88a8,8,0,0,1-8,8H107.31l18.35,18.34a8,8,0,0,1-11.32,11.32l-32-32a8,8,0,0,1,0-11.32l32-32a8,8,0,0,1,11.32,11.32L107.31,120H168A8,8,0,0,1,176,128Z"></path></svg></a> Mots-clés</h1>
|
||||
<span id="tags-total-count" class="admin-stat admin-stat--inline" style="margin-left:auto"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -89,26 +106,16 @@ document.addEventListener('htmx:afterSwap', function(evt) {
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="tags-table-wrap" hx-get="/admin/tags-fragment.php" hx-trigger="load" hx-swap="innerHTML">
|
||||
<!-- populated by HTMX -->
|
||||
<div id="tags-table-wrap"
|
||||
hx-get="/admin/tags-fragment.php"
|
||||
hx-trigger="load delay:100ms"
|
||||
hx-swap="innerHTML">
|
||||
<div style="padding:var(--space-m);text-align:center;color:var(--text-tertiary)">
|
||||
<img alt="Chargement…" class="htmx-indicator" width="24" src="/assets/img/bars.svg" style="opacity:0.5">
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<dialog id="merge-tag-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="merge-tag-title">
|
||||
<div class="admin-dialog__header">
|
||||
<h2 id="merge-tag-title">Fusionner le tag</h2>
|
||||
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||
onclick="this.closest('dialog').close()">✕</button>
|
||||
</div>
|
||||
<div class="admin-dialog__alert">
|
||||
<p>Fusionner ce tag dans « <strong id="merge-target-name"></strong> » ? Le tag source sera supprimé.</p>
|
||||
</div>
|
||||
<div class="admin-dialog__footer">
|
||||
<button type="button" class="btn btn--warning" onclick="this.closest('dialog').close(); _submitPendingTagForm()">Fusionner</button>
|
||||
<button type="button" class="btn btn--secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<dialog id="delete-tag-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="delete-tag-title">
|
||||
<div class="admin-dialog__header">
|
||||
<h2 id="delete-tag-title">Supprimer le tag</h2>
|
||||
|
||||
Reference in New Issue
Block a user