{% extends "base.html" %} {% block title %}{{ classified.title }} - Norda Biznes Partner{% endblock %} {% block extra_css %} {% endblock %} {% block content %}
Powrot do tablicy
{{ 'Szukam' if classified.listing_type == 'szukam' else 'Oferuje' }} {{ classified.category|replace('uslugi', 'Usługi')|replace('produkty', 'Produkty')|replace('wspolpraca', 'Współpraca')|replace('praca', 'Praca')|replace('inne', 'Inne')|replace('nieruchomosci', 'Nieruchomości') }} {% if not classified.is_active %} Nieaktywne {% endif %}
{% if classified.author_id == current_user.id %} {% endif %} {% if current_user.is_authenticated and current_user.can_access_admin_panel() %}
{% endif %}

{{ classified.title }}

{{ classified.description }}
{% if classified.budget_info or classified.location_info %}
{% if classified.budget_info %}
Budzet / Cena
{{ classified.budget_info }}
{% endif %} {% if classified.location_info %}
Lokalizacja
{{ classified.location_info }}
{% endif %}
{% endif %}
{{ (classified.author.name or classified.author.email)[0].upper() }}
{{ classified.author.name or classified.author.email.split('@')[0] }}
{% if classified.company %}
{{ classified.company.name }}
{% endif %}
{% if classified.author_id != current_user.id %}
Skontaktuj sie
{% endif %}
{% if classified.author_id != current_user.id and interests_count > 0 %}
{{ interests_count }} {{ 'osoba zainteresowana' if interests_count == 1 else 'osoby zainteresowane' if interests_count < 5 else 'osob zainteresowanych' }}
{% endif %} {% if classified.author_id == current_user.id and interests_count > 0 %} {% endif %}
{{ classified.views_count }} wyswietlen Dodano: {{ classified.created_at|local_time('%d.%m.%Y %H:%M') }} {% if classified.expires_at %} Wygasa: {{ classified.expires_at|local_time('%d.%m.%Y') }} {% endif %}
{% if readers %}
Widziane przez {{ readers_count }} {{ 'osobę' if readers_count == 1 else 'osoby' if readers_count < 5 else 'osób' }}:
{% for read in readers[:20] %}
{{ (read.user.name or read.user.email)[0]|upper }}
{% endfor %} {% if readers_count > 20 %}
+{{ readers_count - 20 }}
{% endif %}
{% endif %}

Pytania i odpowiedzi {% if classified.author_id == current_user.id and unanswered_count > 0 %} {{ unanswered_count }} nowych {% endif %}

{% if classified.author_id != current_user.id %}
{% endif %}
{% if questions %} {% for q in questions %}
{{ (q.author.name or q.author.email)[0]|upper }}
{{ q.author.name or q.author.email.split('@')[0] }} {% if q.author.company %} - {{ q.author.company.name }}{% endif %} {% if not q.answer %}Oczekuje na odpowiedz{% endif %}
{{ q.created_at|local_time('%d.%m.%Y %H:%M') }}
{% if classified.author_id == current_user.id %}
{% endif %}
{{ q.content }}
{% if q.answer %}
Odpowiedz od {{ classified.author.name or classified.author.email.split('@')[0] }}
{{ q.answer }}
{% elif classified.author_id == current_user.id %}
{% endif %}
{% endfor %} {% else %}
Brak pytan. {% if classified.author_id != current_user.id %}Badz pierwszy i zadaj pytanie!{% endif %}
{% endif %}
{% endblock %} {% block extra_js %} const csrfToken = '{{ csrf_token() }}'; let confirmResolve = null; function showConfirm(message, options = {}) { return new Promise(resolve => { confirmResolve = resolve; document.getElementById('confirmModalIcon').textContent = options.icon || '❓'; document.getElementById('confirmModalTitle').textContent = options.title || 'Potwierdzenie'; document.getElementById('confirmModalMessage').innerHTML = message; document.getElementById('confirmModalOk').textContent = options.okText || 'OK'; document.getElementById('confirmModalOk').className = 'btn ' + (options.okClass || 'btn-primary'); document.getElementById('confirmModal').classList.add('active'); }); } function closeConfirm(result) { document.getElementById('confirmModal').classList.remove('active'); if (confirmResolve) { confirmResolve(result); confirmResolve = null; } } document.getElementById('confirmModalOk').addEventListener('click', () => closeConfirm(true)); document.getElementById('confirmModalCancel').addEventListener('click', () => closeConfirm(false)); document.getElementById('confirmModal').addEventListener('click', e => { if (e.target.id === 'confirmModal') closeConfirm(false); }); function showToast(message, type = 'info', duration = 4000) { const container = document.getElementById('toastContainer'); const icons = { success: '✓', error: '✕', warning: '⚠', info: 'ℹ' }; const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.innerHTML = `${icons[type]||'ℹ'}${message}`; container.appendChild(toast); setTimeout(() => { toast.style.animation = 'toastOut 0.3s ease forwards'; setTimeout(() => toast.remove(), 300); }, duration); } async function closeClassified() { const confirmed = await showConfirm('Czy na pewno chcesz zamknąć to ogłoszenie?', { icon: '🔒', title: 'Zamykanie ogłoszenia', okText: 'Zamknij', okClass: 'btn-warning' }); if (!confirmed) return; try { const response = await fetch('{{ url_for("classifieds.classifieds_close", classified_id=classified.id) }}', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken } }); const data = await response.json(); if (data.success) { showToast('Ogłoszenie zostało zamknięte', 'success'); setTimeout(() => window.location.href = '{{ url_for("classifieds.classifieds_index") }}', 1500); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } // Admin functions async function deleteClassified() { const confirmed = await showConfirm('Czy na pewno chcesz usunąć to ogłoszenie?

