mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
add sticky thead to index, langues, and mots-clés tables
This commit is contained in:
1
TODO.md
1
TODO.md
@@ -104,6 +104,7 @@
|
|||||||
|
|
||||||
# Current tasks
|
# Current tasks
|
||||||
|
|
||||||
|
- [x] Sticky thead: fix with border-collapse:separate, CSS class, --sticky-top var, +min-height:50vh on wrappers, +bulk delete for mots-clés
|
||||||
- [x] Edit submit redirects to recapitulatif instead of staying on edit.php
|
- [x] Edit submit redirects to recapitulatif instead of staying on edit.php
|
||||||
- [x] Mandatory auto-generated passwords on share links (no custom passwords, regenerate-only in edit, rate limit on password gate)
|
- [x] Mandatory auto-generated passwords on share links (no custom passwords, regenerate-only in edit, rate limit on password gate)
|
||||||
- [x] .gitignore / .ignore: exclude *.db-wal and *.db-shm
|
- [x] .gitignore / .ignore: exclude *.db-wal and *.db-shm
|
||||||
|
|||||||
@@ -54,6 +54,16 @@ try {
|
|||||||
$db->deleteLanguage($id);
|
$db->deleteLanguage($id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'delete_bulk':
|
||||||
|
$sourceIds = isset($_POST['selected_langs']) && is_array($_POST['selected_langs'])
|
||||||
|
? array_map('intval', $_POST['selected_langs'])
|
||||||
|
: [];
|
||||||
|
if (empty($sourceIds)) throw new Exception("Aucune langue sélectionnée.");
|
||||||
|
foreach ($sourceIds as $sid) {
|
||||||
|
$db->deleteLanguage($sid);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Exception("Action inconnue.");
|
throw new Exception("Action inconnue.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,17 @@ try {
|
|||||||
$logger->logTagAction('delete', ['tag_id' => $id]);
|
$logger->logTagAction('delete', ['tag_id' => $id]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'delete_bulk':
|
||||||
|
$sourceIds = isset($_POST['selected_tags']) && is_array($_POST['selected_tags'])
|
||||||
|
? array_map('intval', $_POST['selected_tags'])
|
||||||
|
: [];
|
||||||
|
if (empty($sourceIds)) throw new Exception("Aucun mot-clé sélectionné.");
|
||||||
|
foreach ($sourceIds as $sid) {
|
||||||
|
$db->deleteTag($sid);
|
||||||
|
$logger->logTagAction('delete', ['tag_id' => $sid]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Exception("Action inconnue.");
|
throw new Exception("Action inconnue.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,19 @@ try {
|
|||||||
die('<div class="flash-error">Erreur : ' . htmlspecialchars($e->getMessage()) . '</div>');
|
die('<div class="flash-error">Erreur : ' . htmlspecialchars($e->getMessage()) . '</div>');
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<div id="langues-bulk-actions" class="admin-bulk-actions" style="display:none">
|
<div id="langues-bulk-actions" class="admin-bulk-actions" style="display:none;position:sticky;top:0;z-index:10">
|
||||||
<strong><span id="langues-selected-count">0</span> langue(s) sélectionnée(s)</strong>
|
<strong><span id="langues-selected-count">0</span> langue(s) sélectionnée(s)</strong>
|
||||||
<div class="admin-bulk-btns">
|
<div class="admin-bulk-btns">
|
||||||
<button type="button" class="btn btn--sm btn--secondary" onclick="languesCancelSelection()" title="Annuler la sélection">
|
<button type="button" class="btn btn--sm btn--secondary" onclick="languesCancelSelection()" title="Annuler la sélection">
|
||||||
<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>
|
<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>
|
||||||
Annuler
|
Annuler
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" class="btn btn--sm btn--red admin-btn-delete"
|
||||||
|
onclick="languesConfirmBulkDelete()"
|
||||||
|
title="Supprimer les langues sélectionnées">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"/></svg>
|
||||||
|
Supprimer
|
||||||
|
</button>
|
||||||
<button type="button" class="btn btn--sm btn--warning admin-btn-merge"
|
<button type="button" class="btn btn--sm btn--warning admin-btn-merge"
|
||||||
onclick="languesConfirmBulkMerge()"
|
onclick="languesConfirmBulkMerge()"
|
||||||
title="Fusionner les langues sélectionnées">
|
title="Fusionner les langues sélectionnées">
|
||||||
@@ -48,7 +54,7 @@ try {
|
|||||||
<div id="langues-bulk-checkboxes"></div>
|
<div id="langues-bulk-checkboxes"></div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<table>
|
<table class="admin-table--sticky">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" style="width:1%"><input type="checkbox" onchange="languesToggleAll(this)"></th>
|
<th scope="col" style="width:1%"><input type="checkbox" onchange="languesToggleAll(this)"></th>
|
||||||
|
|||||||
@@ -24,17 +24,23 @@ try {
|
|||||||
die('<div class="flash-error">Erreur : ' . htmlspecialchars($e->getMessage()) . '</div>');
|
die('<div class="flash-error">Erreur : ' . htmlspecialchars($e->getMessage()) . '</div>');
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<div id="motscles-bulk-actions" class="admin-bulk-actions" style="display:none">
|
<div id="motscles-bulk-actions" class="admin-bulk-actions" style="display:none;position:sticky;top:0;z-index:10">
|
||||||
<strong><span id="motscles-selected-count">0</span> mot(s)-clé(s) sélectionné(s)</strong>
|
<strong><span id="motscles-selected-count">0</span> mot(s)-clé(s) sélectionné(s)</strong>
|
||||||
<div class="admin-bulk-btns">
|
<div class="admin-bulk-btns">
|
||||||
<button type="button" class="btn btn--sm btn--secondary" onclick="motsclesCancelSelection()" title="Annuler la sélection">
|
<button type="button" class="btn btn--sm btn--secondary" onclick="motsclesCancelSelection()" title="Annuler la sélection">
|
||||||
<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>
|
<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>
|
||||||
Annuler
|
Annuler
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" class="btn btn--sm btn--red admin-btn-delete"
|
||||||
|
onclick="motsclesConfirmBulkDelete()"
|
||||||
|
title="Supprimer les mots-clés sélectionnés">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"/></svg>
|
||||||
|
Supprimer
|
||||||
|
</button>
|
||||||
<button type="button" class="btn btn--sm btn--warning admin-btn-merge"
|
<button type="button" class="btn btn--sm btn--warning admin-btn-merge"
|
||||||
onclick="motsclesConfirmBulkMerge()"
|
onclick="motsclesConfirmBulkMerge()"
|
||||||
title="Fusionner les mots-clés sélectionnés">
|
title="Fusionner les mots-clés sélectionnés">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M224,152V96a8,8,0,0,0-8-8H168V40a8,8,0,0,0-8-8H40a8,8,0,0,0-8,8v64h0v56a8,8,0,0,0,8,8H88v48a8,8,0,0,0,8,8H216a8,8,0,0,0,8-8V152Zm-68.69,56L48,100.69V59.31L196.69,208Zm-96-160h41.38L208,155.31v41.38ZM208,132.69,179.31,104H208Zm-56-56L123.31,48H152ZM48,123.31,76.69,152H48Zm56,56L132.69,208H104Z"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256"><path d="M224,152V96a8,8,0,0,0-8-8H168V40a8,8,0,0,0-8-8H40a8,8,0,0,0-8,8v64h0v56a8,8,0,0,0,8,8H88v48a8,8,0,0,0,8,8H216a8,8,0,0,0,8-8V152Zm-68.69,56L48,100.69V59.31L196.69,208Zm-96-160h41.38L208,155.31v41.38ZM208,132.69,179.31,104H208Zm-56-56L123.31,48H152ZM48,123.31,76.69,152H48Zm56,56L132.69,208H104Z"/></svg>
|
||||||
Fusionner
|
Fusionner
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,7 +54,7 @@ try {
|
|||||||
<div id="motscles-bulk-checkboxes"></div>
|
<div id="motscles-bulk-checkboxes"></div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<table>
|
<table class="admin-table--sticky">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" style="width:1%"><input type="checkbox" onchange="motsclesToggleAll(this)"></th>
|
<th scope="col" style="width:1%"><input type="checkbox" onchange="motsclesToggleAll(this)"></th>
|
||||||
|
|||||||
@@ -74,16 +74,6 @@
|
|||||||
padding: 0 0 var(--space-2xl);
|
padding: 0 0 var(--space-2xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
#admin-table-wrap table thead {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#admin-table-wrap table thead th {
|
|
||||||
background: var(--bg-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.admin-body main > table tbody tr:nth-child(even) {
|
.admin-body main > table tbody tr:nth-child(even) {
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,3 +26,17 @@ td {
|
|||||||
border-bottom: 1px solid var(--border-primary);
|
border-bottom: 1px solid var(--border-primary);
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Sticky header — border-collapse:collapse blocks position:sticky in Chrome.
|
||||||
|
--sticky-top is set by JS when a bulk actions bar appears above the table. */
|
||||||
|
.admin-table--sticky {
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-table--sticky thead th {
|
||||||
|
position: sticky;
|
||||||
|
top: var(--sticky-top, 0px);
|
||||||
|
z-index: 5;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
hx-get="/admin/contenus-langues-fragment.php"
|
hx-get="/admin/contenus-langues-fragment.php"
|
||||||
hx-trigger="load"
|
hx-trigger="load"
|
||||||
hx-swap="innerHTML"
|
hx-swap="innerHTML"
|
||||||
style="max-height:50vh;overflow-y:auto">
|
style="min-height:50vh;max-height:50vh;overflow-y:auto">
|
||||||
<div style="padding:var(--space-m); text-align:center; color:var(--text-tertiary)">
|
<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">
|
<img alt="Chargement…" class="htmx-indicator" width="24" src="/assets/img/bars.svg" style="opacity:0.5">
|
||||||
</div>
|
</div>
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
hx-get="/admin/contenus-motscles-fragment.php"
|
hx-get="/admin/contenus-motscles-fragment.php"
|
||||||
hx-trigger="load"
|
hx-trigger="load"
|
||||||
hx-swap="innerHTML"
|
hx-swap="innerHTML"
|
||||||
style="max-height:50vh;overflow-y:auto">
|
style="min-height:50vh;max-height:50vh;overflow-y:auto">
|
||||||
<div style="padding:var(--space-m); text-align:center; color:var(--text-tertiary)">
|
<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">
|
<img alt="Chargement…" class="htmx-indicator" width="24" src="/assets/img/bars.svg" style="opacity:0.5">
|
||||||
</div>
|
</div>
|
||||||
@@ -323,6 +323,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="langues-bulk-delete-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="langues-bulk-delete-title">
|
||||||
|
<div class="admin-dialog__header">
|
||||||
|
<h2 id="langues-bulk-delete-title">Supprimer des langues</h2>
|
||||||
|
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||||
|
onclick="this.closest('dialog').close()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__alert">
|
||||||
|
<p>Supprimer définitivement <strong><span id="langues-bulk-delete-count"></span> langue(s)</strong> ? Cette action est irréversible.</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__footer">
|
||||||
|
<button type="button" class="btn btn--danger" onclick="languesExecBulkDelete()">Supprimer</button>
|
||||||
|
<button type="button" class="btn btn--secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let _languesPendingForm = null;
|
let _languesPendingForm = null;
|
||||||
|
|
||||||
@@ -371,7 +386,18 @@ function languesToggleAll(src) {
|
|||||||
function languesUpdateBulk() {
|
function languesUpdateBulk() {
|
||||||
const n = document.querySelectorAll('input[name="selected_langs[]"]:checked').length;
|
const n = document.querySelectorAll('input[name="selected_langs[]"]:checked').length;
|
||||||
document.getElementById('langues-selected-count').textContent = n;
|
document.getElementById('langues-selected-count').textContent = n;
|
||||||
document.getElementById('langues-bulk-actions').style.display = n > 1 ? 'flex' : 'none';
|
const bar = document.getElementById('langues-bulk-actions');
|
||||||
|
const wrap = document.getElementById('langues-table-wrap');
|
||||||
|
const visible = n > 1;
|
||||||
|
bar.style.display = visible ? 'flex' : 'none';
|
||||||
|
// Force reflow then read bar height for sticky th offset
|
||||||
|
if (visible) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
wrap.style.setProperty('--sticky-top', bar.offsetHeight + 'px');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
wrap.style.setProperty('--sticky-top', '0px');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function languesCancelSelection() {
|
function languesCancelSelection() {
|
||||||
@@ -379,6 +405,28 @@ function languesCancelSelection() {
|
|||||||
languesUpdateBulk();
|
languesUpdateBulk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function languesConfirmBulkDelete() {
|
||||||
|
const checked = document.querySelectorAll('input[name="selected_langs[]"]:checked');
|
||||||
|
if (checked.length < 1) return;
|
||||||
|
document.getElementById('langues-bulk-delete-count').textContent = checked.length;
|
||||||
|
document.getElementById('langues-bulk-delete-dialog').showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function languesExecBulkDelete() {
|
||||||
|
const container = document.getElementById('langues-bulk-checkboxes');
|
||||||
|
container.innerHTML = '';
|
||||||
|
document.querySelectorAll('input[name="selected_langs[]"]:checked').forEach(cb => {
|
||||||
|
const inp = document.createElement('input');
|
||||||
|
inp.type = 'hidden';
|
||||||
|
inp.name = 'selected_langs[]';
|
||||||
|
inp.value = cb.value;
|
||||||
|
container.appendChild(inp);
|
||||||
|
});
|
||||||
|
document.getElementById('langues-bulk-form').querySelector('input[name="action"]').value = 'delete_bulk';
|
||||||
|
document.getElementById('langues-bulk-delete-dialog').close();
|
||||||
|
document.getElementById('langues-bulk-form').submit();
|
||||||
|
}
|
||||||
|
|
||||||
function languesConfirmBulkMerge() {
|
function languesConfirmBulkMerge() {
|
||||||
const checked = document.querySelectorAll('input[name="selected_langs[]"]:checked');
|
const checked = document.querySelectorAll('input[name="selected_langs[]"]:checked');
|
||||||
if (checked.length < 2) return;
|
if (checked.length < 2) return;
|
||||||
@@ -454,6 +502,21 @@ document.addEventListener('htmx:afterSwap', function(evt) {
|
|||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="motscles-bulk-delete-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="motscles-bulk-delete-title">
|
||||||
|
<div class="admin-dialog__header">
|
||||||
|
<h2 id="motscles-bulk-delete-title">Supprimer des mots-clés</h2>
|
||||||
|
<button type="button" class="admin-dialog__close" aria-label="Fermer"
|
||||||
|
onclick="this.closest('dialog').close()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__alert">
|
||||||
|
<p>Supprimer définitivement <strong><span id="motscles-bulk-delete-count"></span> mot(s)-clé(s)</strong> ? Cette action est irréversible.</p>
|
||||||
|
</div>
|
||||||
|
<div class="admin-dialog__footer">
|
||||||
|
<button type="button" class="btn btn--danger" onclick="motsclesExecBulkDelete()">Supprimer</button>
|
||||||
|
<button type="button" class="btn btn--secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let _motsclesPendingForm = null;
|
let _motsclesPendingForm = null;
|
||||||
|
|
||||||
@@ -502,7 +565,18 @@ function motsclesToggleAll(src) {
|
|||||||
function motsclesUpdateBulk() {
|
function motsclesUpdateBulk() {
|
||||||
const n = document.querySelectorAll('input[name="selected_tags[]"]:checked').length;
|
const n = document.querySelectorAll('input[name="selected_tags[]"]:checked').length;
|
||||||
document.getElementById('motscles-selected-count').textContent = n;
|
document.getElementById('motscles-selected-count').textContent = n;
|
||||||
document.getElementById('motscles-bulk-actions').style.display = n > 1 ? 'flex' : 'none';
|
const bar = document.getElementById('motscles-bulk-actions');
|
||||||
|
const wrap = document.getElementById('motscles-table-wrap');
|
||||||
|
const visible = n > 1;
|
||||||
|
bar.style.display = visible ? 'flex' : 'none';
|
||||||
|
// Force reflow then read bar height for sticky th offset
|
||||||
|
if (visible) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
wrap.style.setProperty('--sticky-top', bar.offsetHeight + 'px');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
wrap.style.setProperty('--sticky-top', '0px');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function motsclesConfirmBulkMerge() {
|
function motsclesConfirmBulkMerge() {
|
||||||
@@ -540,6 +614,28 @@ function motsclesCancelSelection() {
|
|||||||
motsclesUpdateBulk();
|
motsclesUpdateBulk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function motsclesConfirmBulkDelete() {
|
||||||
|
const checked = document.querySelectorAll('input[name="selected_tags[]"]:checked');
|
||||||
|
if (checked.length < 1) return;
|
||||||
|
document.getElementById('motscles-bulk-delete-count').textContent = checked.length;
|
||||||
|
document.getElementById('motscles-bulk-delete-dialog').showModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function motsclesExecBulkDelete() {
|
||||||
|
const container = document.getElementById('motscles-bulk-checkboxes');
|
||||||
|
container.innerHTML = '';
|
||||||
|
document.querySelectorAll('input[name="selected_tags[]"]:checked').forEach(cb => {
|
||||||
|
const inp = document.createElement('input');
|
||||||
|
inp.type = 'hidden';
|
||||||
|
inp.name = 'selected_tags[]';
|
||||||
|
inp.value = cb.value;
|
||||||
|
container.appendChild(inp);
|
||||||
|
});
|
||||||
|
document.getElementById('motscles-bulk-form').querySelector('input[name="action"]').value = 'delete_bulk';
|
||||||
|
document.getElementById('motscles-bulk-delete-dialog').close();
|
||||||
|
document.getElementById('motscles-bulk-form').submit();
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('htmx:afterSwap', function(evt) {
|
document.addEventListener('htmx:afterSwap', function(evt) {
|
||||||
if (evt.target.id === 'motscles-table-wrap') {
|
if (evt.target.id === 'motscles-table-wrap') {
|
||||||
document.querySelectorAll('input[name="selected_tags[]"]').forEach(cb => cb.addEventListener('change', motsclesUpdateBulk));
|
document.querySelectorAll('input[name="selected_tags[]"]').forEach(cb => cb.addEventListener('change', motsclesUpdateBulk));
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ $sortArrow = function(string $col) use ($sortCol, $sortDir): string {
|
|||||||
<div id="bulk-checkboxes"></div>
|
<div id="bulk-checkboxes"></div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<table>
|
<table class="admin-table--sticky">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col"><input type="checkbox" onchange="toggleAll(this)"></th>
|
<th scope="col"><input type="checkbox" onchange="toggleAll(this)"></th>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
function toggleAll(src){document.querySelectorAll('input[name="selected_theses[]"]').forEach(cb=>cb.checked=src.checked);updateBulk();}
|
function toggleAll(src){document.querySelectorAll('input[name="selected_theses[]"]').forEach(cb=>cb.checked=src.checked);updateBulk();}
|
||||||
function updateBulk(){const n=document.querySelectorAll('input[name="selected_theses[]"]:checked').length;const b=document.getElementById('bulk-actions');document.getElementById('selected-count').textContent=n;b.style.display=n>0?'flex':'none';}
|
function updateBulk(){const n=document.querySelectorAll('input[name="selected_theses[]"]:checked').length;const b=document.getElementById('bulk-actions');document.getElementById('selected-count').textContent=n;b.style.display=n>0?'flex':'none';document.getElementById('admin-table-wrap').style.setProperty('--sticky-top',n>0?b.offsetHeight+'px':'0px');}
|
||||||
function getSelectedIds(){return Array.from(document.querySelectorAll('input[name="selected_theses[]"]:checked')).map(cb=>cb.value);}
|
function getSelectedIds(){return Array.from(document.querySelectorAll('input[name="selected_theses[]"]:checked')).map(cb=>cb.value);}
|
||||||
function confirmBulk(act){const ids=getSelectedIds();if(!ids.length){document.getElementById('no-selection-dialog').showModal();return;}const n=ids.length;document.getElementById('bulk-action-input').value=act;if(act==='delete'){document.getElementById('bulk-delete-count').textContent=n;document.getElementById('bulk-delete-dialog').showModal();}else{document.getElementById('bulk-confirm-word').textContent=act=='publish'?'Publier':'Dépublier';document.getElementById('bulk-confirm-count').textContent=n;document.getElementById('bulk-confirm-dialog').showModal();}}
|
function confirmBulk(act){const ids=getSelectedIds();if(!ids.length){document.getElementById('no-selection-dialog').showModal();return;}const n=ids.length;document.getElementById('bulk-action-input').value=act;if(act==='delete'){document.getElementById('bulk-delete-count').textContent=n;document.getElementById('bulk-delete-dialog').showModal();}else{document.getElementById('bulk-confirm-word').textContent=act=='publish'?'Publier':'Dépublier';document.getElementById('bulk-confirm-count').textContent=n;document.getElementById('bulk-confirm-dialog').showModal();}}
|
||||||
function execBulk(){const a=document.getElementById('bulk-action-input').value;const f=document.getElementById('bulk-form');f.action = a=='delete' ? 'actions/delete.php' : 'actions/publish.php';const c=document.getElementById('bulk-checkboxes');c.innerHTML='';getSelectedIds().forEach(id=>{const inp=document.createElement('input');inp.type='hidden';inp.name='selected_theses[]';inp.value=id;c.appendChild(inp);});f.submit();}
|
function execBulk(){const a=document.getElementById('bulk-action-input').value;const f=document.getElementById('bulk-form');f.action = a=='delete' ? 'actions/delete.php' : 'actions/publish.php';const c=document.getElementById('bulk-checkboxes');c.innerHTML='';getSelectedIds().forEach(id=>{const inp=document.createElement('input');inp.type='hidden';inp.name='selected_theses[]';inp.value=id;c.appendChild(inp);});f.submit();}
|
||||||
|
|||||||
Reference in New Issue
Block a user