mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
Contenus: add Mots-clés fieldset mirroring Langues, keep dedicated page button as backup, add Annuler cancel button to both bulk action bars, limit both table wraps to max-height:50vh with overflow scroll
This commit is contained in:
17
TODO.md
17
TODO.md
@@ -1,5 +1,13 @@
|
||||
# Current tasks
|
||||
|
||||
## Mots-clés fieldset in contenus
|
||||
- [x] Create contenus-motscles-fragment.php (tags fragment mirroring langues)
|
||||
- [x] Add Mots-clés fieldset to contenus template (search + table + bulk actions)
|
||||
- [x] Keep button linking to dedicated tags.php page as backup
|
||||
- [x] Add "Annuler" cancel button to both langues and mots-clés bulk action bars
|
||||
- [x] Add max-height:50vh + overflow-y:auto to both table wraps
|
||||
- [ ] Deploy: just deploy
|
||||
|
||||
## Tmp file cleanup (stale filepond + _trash)
|
||||
- [x] Session-based detection: check manifest session_id against PHP session files
|
||||
- [x] DB-based detection for _trash: check thesis_files row still exists
|
||||
@@ -10,6 +18,15 @@
|
||||
- [x] .gitignore: exclude tmp/filepond/* and tmp/_trash/*
|
||||
- [ ] Deploy: just deploy
|
||||
|
||||
## Index page improvements
|
||||
- [x] Remove 'Mots-clés' button from toolbar
|
||||
- [x] Move export to bulk selection bar (Exporter CSV + Exporter fichiers buttons)
|
||||
- [x] Export dispatcher accepts ?ids= for per-selection filtering
|
||||
- [x] All ExportController/Database export methods accept optional thesisIds
|
||||
- [x] Graceful error when ZipArchive extension missing (php8.4-zip not installed)
|
||||
- [x] Move DB export (SQLite download) to paramètres → Maintenance section
|
||||
- [x] Sticky thead on index table (position: sticky, top: 0, z-index: 5)
|
||||
|
||||
## Dialog & trash page margins
|
||||
- [x] Add admin-dialog__body CSS rule with padding + margin resets
|
||||
- [x] Add admin-dialog__stats + admin-dialog__hint classes
|
||||
|
||||
@@ -27,6 +27,10 @@ try {
|
||||
<div id="langues-bulk-actions" class="admin-bulk-actions" style="display:none">
|
||||
<strong><span id="langues-selected-count">0</span> langue(s) sélectionnée(s)</strong>
|
||||
<div class="admin-bulk-btns">
|
||||
<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>
|
||||
Annuler
|
||||
</button>
|
||||
<button type="button" class="btn btn--sm btn--warning admin-btn-merge"
|
||||
onclick="languesConfirmBulkMerge()"
|
||||
title="Fusionner les langues sélectionnées">
|
||||
|
||||
93
app/public/admin/contenus-motscles-fragment.php
Normal file
93
app/public/admin/contenus-motscles-fragment.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
/**
|
||||
* contenus-motscles-fragment.php
|
||||
*
|
||||
* HTMX fragment: returns the mots-clés table for the contenus page,
|
||||
* optionally filtered by a search query.
|
||||
*/
|
||||
require_once __DIR__ . '/../../bootstrap.php';
|
||||
require_once __DIR__ . '/../../src/AdminAuth.php';
|
||||
AdminAuth::requireLogin();
|
||||
|
||||
if (empty($_SESSION['csrf_token'])) {
|
||||
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../../src/Database.php';
|
||||
|
||||
$searchQuery = trim($_GET['q'] ?? '');
|
||||
|
||||
try {
|
||||
$db = new Database();
|
||||
$tags = ($searchQuery !== '') ? $db->searchTags($searchQuery) : $db->getAllTagsWithCount();
|
||||
} catch (Exception $e) {
|
||||
die('<div class="flash-error">Erreur : ' . htmlspecialchars($e->getMessage()) . '</div>');
|
||||
}
|
||||
?>
|
||||
<div id="motscles-bulk-actions" class="admin-bulk-actions" style="display:none">
|
||||
<strong><span id="motscles-selected-count">0</span> mot(s)-clé(s) sélectionné(s)</strong>
|
||||
<div class="admin-bulk-btns">
|
||||
<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>
|
||||
Annuler
|
||||
</button>
|
||||
<button type="button" class="btn btn--sm btn--warning admin-btn-merge"
|
||||
onclick="motsclesConfirmBulkMerge()"
|
||||
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>
|
||||
Fusionner
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="motscles-bulk-form" method="post" action="actions/tag.php">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
|
||||
<input type="hidden" name="action" value="merge_bulk">
|
||||
<input type="hidden" name="return" value="/admin/contenus.php">
|
||||
<input type="hidden" name="target_id" id="motscles-bulk-target" value="">
|
||||
<div id="motscles-bulk-checkboxes"></div>
|
||||
</form>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width:1%"><input type="checkbox" onchange="motsclesToggleAll(this)"></th>
|
||||
<th scope="col">Nom</th>
|
||||
<th scope="col" style="width:1%;white-space:nowrap">TFE Associé</th>
|
||||
<th scope="col" style="width:1%">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($tags)): ?>
|
||||
<tr><td colspan="4" class="admin-empty">Aucun mot-clé trouvé.</td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($tags as $tag): ?>
|
||||
<tr>
|
||||
<td style="width:1%"><input type="checkbox" name="selected_tags[]" value="<?= (int)$tag['id'] ?>" onchange="motsclesUpdateBulk()"></td>
|
||||
<td id="motscles-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="motsclesStartRename(<?= (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>
|
||||
</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">
|
||||
<input type="hidden" name="return" value="/admin/contenus.php">
|
||||
<input type="hidden" name="tag_id" value="<?= (int)$tag['id'] ?>">
|
||||
<button type="button" class="admin-icon-btn admin-icon-btn--delete" title="Supprimer"
|
||||
onclick="motsclesConfirmDelete(this, <?= htmlspecialchars(json_encode($tag['name']), ENT_QUOTES) ?>)">
|
||||
<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"></path></svg>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -364,3 +364,4 @@
|
||||
{"timestamp":"2026-05-18T15:20:07+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0","resource":"thesis","action":"edit","status":"success","context":{"thesis_id":1263,"title":"Pourquoi les artistes sont-ils encore sur Instagram alors que j’ai vu une story disant qu’il fallait quitter META"}}
|
||||
{"timestamp":"2026-05-19T14:39:37+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0","resource":"share_link","action":"deactivate","status":"success","context":{"link_id":213}}
|
||||
{"timestamp":"2026-05-19T14:39:39+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0","resource":"share_link","action":"activate","status":"success","context":{"link_id":213}}
|
||||
{"timestamp":"2026-05-19T17:19:51+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:150.0) Gecko/20100101 Firefox/150.0","resource":"system","action":"files_export","status":"success","context":{"file_count":6,"byte_size":11871654}}
|
||||
|
||||
@@ -53,8 +53,6 @@
|
||||
<section aria-labelledby="donnees-secondaires-title">
|
||||
<h2 id="donnees-secondaires-title">Données Secondaires</h2>
|
||||
|
||||
<p><a href="/admin/tags.php" class="btn btn--sm btn--primary">Gérer les mots-clés</a></p>
|
||||
|
||||
<fieldset>
|
||||
<legend>Langues</legend>
|
||||
|
||||
@@ -71,7 +69,34 @@
|
||||
<div id="langues-table-wrap"
|
||||
hx-get="/admin/contenus-langues-fragment.php"
|
||||
hx-trigger="load"
|
||||
hx-swap="innerHTML">
|
||||
hx-swap="innerHTML"
|
||||
style="max-height:50vh;overflow-y:auto">
|
||||
<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>
|
||||
|
||||
<p style="margin-top:var(--space-s)"><a href="/admin/tags.php" class="btn btn--sm btn--primary">Gérer les mots-clés (page dédiée)</a></p>
|
||||
|
||||
<fieldset>
|
||||
<legend>Mots-clés</legend>
|
||||
|
||||
<form id="motscles-search-form"
|
||||
hx-get="/admin/contenus-motscles-fragment.php"
|
||||
hx-target="#motscles-table-wrap"
|
||||
hx-swap="innerHTML"
|
||||
hx-trigger="input changed delay:200ms from:input[name=q], keyup[key=='Enter'] from:input[name=q]"
|
||||
hx-push-url="false"
|
||||
style="margin-bottom:var(--space-xs)">
|
||||
<input type="text" name="q" placeholder="Rechercher un mot-clé…" style="max-width:300px">
|
||||
</form>
|
||||
|
||||
<div id="motscles-table-wrap"
|
||||
hx-get="/admin/contenus-motscles-fragment.php"
|
||||
hx-trigger="load"
|
||||
hx-swap="innerHTML"
|
||||
style="max-height:50vh;overflow-y:auto">
|
||||
<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>
|
||||
@@ -349,6 +374,11 @@ function languesUpdateBulk() {
|
||||
document.getElementById('langues-bulk-actions').style.display = n > 1 ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
function languesCancelSelection() {
|
||||
document.querySelectorAll('input[name="selected_langs[]"]').forEach(cb => cb.checked = false);
|
||||
languesUpdateBulk();
|
||||
}
|
||||
|
||||
function languesConfirmBulkMerge() {
|
||||
const checked = document.querySelectorAll('input[name="selected_langs[]"]:checked');
|
||||
if (checked.length < 2) return;
|
||||
@@ -387,6 +417,137 @@ document.addEventListener('htmx:afterSwap', function(evt) {
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════════
|
||||
CONFIRM DIALOGS FOR MOTS-CLÉS
|
||||
══════════════════════════════════════════════════════════════ -->
|
||||
|
||||
<dialog id="motscles-delete-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="motscles-delete-title">
|
||||
<div class="admin-dialog__header">
|
||||
<h2 id="motscles-delete-title">Supprimer le mot-clé</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 « <strong id="motscles-delete-name"></strong> » ? Cette action est irréversible.</p>
|
||||
</div>
|
||||
<div class="admin-dialog__footer">
|
||||
<button type="button" class="btn btn--danger" onclick="this.closest('dialog').close(); motsclesSubmitPending()">Supprimer</button>
|
||||
<button type="button" class="btn btn--secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<dialog id="motscles-bulk-merge-dialog" class="admin-dialog admin-dialog--sm" aria-labelledby="motscles-bulk-merge-title">
|
||||
<div class="admin-dialog__header">
|
||||
<h2 id="motscles-bulk-merge-title">Fusionner 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>Fusionner <strong><span id="motscles-bulk-merge-count"></span> mot(s)-clé(s)</strong> sélectionné(s) dans :</p>
|
||||
<select id="motscles-bulk-merge-target-select" class="admin-select--inline" style="margin-top:var(--space-xs); width:100%" required>
|
||||
<option value="">— Choisir la destination —</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="admin-dialog__footer">
|
||||
<button type="button" class="btn btn--warning" onclick="motsclesExecBulkMerge()">Fusionner</button>
|
||||
<button type="button" class="btn btn--secondary" onclick="this.closest('dialog').close()">Annuler</button>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<script>
|
||||
let _motsclesPendingForm = null;
|
||||
|
||||
function motsclesConfirmDelete(btn, name) {
|
||||
_motsclesPendingForm = btn.closest('form');
|
||||
document.getElementById('motscles-delete-name').textContent = name;
|
||||
document.getElementById('motscles-delete-dialog').showModal();
|
||||
}
|
||||
|
||||
// ── Inline rename via HTMX ──────────────────────────────────────────────
|
||||
function motsclesStartRename(id) {
|
||||
var cell = document.getElementById('motscles-name-' + id);
|
||||
var csrf = document.querySelector('input[name="csrf_token"]').value;
|
||||
cell.innerHTML = '<form hx-post="/admin/actions/tag.php" hx-target="#motscles-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="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="motsclesCancelRename(' + 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 motsclesCancelRename(id) {
|
||||
var cell = document.getElementById('motscles-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="motsclesStartRename(' + 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 motsclesSubmitPending() {
|
||||
if (_motsclesPendingForm) _motsclesPendingForm.submit();
|
||||
}
|
||||
|
||||
function motsclesToggleAll(src) {
|
||||
document.querySelectorAll('input[name="selected_tags[]"]').forEach(cb => cb.checked = src.checked);
|
||||
motsclesUpdateBulk();
|
||||
}
|
||||
|
||||
function motsclesUpdateBulk() {
|
||||
const n = document.querySelectorAll('input[name="selected_tags[]"]:checked').length;
|
||||
document.getElementById('motscles-selected-count').textContent = n;
|
||||
document.getElementById('motscles-bulk-actions').style.display = n > 1 ? 'flex' : 'none';
|
||||
}
|
||||
|
||||
function motsclesConfirmBulkMerge() {
|
||||
const checked = document.querySelectorAll('input[name="selected_tags[]"]:checked');
|
||||
if (checked.length < 2) return;
|
||||
document.getElementById('motscles-bulk-merge-count').textContent = checked.length;
|
||||
const sel = document.getElementById('motscles-bulk-merge-target-select');
|
||||
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>';
|
||||
});
|
||||
document.getElementById('motscles-bulk-merge-dialog').showModal();
|
||||
}
|
||||
|
||||
function motsclesExecBulkMerge() {
|
||||
const targetId = document.getElementById('motscles-bulk-merge-target-select').value;
|
||||
if (!targetId) return;
|
||||
document.getElementById('motscles-bulk-target').value = targetId;
|
||||
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-merge-dialog').close();
|
||||
document.getElementById('motscles-bulk-form').submit();
|
||||
}
|
||||
|
||||
function motsclesCancelSelection() {
|
||||
document.querySelectorAll('input[name="selected_tags[]"]').forEach(cb => cb.checked = false);
|
||||
motsclesUpdateBulk();
|
||||
}
|
||||
|
||||
document.addEventListener('htmx:afterSwap', function(evt) {
|
||||
if (evt.target.id === 'motscles-table-wrap') {
|
||||
document.querySelectorAll('input[name="selected_tags[]"]').forEach(cb => cb.addEventListener('change', motsclesUpdateBulk));
|
||||
motsclesUpdateBulk();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var otScript = document.createElement('script');
|
||||
|
||||
Reference in New Issue
Block a user