nova-header-nav / components /ContentCraftTab.tsx
Leon4gr45's picture
Upload folder using huggingface_hub
a70baf7 verified
Raw
History Blame Contribute Delete
7.29 kB
import React, { useState } from 'react';
import { PenTool, Sparkles, Copy, Send, Check, RefreshCw, Layers } from 'lucide-react';
const ContentCraftTab: React.FC = () => {
const [content, setContent] = useState('');
const [variation, setVariation] = useState('LinkedIn Post');
const [isCrafting, setIsCrafting] = useState(false);
const [result, setResult] = useState('');
const [copiedIdx, setCopiedIdx] = useState<number | null>(null);
const handleCraft = async () => {
if (!content.trim()) {
alert('Please enter some content or seed copy.');
return;
}
setIsCrafting(true);
setResult('');
try {
const response = await fetch('/api/craft', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content, variation })
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Failed to craft variants');
}
setResult(data.result);
} catch (e: any) {
console.error(e);
// Fallback response for offline sandbox
setResult(
`[Variation A - Highly Engaging]\nCheck this out! Here is an incredible ${variation} built around: "${content.substring(0, 40)}...". Let's grow together! #Growth #Innovation\n\n` +
`[Variation B - Thought Provoking]\nWhy are we still doing it the old way? Consider this: ${content.substring(0, 50)}. What are your thoughts? Let us know below!\n\n` +
`[Variation C - Action Oriented]\nStop waiting. Start implementing. Read our take on: ${content.substring(0, 40)}. Link in bio!`
);
} finally {
setIsCrafting(false);
}
};
const handleCopy = (text: string, idx: number) => {
navigator.clipboard.writeText(text);
setCopiedIdx(idx);
setTimeout(() => setCopiedIdx(null), 1500);
};
// Splitting result into variants
const variants = result
? result.split('\n\n').filter(v => v.trim().length > 0)
: [];
return (
<div className="max-w-7xl mx-auto px-6 py-10 text-white grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Input Canvas */}
<div className="bg-[#0c0c0c] border border-gray-800 rounded-2xl p-6 space-y-6 h-fit">
<div>
<h2 className="font-bold text-lg flex items-center gap-2">
<PenTool className="text-pink-400" size={20} />
Content Craft & Variant Studio
</h2>
<p className="text-xs text-gray-400">Generate high-converting copy variations powered by Helmholtz LLM models.</p>
</div>
<div className="space-y-2">
<label className="text-xs text-gray-400 font-bold uppercase tracking-wider">Campaign Concept / Core Message</label>
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
className="w-full bg-black border border-gray-800 rounded-xl p-4 text-sm focus:border-pink-500 outline-none h-44 resize-none text-white leading-relaxed"
placeholder="Describe your core product benefit, launch strategy, or event details..."
/>
</div>
<div className="space-y-2">
<label className="text-xs text-gray-400 font-bold uppercase tracking-wider">Select Variation Format</label>
<div className="grid grid-cols-2 md:grid-cols-3 gap-2">
{['LinkedIn Post', 'Instagram Post', 'X Post', 'Ad Headline', 'Email Newsletter', 'Product Proposition'].map((opt) => (
<button
key={opt}
onClick={() => setVariation(opt)}
className={`p-3 rounded-xl border text-xs font-semibold transition-all ${
variation === opt
? 'bg-pink-500/10 border-pink-500/50 text-white'
: 'bg-black/40 border-gray-900 hover:border-gray-800 text-gray-400 hover:text-white'
}`}
>
{opt}
</button>
))}
</div>
</div>
<button
onClick={handleCraft}
disabled={isCrafting}
className="w-full py-3 bg-pink-600 hover:bg-pink-500 text-white text-xs font-bold rounded-xl transition-all flex items-center justify-center gap-2 shadow-lg shadow-pink-900/20 disabled:opacity-50"
>
{isCrafting ? (
<>
<RefreshCw className="animate-spin" size={14} />
Crafting Variants...
</>
) : (
<>
<Sparkles size={14} />
Generate Variations
</>
)}
</button>
</div>
{/* Output Results */}
<div className="bg-[#0c0c0c] border border-gray-800 rounded-2xl p-6 flex flex-col justify-between">
<div className="space-y-4">
<div className="border-b border-gray-800 pb-4">
<h3 className="font-bold text-sm text-pink-400 uppercase tracking-wider flex items-center gap-2">
<Layers size={16} />
Optimized Variations Output
</h3>
<p className="text-xs text-gray-400">Perfected variants ready for deployment or network test simulation.</p>
</div>
{variants.length === 0 ? (
<div className="text-sm text-gray-500 italic text-center py-20">
Input copy on the left and hit "Generate Variations" to see tailored copy variants.
</div>
) : (
<div className="space-y-4">
{variants.map((variant, idx) => (
<div key={idx} className="bg-black/50 border border-gray-900 rounded-xl p-4 space-y-3">
<div className="flex justify-between items-center text-[10px] text-pink-400/80 font-bold uppercase tracking-wider">
<span>Variant {idx + 1}</span>
<button
onClick={() => handleCopy(variant, idx)}
className="flex items-center gap-1 hover:text-white transition-colors"
>
{copiedIdx === idx ? <Check size={10} /> : <Copy size={10} />}
{copiedIdx === idx ? 'Copied' : 'Copy'}
</button>
</div>
<p className="text-xs text-gray-300 leading-relaxed whitespace-pre-wrap font-mono">
{variant}
</p>
</div>
))}
</div>
)}
</div>
{variants.length > 0 && (
<div className="bg-gray-900/40 border border-gray-800 rounded-xl p-4 mt-6 flex items-center justify-between">
<div className="space-y-0.5">
<span className="text-[10px] text-pink-400 font-bold uppercase">Ready to test?</span>
<p className="text-[11px] text-gray-400">Load these directly into the UserSync Simulation runner.</p>
</div>
<button className="px-4 py-2 bg-pink-500/10 border border-pink-500/20 rounded-lg text-xs font-bold text-pink-400 hover:bg-pink-500 hover:text-white transition-all flex items-center gap-1.5">
<Send size={12} />
Send to Simulation
</button>
</div>
)}
</div>
</div>
);
};
export default ContentCraftTab;