53 lines
1.4 KiB
JavaScript
53 lines
1.4 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
let csrfToken = '';
|
|
|
|
async function fetchCsrfToken() {
|
|
const res = await fetch('/api/csrf-token', { credentials: 'same-origin' });
|
|
if (!res.ok) throw new Error('Failed to fetch CSRF token');
|
|
const data = await res.json();
|
|
csrfToken = data.csrfToken;
|
|
return csrfToken;
|
|
}
|
|
|
|
function csrfHeaders(base = {}) {
|
|
return { ...base, 'x-csrf-token': csrfToken };
|
|
}
|
|
|
|
function showToast(message, type = 'success') {
|
|
const toast = document.createElement('div');
|
|
toast.className = `toast toast-${type}`;
|
|
toast.textContent = message;
|
|
document.getElementById('toast-container').appendChild(toast);
|
|
setTimeout(() => toast.remove(), 3500);
|
|
}
|
|
|
|
function formatLocalDateTime(d) {
|
|
const pad = n => String(n).padStart(2, '0');
|
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
}
|
|
|
|
const MOBILE_BREAKPOINT = 900;
|
|
|
|
function isMobileViewport() {
|
|
return window.innerWidth <= MOBILE_BREAKPOINT;
|
|
}
|
|
|
|
function setSidebarOpen(open) {
|
|
document.body.classList.toggle('sidebar-open', open);
|
|
const toggle = document.getElementById('menu-toggle');
|
|
if (toggle) toggle.setAttribute('aria-expanded', String(open));
|
|
}
|
|
|
|
window.Util = {
|
|
fetchCsrfToken,
|
|
csrfHeaders,
|
|
showToast,
|
|
formatLocalDateTime,
|
|
isMobileViewport,
|
|
setSidebarOpen,
|
|
MOBILE_BREAKPOINT
|
|
};
|
|
})();
|