import React, { useState } from 'react'; import { FileText, Mail, Copy, Check, Loader2, AlertCircle } from 'lucide-react'; const NewsletterBuilder = () => { const [urls, setUrls] = useState(['', '', '']); const [articles, setArticles] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [copied, setCopied] = useState(false); const [apiEndpoint, setApiEndpoint] = useState('https://newsletter-builder-tawny.vercel.app/api/fetch-article'); const extractMetadata = async (url) => { try { const response = await fetch(apiEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ url }) }); if (!response.ok) { throw new Error('Failed to fetch article'); } return await response.json(); } catch (err) { console.error('Error fetching:', url, err); throw err; } }; const handleFetchArticles = async () => { setLoading(true); setError(''); setArticles([]); const validUrls = urls.filter(url => url.trim()); if (validUrls.length < 2) { setError('Please enter at least 2 article URLs'); setLoading(false); return; } try { const results = await Promise.allSettled( validUrls.map(url => extractMetadata(url)) ); const validArticles = results .filter(result => result.status === 'fulfilled') .map(result => result.value); if (validArticles.length === 0) { setError('Could not fetch any articles. Make sure the backend API is accessible.'); setLoading(false); return; } validArticles.sort((a, b) => b.wordCount - a.wordCount); setArticles(validArticles); if (validArticles.length < validUrls.length) { setError(`Successfully fetched ${validArticles.length} out of ${validUrls.length} articles.`); } } catch (err) { setError('Error fetching articles. Please check the API endpoint.'); console.error(err); } setLoading(false); }; const generateNewsletterHTML = () => { if (articles.length === 0) return ''; const mainArticle = articles[0]; const subArticles = articles.slice(1); return ` Newsletter

📰 Newsletter

Main Story

${mainArticle.title}

${mainArticle.excerpt}

Read full article →
${subArticles.length > 0 ? `
Other Stories
${subArticles.map(article => `

${article.title}

${article.excerpt.substring(0, 150)}...

Read more →
`).join('')}
` : ''}
Quick Reads
${articles.map(article => ` `).join('')}
Recommended Reads
${articles.slice().reverse().map(article => ` `).join('')}
`; }; const copyHTML = () => { const html = generateNewsletterHTML(); navigator.clipboard.writeText(html); setCopied(true); setTimeout(() => setCopied(false), 2000); }; return (

Newsletter Builder

Transform article URLs into beautiful newsletters

Ready to Use - Backend Deployed

✓ Backend is hosted on Vercel (no local setup needed)

✓ Just paste your article URLs and click Generate

Using API: {apiEndpoint}

Article URLs

setApiEndpoint(e.target.value)} placeholder="https://your-project.vercel.app/api/fetch-article" className="w-full px-4 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent" />

Default uses deployed Vercel backend

{urls.map((url, index) => (
{ const newUrls = [...urls]; newUrls[index] = e.target.value; setUrls(newUrls); }} placeholder="https://fitpreethi.com/article-url" className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
))}
{error && (
0 ? 'bg-yellow-50 border-yellow-200 text-yellow-800' : 'bg-red-50 border-red-200 text-red-700' }`}> {error}
)} {articles.length > 0 && ( )}

Newsletter Preview

{articles.length === 0 ? (

Enter article URLs and click Generate Newsletter

) : (
)}

How it works:

  • ✓ Backend server fetches article HTML (bypasses CORS restrictions)
  • ✓ Extracts metadata: title, description, Open Graph tags, word count
  • ✓ Articles auto-sorted by length (longest = main story)
  • ✓ Generates 4 sections: Main Story, Other Stories, Quick Reads, Recommended Reads
  • ✓ Copy HTML and paste into any email service provider
); }; export default NewsletterBuilder;