Ta operacja jest nieodwracalna.', { icon: '🗑️', title: 'Usuń ogłoszenie', okText: 'Usuń', okClass: 'btn-danger' }); if (!confirmed) return; try { const response = await fetch('{{ url_for("classifieds.classifieds_delete", classified_id=classified.id) }}', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken } }); const data = await response.json(); if (data.success) { showToast('Ogłoszenie usunięte', 'success'); setTimeout(() => window.location.href = '{{ url_for("classifieds.classifieds_index") }}', 1500); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } async function toggleActive() { const isActive = {{ 'true' if classified.is_active else 'false' }}; const action = isActive ? 'dezaktywować' : 'aktywować'; const confirmed = await showConfirm(`Czy na pewno chcesz ${action} to ogłoszenie?`, { icon: isActive ? '🚫' : '✅', title: isActive ? 'Dezaktywuj ogłoszenie' : 'Aktywuj ogłoszenie', okText: isActive ? 'Dezaktywuj' : 'Aktywuj', okClass: isActive ? 'btn-warning' : 'btn-success' }); if (!confirmed) return; try { const response = await fetch('{{ url_for("classifieds.classifieds_toggle_active", classified_id=classified.id) }}', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken } }); const data = await response.json(); if (data.success) { showToast(data.message, 'success'); setTimeout(() => location.reload(), 1500); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } // ============================================================ // INTEREST (ZAINTERESOWANIA) // ============================================================ async function toggleInterest() { try { const response = await fetch('{{ url_for("classifieds.classifieds_interest", classified_id=classified.id) }}', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken } }); const data = await response.json(); if (data.success) { const btn = document.getElementById('interestBtn'); const btnText = document.getElementById('interestBtnText'); const svg = btn.querySelector('svg'); if (data.interested) { btn.classList.add('interested'); btnText.textContent = 'Zainteresowany'; svg.setAttribute('fill', 'currentColor'); } else { btn.classList.remove('interested'); btnText.textContent = 'Jestem zainteresowany'; svg.setAttribute('fill', 'none'); } showToast(data.message, 'success'); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } async function showInterestsModal() { document.getElementById('interestsModal').classList.add('active'); document.getElementById('interestsList').innerHTML = '
Ładowanie...
'; try { const response = await fetch('{{ url_for("classifieds.classifieds_interests", classified_id=classified.id) }}'); const data = await response.json(); if (data.success) { if (data.interests.length === 0) { document.getElementById('interestsList').innerHTML = '
Brak zainteresowanych
'; } else { document.getElementById('interestsList').innerHTML = data.interests.map(i => `
${i.user_initial}
${i.user_name}
${i.company_name ? `
${i.company_name}
` : ''} ${i.message ? `
"${i.message}"
` : ''}
${new Date(i.created_at).toLocaleDateString('pl-PL')}
`).join(''); } } else { document.getElementById('interestsList').innerHTML = '
Błąd ładowania
'; } } catch (error) { document.getElementById('interestsList').innerHTML = '
Błąd połączenia
'; } } function closeInterestsModal() { document.getElementById('interestsModal').classList.remove('active'); } document.getElementById('interestsModal').addEventListener('click', e => { if (e.target.id === 'interestsModal') closeInterestsModal(); }); // ============================================================ // Q&A (PYTANIA I ODPOWIEDZI) // ============================================================ async function askQuestion() { const content = document.getElementById('questionContent').value.trim(); if (!content) { showToast('Wpisz treść pytania', 'warning'); return; } try { const response = await fetch('{{ url_for("classifieds.classifieds_ask", classified_id=classified.id) }}', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ content }) }); const data = await response.json(); if (data.success) { showToast('Pytanie dodane', 'success'); document.getElementById('questionContent').value = ''; setTimeout(() => location.reload(), 1000); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } async function answerQuestion(questionId) { const content = document.getElementById(`answerContent-${questionId}`).value.trim(); if (!content) { showToast('Wpisz treść odpowiedzi', 'warning'); return; } try { const response = await fetch(`/b2b/{{ classified.id }}/question/${questionId}/answer`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken }, body: JSON.stringify({ answer: content }) }); const data = await response.json(); if (data.success) { showToast('Odpowiedź dodana', 'success'); setTimeout(() => location.reload(), 1000); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } async function toggleQuestionVisibility(questionId) { try { const response = await fetch(`/b2b/{{ classified.id }}/question/${questionId}/hide`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken } }); const data = await response.json(); if (data.success) { showToast(data.message, 'success'); setTimeout(() => location.reload(), 1000); } else { showToast(data.error || 'Wystąpił błąd', 'error'); } } catch (error) { showToast('Błąd połączenia', 'error'); } } {% endblock %}