diff --git a/blueprints/community/classifieds/routes.py b/blueprints/community/classifieds/routes.py index 948515b..a203f07 100644 --- a/blueprints/community/classifieds/routes.py +++ b/blueprints/community/classifieds/routes.py @@ -116,6 +116,18 @@ def new(): ).first(): form_company_id = current_user.company_id + # Deduplikacja double/triple-click: jeśli ten sam autor+firma+tytuł + # wpadły w ostatnich 60 sekundach — traktuj jako powtórkę przesyłu formularza. + recent_duplicate = db.query(Classified).filter( + Classified.author_id == current_user.id, + Classified.company_id == form_company_id, + Classified.title == title, + Classified.created_at >= datetime.now() - timedelta(seconds=60), + ).order_by(Classified.created_at.desc()).first() + if recent_duplicate: + flash('To ogłoszenie właśnie dodano — wyświetlamy istniejące.', 'info') + return redirect(url_for('.classifieds_view', classified_id=recent_duplicate.id)) + classified = Classified( author_id=current_user.id, company_id=form_company_id, diff --git a/templates/admin/announcements_form.html b/templates/admin/announcements_form.html index ff068b0..0406270 100755 --- a/templates/admin/announcements_form.html +++ b/templates/admin/announcements_form.html @@ -169,7 +169,7 @@ {% endif %}
-
+ @@ -301,6 +301,24 @@ {% endblock %} {% block extra_js %} + // Anty double/triple-click — blokada wielokrotnego submitu + (function() { + const form = document.getElementById('announcementForm'); + if (!form) return; + form.addEventListener('submit', function(e) { + const btns = form.querySelectorAll('button[type="submit"]'); + if (Array.from(btns).some(function(b) { return b.disabled; })) { + e.preventDefault(); + return; + } + btns.forEach(function(b) { + b.dataset.originalText = b.textContent.trim(); + b.disabled = true; + b.textContent = 'Wysyłanie...'; + }); + }); + })(); + // Character counter for excerpt const excerptTextarea = document.getElementById('excerpt'); const excerptCounter = document.getElementById('excerpt-counter'); diff --git a/templates/board/meeting_form.html b/templates/board/meeting_form.html index 5aae377..70b93ab 100644 --- a/templates/board/meeting_form.html +++ b/templates/board/meeting_form.html @@ -723,6 +723,24 @@ {% endblock %} {% block extra_js %} +// Anty double/triple-click — blokada wielokrotnego submitu +(function() { + const form = document.getElementById('meetingForm'); + if (!form) return; + form.addEventListener('submit', function(e) { + const btns = form.querySelectorAll('button[type="submit"]'); + if (Array.from(btns).some(function(b) { return b.disabled; })) { + e.preventDefault(); + return; + } + btns.forEach(function(b) { + b.dataset.originalText = b.innerHTML; + b.disabled = true; + b.textContent = 'Wysyłanie...'; + }); + }); +})(); + // Tab switching document.querySelectorAll('.form-tab').forEach(tab => { tab.addEventListener('click', function() { diff --git a/templates/classifieds/new.html b/templates/classifieds/new.html index 81df2f2..2330a68 100755 --- a/templates/classifieds/new.html +++ b/templates/classifieds/new.html @@ -435,7 +435,13 @@ var quill = new Quill('#quill-editor', { // their green check immediately on page load. refreshTitle(); refreshCat(); refreshRadios(); refreshDesc(); + var submitBtn = document.getElementById('submitBtn'); document.getElementById('classifiedForm').addEventListener('submit', function(e) { + // Anty double/triple-click — jeśli już wysyłamy, blokuj kolejny submit + if (submitBtn && submitBtn.disabled) { + e.preventDefault(); + return; + } var html = quill.root.innerHTML; var empty = (html === '


' || quill.getText().trim() === ''); if (empty) { @@ -447,6 +453,10 @@ var quill = new Quill('#quill-editor', { } qc && qc.classList.remove('field-error'); document.getElementById('description').value = html; + if (submitBtn) { + submitBtn.disabled = true; + submitBtn.textContent = 'Wysyłanie...'; + } }); })(); @@ -565,7 +575,11 @@ var quill = new Quill('#quill-editor', { filesMap.forEach(file => formData.append('attachments[]', file)); fetch(form.action, { method: 'POST', body: formData }) .then(resp => { if (resp.redirected) { window.location.href = resp.url; } else { window.location.reload(); } }) - .catch(() => alert('Błąd wysyłania')); + .catch(() => { + alert('Błąd wysyłania'); + var btn = document.getElementById('submitBtn'); + if (btn) { btn.disabled = false; btn.textContent = 'Dodaj ogłoszenie'; } + }); }); function formatFileSize(bytes) {