import React, { useState, useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Badge } from '@/components/ui/badge'; import { Spinner } from '@/components/ui/spinner'; interface Campaign { id: string; name: string; headline_spintax_root: string; location_mode: string; status: string; date_created: string; } interface GenerationResult { metadata: { slotCount: number; spintaxCombinations: number; locationCount: number; totalPossible: number; wasTruncated: boolean; }; results: { processed: number; inserted: number; skipped: number; alreadyExisted: number; }; } export default function CampaignManager() { const [campaigns, setCampaigns] = useState([]); const [loading, setLoading] = useState(true); const [generating, setGenerating] = useState(null); const [lastResult, setLastResult] = useState(null); const [showForm, setShowForm] = useState(false); const [formData, setFormData] = useState({ name: '', headline_spintax_root: '', location_mode: 'none', niche_variables: '{}' }); useEffect(() => { fetchCampaigns(); }, []); async function fetchCampaigns() { try { const res = await fetch('/api/campaigns'); const data = await res.json(); setCampaigns(data.campaigns || []); } catch (err) { console.error('Error fetching campaigns:', err); } finally { setLoading(false); } } async function handleSubmit(e: React.FormEvent) { e.preventDefault(); try { await fetch('/api/campaigns', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); setShowForm(false); setFormData({ name: '', headline_spintax_root: '', location_mode: 'none', niche_variables: '{}' }); fetchCampaigns(); } catch (err) { console.error('Error creating campaign:', err); } } async function generateHeadlines(campaignId: string) { setGenerating(campaignId); setLastResult(null); try { const res = await fetch('/api/seo/generate-headlines', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ campaign_id: campaignId, max_headlines: 10000 }) }); const data = await res.json(); if (data.success) { setLastResult(data as GenerationResult); } else { alert('Error: ' + (data.error || 'Unknown error')); } } catch (err) { console.error('Error generating headlines:', err); alert('Failed to generate headlines'); } finally { setGenerating(null); } } if (loading) { return ; } return (

Manage your SEO campaigns with Cartesian Permutation headline generation.

{/* Generation Result Modal */} {lastResult && (

✓ Headlines Generated Successfully

Spintax Slots:

{lastResult.metadata.slotCount}

Spintax Combinations:

{lastResult.metadata.spintaxCombinations.toLocaleString()}

Locations:

{lastResult.metadata.locationCount.toLocaleString()}

Total Possible (n×k):

{lastResult.metadata.totalPossible.toLocaleString()}

Inserted: {lastResult.results.inserted.toLocaleString()} Skipped (duplicates): {lastResult.results.skipped.toLocaleString()} Already existed: {lastResult.results.alreadyExisted.toLocaleString()}
{lastResult.metadata.wasTruncated && (

⚠ Results truncated to 10,000 headlines (safety limit)

)}
)} {showForm && ( Create New Campaign
setFormData({ ...formData, name: e.target.value })} placeholder="e.g., Local Dental SEO" required />