/** * External Communications Manager * Enhanced Blog Curation with Multi-Channel Content Generation */ // Publication targets (loaded from server) let PUBLICATIONS = {}; // Get auth token function getAuthToken() { return localStorage.getItem('admin_token'); } // API call helper async function apiCall(endpoint, options = {}) { const token = getAuthToken(); const defaultOptions = { cache: 'no-store', // Force fresh requests - prevent cached 500 errors headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }; const response = await fetch(endpoint, { ...defaultOptions, ...options }); if (response.status === 401) { localStorage.removeItem('admin_token'); window.location.href = '/admin/login.html'; throw new Error('Unauthorized'); } return response; } // Load publication targets async function loadPublicationTargets() { // For Phase 1, we define publications client-side // In Phase 2, this would fetch from /api/publications PUBLICATIONS = { letter: [ { id: 'economist-letter', name: 'The Economist', rank: 1, words: '200-250', email: 'letters@economist.com', response: '2-7 days' }, { id: 'ft-letter', name: 'Financial Times', rank: 2, words: '200-250', email: 'letters.editor@ft.com', response: '2-5 days' }, { id: 'guardian-letter', name: 'The Guardian', rank: 4, words: '200-250', email: 'letters@theguardian.com', response: '2-5 days' }, { id: 'nyt-letter', name: 'New York Times', rank: 6, words: '150-200', email: 'letters@nytimes.com', response: '2-7 days' }, { id: 'wsj-letter', name: 'Wall Street Journal', rank: 11, words: '200-250', email: 'wsj.ltrs@wsj.com', response: '2-5 days' }, { id: 'der-spiegel-letter', name: 'Der Spiegel (German)', rank: 16, words: '200-300', email: 'leserbriefe@spiegel.de', response: '5-10 days', language: 'de' }, { id: 'latimes-letter', name: 'Los Angeles Times', rank: 17, words: '100-150', email: 'letters@latimes.com', response: '2-5 days' }, { id: 'die-presse-letter', name: 'Die Presse (Austrian)', rank: 20, words: '150-200', email: 'leserbriefe@diepresse.com', response: '3-7 days', language: 'de' } ], oped: [ { id: 'mit-tech-review-oped', name: 'MIT Technology Review', rank: 3, words: '800-1500', email: 'editors@technologyreview.com', response: '3-6 weeks', pitch: true }, { id: 'ieee-spectrum-oped', name: 'IEEE Spectrum', rank: 5, words: '1000-2000', method: 'form', response: '4-8 weeks' }, { id: 'nyt-oped', name: 'New York Times', rank: 6, words: '800-1200', email: 'oped@nytimes.com', response: '2-4 weeks', pitch: true }, { id: 'washpost-oped', name: 'Washington Post', rank: 7, words: '750-800', method: 'form', response: '2-4 weeks' }, { id: 'caixin-global-oped', name: 'Caixin Global (China)', rank: 8, words: '800-1500', email: 'english@caixin.com', response: '2-4 weeks', pitch: true }, { id: 'hindu-open-page', name: 'The Hindu (India)', rank: 9, words: '800-1200', email: 'openpage@thehindu.co.in', response: '1-3 weeks' }, { id: 'le-monde-oped', name: 'Le Monde (French)', rank: 10, words: '900-1200', method: 'form', response: '2-4 weeks', language: 'fr' }, { id: 'wired-oped', name: 'Wired', rank: 12, words: '1000-1500', method: 'form', response: '3-6 weeks', pitch: true }, { id: 'mail-guardian-oped', name: 'Mail & Guardian (South Africa)', rank: 13, words: '800-1200', method: 'website', response: '1-2 weeks' }, { id: 'venturebeat-oped', name: 'VentureBeat', rank: 16, words: '800-1500', method: 'form', response: '1-2 weeks' }, { id: 'folha-oped', name: 'Folha de S.Paulo (Brazil)', rank: 16, words: '600-900', method: 'form', response: '1-2 weeks', note: 'English edition available' } ], social: [ { id: 'linkedin', name: 'LinkedIn Articles', rank: 14, words: '1000-2000', method: 'self-publish', response: 'immediate' }, { id: 'daily-blog-nz', name: 'The Daily Blog (NZ)', rank: 15, words: '800-1200', email: 'thedailyblog@gmail.com', response: '1-3 days' }, { id: 'substack-essay', name: 'Substack', rank: 18, words: '1500-2500', method: 'self-publish', response: 'immediate' }, { id: 'medium-essay', name: 'Medium', rank: 19, words: '1200-2500', method: 'self-publish', response: 'immediate' } ] }; } // Handle content type selection function setupContentTypeHandlers() { const contentTypeRadios = document.querySelectorAll('input[name="contentType"]'); const publicationSection = document.getElementById('publication-section'); const articleRefSection = document.getElementById('article-reference-section'); const topicSection = document.getElementById('topic-section'); const topicStepLabel = document.getElementById('topic-step-label'); const contextStepLabel = document.getElementById('context-step-label'); contentTypeRadios.forEach(radio => { radio.addEventListener('change', (e) => { const type = e.target.value; // Update UI based on content type if (type === 'blog') { // Blog: no publication selector, no article ref publicationSection.classList.add('hidden'); articleRefSection.classList.add('hidden'); topicSection.classList.remove('hidden'); topicStepLabel.textContent = 'Step 2'; contextStepLabel.textContent = 'Step 3'; } else if (type === 'letter') { // Letter: show publication + article ref publicationSection.classList.remove('hidden'); articleRefSection.classList.remove('hidden'); topicSection.classList.add('hidden'); contextStepLabel.textContent = 'Step 4'; populatePublications('letter'); } else if (type === 'oped') { // Op-Ed: show publication, no article ref publicationSection.classList.remove('hidden'); articleRefSection.classList.add('hidden'); topicSection.classList.remove('hidden'); topicStepLabel.textContent = 'Step 3'; contextStepLabel.textContent = 'Step 4'; populatePublications('oped'); } else if (type === 'social') { // Social: show publication (platforms), no article ref publicationSection.classList.remove('hidden'); articleRefSection.classList.add('hidden'); topicSection.classList.remove('hidden'); topicStepLabel.textContent = 'Step 3'; contextStepLabel.textContent = 'Step 4'; populatePublications('social'); } }); }); } // Populate publication dropdown based on type function populatePublications(type) { const dropdown = document.getElementById('publication-target'); dropdown.innerHTML = ''; const pubs = PUBLICATIONS[type] || []; pubs.sort((a, b) => a.rank - b.rank).forEach(pub => { const option = document.createElement('option'); option.value = pub.id; option.textContent = `#${pub.rank} ${pub.name} (${pub.words} words)`; option.dataset.pubData = JSON.stringify(pub); dropdown.appendChild(option); }); } // Handle publication selection function setupPublicationHandler() { const dropdown = document.getElementById('publication-target'); const infoBox = document.getElementById('publication-info'); const wordCount = document.getElementById('pub-word-count'); const submission = document.getElementById('pub-submission'); const response = document.getElementById('pub-response'); const suggestBtn = document.getElementById('suggest-topics-btn'); const suggestionsDiv = document.getElementById('topic-suggestions'); // Elements might not exist if we're on a different section if (!dropdown) { console.log('[External Communications] Publication dropdown not found - not in Generate section'); return; } dropdown.addEventListener('change', (e) => { if (!e.target.value) { infoBox.classList.add('hidden'); suggestBtn.classList.add('hidden'); suggestionsDiv.classList.add('hidden'); return; } const option = e.target.selectedOptions[0]; const pub = JSON.parse(option.dataset.pubData); wordCount.textContent = `${pub.words} words${pub.pitch ? ' (pitch first)' : ''}`; if (pub.email) { submission.textContent = pub.email; } else if (pub.method === 'form') { submission.textContent = 'Via online form'; } else if (pub.method === 'website') { submission.textContent = 'Via website contact'; } else if (pub.method === 'self-publish') { submission.textContent = 'Self-publish platform'; } response.textContent = pub.response; infoBox.classList.remove('hidden'); // Show topic suggestions button for publications with readership profiles // Currently only economist-letter and nyt-oped have readership profiles const pubsWithProfiles = ['economist-letter', 'nyt-oped']; if (pubsWithProfiles.includes(pub.id)) { suggestBtn.classList.remove('hidden'); } else { suggestBtn.classList.add('hidden'); } // Clear previous suggestions suggestionsDiv.classList.add('hidden'); }); } // Setup publication-specific topic suggestions function setupTopicSuggestions() { const suggestBtn = document.getElementById('suggest-topics-btn'); const suggestText = document.getElementById('suggest-topics-text'); const suggestLoader = document.getElementById('suggest-topics-loader'); const suggestionsDiv = document.getElementById('topic-suggestions'); const dropdown = document.getElementById('publication-target'); suggestBtn.addEventListener('click', async () => { const publicationId = dropdown.value; if (!publicationId) return; // Show loading state suggestBtn.disabled = true; suggestText.classList.add('hidden'); suggestLoader.classList.remove('hidden'); suggestionsDiv.classList.add('hidden'); try { const response = await apiCall('/api/blog/suggest-topics-for-publication', { method: 'POST', body: JSON.stringify({ publicationId }) }); if (!response.ok) { throw new Error('Failed to fetch topic suggestions'); } const data = await response.json(); displayTopicSuggestions(data, publicationId); } catch (error) { console.error('Error fetching topic suggestions:', error); suggestionsDiv.innerHTML = `
Failed to generate topic suggestions. Please try again.
📊 Auto-fill: Audience = ${autoFill.audience}, Tone = ${autoFill.tone}, Culture = ${autoFill.culture}
${topic.rationale}
✅ Topic applied! Fields auto-filled based on publication readership.
'; suggestionsDiv.appendChild(successMsg); setTimeout(() => { successMsg.remove(); }, 3000); } // Handle form submission async function setupFormHandler() { const form = document.getElementById('draft-form'); const submitBtn = document.getElementById('draft-btn'); const status = document.getElementById('draft-status'); form.addEventListener('submit', async (e) => { e.preventDefault(); submitBtn.disabled = true; submitBtn.textContent = 'Generating...'; status.textContent = 'Generating draft...'; status.className = 'text-sm text-blue-600'; try { const formData = new FormData(form); const contentType = formData.get('contentType'); // Build request payload const payload = { contentType, audience: formData.get('audience'), tone: formData.get('tone') || 'standard', culture: formData.get('culture') || 'universal', language: formData.get('language') || 'en' }; // Add type-specific data if (contentType === 'blog') { payload.topic = formData.get('topic'); payload.focus = formData.get('focus'); } else if (contentType === 'letter') { payload.publicationTarget = formData.get('publicationTarget'); payload.articleReference = { title: formData.get('articleTitle'), date: formData.get('articleDate'), mainPoint: formData.get('mainPoint') }; } else if (contentType === 'oped' || contentType === 'social') { payload.publicationTarget = formData.get('publicationTarget'); payload.topic = formData.get('topic'); payload.focus = formData.get('focus'); } // Validate required fields if (!payload.audience) { throw new Error('Please select a target audience'); } if (contentType === 'letter' && !payload.publicationTarget) { throw new Error('Please select a publication for your letter'); } if (contentType === 'letter' && (!payload.articleReference.title || !payload.articleReference.mainPoint)) { throw new Error('Please provide article title and your main point for letter to editor'); } if ((contentType === 'blog' || contentType === 'oped') && !payload.topic) { throw new Error('Please provide a topic'); } // Submit to API const response = await apiCall('/api/blog/suggest-topics', { method: 'POST', body: JSON.stringify(payload) }); if (!response.ok) { const error = await response.json(); throw new Error(error.message || 'Failed to generate content'); } const result = await response.json(); status.textContent = '✓ Draft generated! Added to moderation queue.'; status.className = 'text-sm text-green-600'; // Reset form after short delay setTimeout(() => { form.reset(); document.getElementById('publication-section').classList.add('hidden'); document.getElementById('article-reference-section').classList.add('hidden'); status.textContent = ''; submitBtn.textContent = 'Generate Draft'; submitBtn.disabled = false; }, 3000); } catch (error) { console.error('Error generating content:', error); status.textContent = `Error: ${error.message}`; status.className = 'text-sm text-red-600'; submitBtn.textContent = 'Generate Draft'; submitBtn.disabled = false; } }); } // Initialize page async function init() { await loadPublicationTargets(); setupContentTypeHandlers(); setupPublicationHandler(); setupTopicSuggestions(); setupFormHandler(); console.log('[External Communications] Initialized with', Object.keys(PUBLICATIONS).length, 'content types'); } // Run on page load if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); }