Add autosave draft system for partage form with HTMX-based session persistence

- New fragment endpoint POST/GET /partage/fragments/draft.php:
  saves all form fields to PHP session, excludes file/csrf/slug fields
  GET returns JSON for JS hydration on page load
  rotates both global CSRF and share CSRF tokens in sync

- form.php accepts optional $formExtraAttrs and $showAutosaveStatus:
  allows injecting HTMX attributes and 'Brouillon enregistré' indicator

- renderShareLinkForm adds hx-post with change/input debounce trigger,
  loads autosave-handler.js, hydrate fields from draft on page load

- Draft cleared on successful form submission in handleShareLinkSubmission

- autosave-handler.js now also updates share_link_token hidden input
  when rotating CSRF token (partage form uses both csrf_token and share_link_token)

- Added .autosave-status CSS to form.css (was admin.css-only)

- Updated fragment routing to accept GET requests (needed for draft hydration)
This commit is contained in:
Pontoporeia
2026-06-11 10:32:53 +02:00
parent 4b37a05be3
commit 99125cc8e3
33 changed files with 1388 additions and 806 deletions

View File

@@ -8,31 +8,32 @@
* Or with a custom selector pattern:
* <button onclick="copyUrlFrom(document.getElementById('my-url'))">Copier</button>
*/
(function () {
'use strict';
(() => {
window.copyUrl = (id) => {
var input = document.getElementById(`url-${id}`);
if (input) {
window.copyUrlFrom(input);
}
};
window.copyUrl = function (id) {
var input = document.getElementById('url-' + id);
if (input) {
window.copyUrlFrom(input);
}
};
window.copyUrlFrom = function (sourceEl) {
var text = sourceEl.value || sourceEl.textContent || '';
if (!text) return;
navigator.clipboard.writeText(text).then(function () {
var btn = window.event && window.event.target ? window.event.target.closest('button') : null;
if (btn) {
var origTitle = btn.getAttribute('title') || '';
var origText = btn.textContent;
btn.setAttribute('title', '\u2713 Copi\u00e9');
btn.textContent = '\u2713 Copi\u00e9';
setTimeout(function () {
btn.setAttribute('title', origTitle);
btn.textContent = origText;
}, 1200);
}
});
};
window.copyUrlFrom = (sourceEl) => {
var text = sourceEl.value || sourceEl.textContent || "";
var btn;
var origTitle;
var origText;
if (!text) return;
navigator.clipboard.writeText(text).then(() => {
btn = window.event?.target ? window.event.target.closest("button") : null;
if (btn) {
origTitle = btn.getAttribute("title") || "";
origText = btn.textContent;
btn.setAttribute("title", "\u2713 Copi\u00e9");
btn.textContent = "\u2713 Copi\u00e9";
setTimeout(() => {
btn.setAttribute("title", origTitle);
btn.textContent = origText;
}, 1200);
}
});
};
})();