// Response Wall JavaScript let currentCampaignSlug = null; let currentOffset = 0; let currentSort = 'recent'; let currentLevel = ''; const LIMIT = 20; // Initialize document.addEventListener('DOMContentLoaded', () => { console.log('Response Wall: Initializing...'); // Get campaign slug from URL if present const urlParams = new URLSearchParams(window.location.search); currentCampaignSlug = urlParams.get('campaign'); console.log('Campaign slug:', currentCampaignSlug); if (!currentCampaignSlug) { showError('No campaign specified'); return; } // Load initial data loadResponseStats(); loadResponses(true); // Set up event listeners document.getElementById('sort-select').addEventListener('change', (e) => { currentSort = e.target.value; loadResponses(true); }); document.getElementById('level-filter').addEventListener('change', (e) => { currentLevel = e.target.value; loadResponses(true); }); const submitBtn = document.getElementById('submit-response-btn'); console.log('Submit button found:', !!submitBtn); if (submitBtn) { submitBtn.addEventListener('click', () => { console.log('Submit button clicked'); openSubmitModal(); }); } // Use event delegation for empty state button since it's dynamically shown document.addEventListener('click', (e) => { if (e.target.id === 'empty-state-submit-btn') { console.log('Empty state button clicked'); openSubmitModal(); } }); const modalCloseBtn = document.getElementById('modal-close-btn'); if (modalCloseBtn) { modalCloseBtn.addEventListener('click', closeSubmitModal); } const cancelBtn = document.getElementById('cancel-submit-btn'); if (cancelBtn) { cancelBtn.addEventListener('click', closeSubmitModal); } const loadMoreBtn = document.getElementById('load-more-btn'); if (loadMoreBtn) { loadMoreBtn.addEventListener('click', loadMoreResponses); } const form = document.getElementById('submit-response-form'); if (form) { form.addEventListener('submit', handleSubmitResponse); } console.log('Response Wall: Initialization complete'); }); // Load response statistics async function loadResponseStats() { try { const response = await fetch(`/api/campaigns/${currentCampaignSlug}/response-stats`); const data = await response.json(); if (data.success) { document.getElementById('stat-total-responses').textContent = data.stats.totalResponses; document.getElementById('stat-verified').textContent = data.stats.verifiedResponses; document.getElementById('stat-upvotes').textContent = data.stats.totalUpvotes; document.getElementById('stats-banner').style.display = 'flex'; } } catch (error) { console.error('Error loading stats:', error); } } // Load responses async function loadResponses(reset = false) { if (reset) { currentOffset = 0; document.getElementById('responses-container').innerHTML = ''; } showLoading(true); try { const params = new URLSearchParams({ sort: currentSort, level: currentLevel, offset: currentOffset, limit: LIMIT }); const response = await fetch(`/api/campaigns/${currentCampaignSlug}/responses?${params}`); const data = await response.json(); showLoading(false); if (data.success && data.responses.length > 0) { renderResponses(data.responses); // Show/hide load more button if (data.pagination.hasMore) { document.getElementById('load-more-container').style.display = 'block'; } else { document.getElementById('load-more-container').style.display = 'none'; } } else if (reset) { showEmptyState(); } } catch (error) { showLoading(false); showError('Failed to load responses'); console.error('Error loading responses:', error); } } // Render responses function renderResponses(responses) { const container = document.getElementById('responses-container'); responses.forEach(response => { const card = createResponseCard(response); container.appendChild(card); }); } // Create response card element function createResponseCard(response) { const card = document.createElement('div'); card.className = 'response-card'; card.dataset.responseId = response.id; const createdDate = new Date(response.created_at).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); let badges = `${escapeHtml(response.representative_level)}`; badges += `${escapeHtml(response.response_type)}`; if (response.is_verified) { badges = `✓ Verified` + badges; } let submittedBy = 'Anonymous'; if (!response.is_anonymous && response.submitted_by_name) { submittedBy = escapeHtml(response.submitted_by_name); } let userCommentHtml = ''; if (response.user_comment) { userCommentHtml = `
Constituent's Comment: ${escapeHtml(response.user_comment)}
`; } let screenshotHtml = ''; if (response.screenshot_url) { screenshotHtml = `
Response screenshot
`; } const upvoteClass = response.hasUpvoted ? 'upvoted' : ''; card.innerHTML = `

${escapeHtml(response.representative_name)}

${response.representative_title ? `${escapeHtml(response.representative_title)}` : ''} ${createdDate}
${badges}
${escapeHtml(response.response_text)}
${userCommentHtml} ${screenshotHtml}
`; // Add event listener for upvote button const upvoteBtn = card.querySelector('.upvote-btn'); upvoteBtn.addEventListener('click', function() { toggleUpvote(response.id, this); }); // Add event listener for screenshot image if present const screenshotImg = card.querySelector('.screenshot-image'); if (screenshotImg) { screenshotImg.addEventListener('click', function() { viewImage(this.dataset.imageUrl); }); } return card; } // Toggle upvote async function toggleUpvote(responseId, button) { const isUpvoted = button.classList.contains('upvoted'); const url = `/api/responses/${responseId}/upvote`; try { const response = await fetch(url, { method: isUpvoted ? 'DELETE' : 'POST', headers: { 'Content-Type': 'application/json' } }); const data = await response.json(); if (data.success) { // Update button state button.classList.toggle('upvoted'); button.querySelector('.upvote-count').textContent = data.upvoteCount; // Reload stats loadResponseStats(); } else { showError(data.error || 'Failed to update upvote'); } } catch (error) { console.error('Error toggling upvote:', error); showError('Failed to update upvote'); } } // Load more responses function loadMoreResponses() { currentOffset += LIMIT; loadResponses(false); } // Open submit modal function openSubmitModal() { console.log('openSubmitModal called'); const modal = document.getElementById('submit-modal'); if (modal) { modal.style.display = 'block'; console.log('Modal displayed'); } else { console.error('Modal element not found'); } } // Close submit modal function closeSubmitModal() { document.getElementById('submit-modal').style.display = 'none'; document.getElementById('submit-response-form').reset(); } // Handle response submission async function handleSubmitResponse(e) { e.preventDefault(); const formData = new FormData(e.target); try { const response = await fetch(`/api/campaigns/${currentCampaignSlug}/responses`, { method: 'POST', body: formData }); const data = await response.json(); if (data.success) { showSuccess(data.message || 'Response submitted successfully! It will appear after moderation.'); closeSubmitModal(); // Don't reload responses since submission is pending approval } else { showError(data.error || 'Failed to submit response'); } } catch (error) { console.error('Error submitting response:', error); showError('Failed to submit response'); } } // View image in modal/new tab function viewImage(url) { window.open(url, '_blank'); } // Utility functions function showLoading(show) { document.getElementById('loading').style.display = show ? 'block' : 'none'; } function showEmptyState() { document.getElementById('empty-state').style.display = 'block'; document.getElementById('responses-container').innerHTML = ''; document.getElementById('load-more-container').style.display = 'none'; } function showError(message) { // Simple alert for now - could integrate with existing error display system alert('Error: ' + message); } function showSuccess(message) { // Simple alert for now - could integrate with existing success display system alert(message); } function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Close modal when clicking outside window.onclick = function(event) { const modal = document.getElementById('submit-modal'); if (event.target === modal) { closeSubmitModal(); } };