// Bootstrap helpers (() => { 'use strict'; // 1) Show Flask flash messages as Bootstrap toasts const flashes = document.getElementById('_flash_msgs'); if (flashes) { try{ const msgs = JSON.parse(flashes.dataset.msgs || "[]"); const stack = document.getElementById('toast-stack'); msgs.forEach((m) => { const el = document.createElement('div'); el.className = 'toast align-items-center text-bg-primary border-0'; el.role = 'alert'; el.ariaLive = 'assertive'; el.ariaAtomic = 'true'; el.innerHTML = `
${m}
`; stack.appendChild(el); const t = new bootstrap.Toast(el, { delay: 3500 }); t.show(); }); }catch(e){ console.warn('Toast parse error', e); } } // 2) Client-side validation (Bootstrap) const forms = document.querySelectorAll('form'); Array.from(forms).forEach(form => { form.addEventListener('submit', event => { if (!form.checkValidity()) { event.preventDefault(); event.stopPropagation(); } else { // 3) Micro-loader on submit const submitBtn = form.querySelector('[type="submit"]'); if (submitBtn && !submitBtn.dataset.loading) { submitBtn.dataset.loading = '1'; submitBtn.disabled = true; const original = submitBtn.innerHTML; submitBtn.dataset.original = original; submitBtn.innerHTML = ` Przetwarzanie...`; } } form.classList.add('was-validated'); }, false); }); // 4) Auto-accordion for long forms: // If a form contains multiple H2/H3 sections, group content between them. const targetForms = document.querySelectorAll('form'); targetForms.forEach(form => { const headers = Array.from(form.querySelectorAll(':scope h2, :scope h3')).filter(h => h.textContent.trim().length>0); if (headers.length >= 2) { const acc = document.createElement('div'); acc.className = 'accordion my-3'; acc.id = 'autoAccordion-' + Math.random().toString(36).slice(2); let startIdx = 0; headers.forEach((h, i) => { const itemId = acc.id + '-item-' + i; const headerId = itemId + '-hdr'; const bodyId = itemId + '-body'; const item = document.createElement('div'); item.className = 'accordion-item bg-body'; const btnText = h.textContent.trim(); // gather content until next header or end const chunk = []; let el = h.nextElementSibling; while (el && !headers.includes(el)) { chunk.push(el); el = el.nextElementSibling; } h.remove(); const bodyInner = document.createElement('div'); chunk.forEach(c => bodyInner.appendChild(c)); item.innerHTML = `

`; item.querySelector('.accordion-body').appendChild(bodyInner); acc.appendChild(item); }); // Insert at top of form form.prepend(acc); } }); // Focus first control const first = document.querySelector('form input, form select, form textarea'); if (first) first.focus({preventScroll:true}); })();