import React, { useState, useRef } from 'react'; export default function UGCStoryboardStudio() { const [currentStep, setCurrentStep] = useState(1); // Step 1: Product & Character State const [productImage, setProductImage] = useState(null); const [productImageBase64, setProductImageBase64] = useState(''); const [productName, setProductName] = useState(''); const [productUsp, setProductUsp] = useState(''); const [isAnalyzingProduct, setIsAnalyzingProduct] = useState(false); const [characterImage, setCharacterImage] = useState(null); const [characterImageBase64, setCharacterImageBase64] = useState(''); const [characterDescription, setCharacterDescription] = useState(''); const [isAnalyzingCharacter, setIsAnalyzingCharacter] = useState(false); // Step 2: Audience, Hook & Setting State const [targetAudience, setTargetAudience] = useState('Wanita Muda & Gen-Z Enthusiast (18-28 thn)'); const [customAudience, setCustomAudience] = useState(''); const [hooksList, setHooksList] = useState([]); const [selectedHook, setSelectedHook] = useState(''); const [customHook, setCustomHook] = useState(''); const [isGeneratingHooks, setIsGeneratingHooks] = useState(false); const [backgroundSetting, setBackgroundSetting] = useState('Kamar Tidur Estetik Modern / Desk Setup Minimalis'); // Step 3: Storyboard Configuration State const [sceneCount, setSceneCount] = useState(6); const [sceneDuration, setSceneDuration] = useState(5); const [promptEngine, setPromptEngine] = useState('Nano Banana 2 Lite (Rekomendasi Utama)'); const [videoEngine, setVideoEngine] = useState('Veo 3.1 Quality'); // Step 4: Output & Preview State const [isGeneratingStoryboard, setIsGeneratingStoryboard] = useState(false); const [storyboardData, setStoryboardData] = useState(null); const [toastMessage, setToastMessage] = useState(''); const [previewImages, setPreviewImages] = useState({}); const [generatingImageIndex, setGeneratingImageIndex] = useState(null); const [playingAudioIndex, setPlayingAudioIndex] = useState(null); const showToast = (msg) => { setToastMessage(msg); setTimeout(() => setToastMessage(''), 3000); }; const handleProductImageUpload = async (e) => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onloadend = async () => { const base64String = reader.result.split(',')[1]; setProductImage(reader.result); setProductImageBase64(base64String); setIsAnalyzingProduct(true); try { const apiKey = ""; const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent?key=${apiKey}`; const payload = { contents: [ { role: "user", parts: [ { text: "Identifikasi produk dalam foto ini. Berikan: 1. Nama/Kategori Produk ringkas, 2. Unik Selling Point (USP) utama & manfaatnya untuk konten affiliate UGC TikTok. Balas dalam format ringkas Bahasa Indonesia." }, { inlineData: { mimeType: file.type || "image/jpeg", data: base64String } } ] } ] }; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await response.json(); const text = data.candidates?.[0]?.content?.parts?.[0]?.text || ''; if (text) { const lines = text.split('\n').filter(l => l.trim()); setProductName(lines[0]?.replace(/^[0-9.#*-\s]+/, '') || 'Produk Estetik Premium'); setProductUsp(text); showToast('✨ Produk berhasil terdeteksi otomatis oleh AI!'); } } catch (err) { console.error("Gagal mendeteksi gambar:", err); } finally { setIsAnalyzingProduct(false); } }; reader.readAsDataURL(file); }; const handleCharacterImageUpload = async (e) => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onloadend = async () => { const base64String = reader.result.split(',')[1]; setCharacterImage(reader.result); setCharacterImageBase64(base64String); setIsAnalyzingCharacter(true); try { const apiKey = ""; const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent?key=${apiKey}`; const payload = { contents: [ { role: "user", parts: [ { text: "Analisis karakter dalam foto ini untuk konsistensi gambar AI. Deskripsikan: Umur perkiraan, etnis/gaya wajah, rambut, pakaian, ekspresi, dan style khas UGC. Buat ringkas dalam bahasa Inggris untuk keperluan prompt AI." }, { inlineData: { mimeType: file.type || "image/jpeg", data: base64String } } ] } ] }; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await response.json(); const text = data.candidates?.[0]?.content?.parts?.[0]?.text || ''; if (text) { setCharacterDescription(text); showToast('👤 Profil & Karakter berhasil diekstrak!'); } } catch (err) { console.error("Gagal mendeteksi karakter:", err); } finally { setIsAnalyzingCharacter(false); } }; reader.readAsDataURL(file); }; const handleGenerateHooks = async () => { if (!productName) { showToast('⚠️ Isi nama produk terlebih dahulu!'); return; } setIsGeneratingHooks(true); try { const apiKey = ""; const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent?key=${apiKey}`; const prompt = `Buatkan 4 opsi HOOK video UGC TikTok/Reels yang sangat tinggi konversinya dalam Bahasa Indonesia kasual/gaul untuk produk berikut: Nama Produk: ${productName} USP/Deskripsi: ${productUsp} Target Market: ${targetAudience} Buat 4 tipe hook berbeda: 1. Problem-Agitate Hook (Menyentuh pain point) 2. Curiosity / Stop Scrolling Hook (Bikin penasaran) 3. Direct Visual & Benefit Hook (Langsung kasih liat keunggulan) 4. Shocking Secret / Before-After Hook (Rahasia rahasia-an) Format output JSON array string murni saja, contoh: ["Hook 1", "Hook 2", "Hook 3", "Hook 4"]`; const payload = { contents: [{ role: 'user', parts: [{ text: prompt }] }], generationConfig: { responseMimeType: "application/json" } }; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await response.json(); const text = data.candidates?.[0]?.content?.parts?.[0]?.text; const parsedHooks = JSON.parse(text); if (Array.isArray(parsedHooks)) { setHooksList(parsedHooks); if (parsedHooks.length > 0) setSelectedHook(parsedHooks[0]); showToast('🎯 4 Opsi Hook berhasil dibuat!'); } } catch (err) { console.error("Gagal membuat hooks:", err); const fallbacks = [ `"Sumpah baru tau produk ${productName} ini ternyata bikin..."`, `"Jangan beli ${productName} ini sebelum kamu tahu fakta ini!"`, `"Awalnya ga percaya tapi pas nyobain ${productName} langsung kaget banget!"`, `"Rahasia ${targetAudience} tetap awet & tampil estetik tiap hari!"` ]; setHooksList(fallbacks); setSelectedHook(fallbacks[0]); } finally { setIsGeneratingHooks(false); } }; const handleGenerateStoryboard = async () => { if (!productName) { showToast('⚠️ Harap lengkapi nama produk terlebih dahulu.'); return; } setIsGeneratingStoryboard(true); setCurrentStep(4); try { const apiKey = ""; const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:generateContent?key=${apiKey}`; const chosenHookFinal = customHook || selectedHook || `Review jujur pemakaian ${productName}`; const chosenAudienceFinal = customAudience || targetAudience; const isYoutubeCreateEngine = videoEngine.toLowerCase().includes('youtube create'); const videoPromptRule = isYoutubeCreateEngine ? '⚠️ KHUSUS ENGINE "Youtube Create": Prompt video WAJIB KETAT MAKSIMAL 900 KARAKTER (strict limit under 900 chars), namun harus sangat kaya informasi gerakan, sudut kamera, dan subjek.' : 'TIDAK ADA BATASAN KARAKTER untuk prompt video. Buat prompt video sespesifik, sedetail, dan seoptimal mungkin menggambarkan pergerakan kamera, subjek, pencahayaan, dan dinamika aksinya.'; const systemPrompt = `Kamu adalah Pakar Prompt UGC (User-Generated Content) Hyper-Realistis, Sutradara Konten TikTok/Reels Affiliate, dan Master Prompt Engineer untuk AI Photo Engine (${promptEngine}) serta AI Video Engine (${videoEngine}). Tugas utama: Menghasilkan storyboard lengkap sebanyak ${sceneCount} scene bergaya UGC yang SANGAT NATURAL, HYPER-REALISTIS, dan HIGH CONVERTING. ATURAN PROMPT FOTO (${promptEngine}): 1. TIDAK ADA BATASAN KARAKTER. Buat photoPrompt sangat komprehensif, ultra-detail, kaya deskripsi visual, dan dioptimalkan penuh untuk ${promptEngine}. 2. GAYA FOTO MENTAH HANDHELD: "Raw handheld smartphone photo, shot on iPhone 15 Pro Max 24mm f/1.8 optical lens feel, authentic UGC style, natural skin texture with subtle imperfections, realistic pore structure, soft ambient window lighting, authentic shadows, zero artificial 3D/CGI render look". 3. KONSISTENSI KARAKTER RINCI: Masukkan rincian fisik karakter secara presisi (${characterDescription || 'Indonesian young adult UGC creator, natural casual home outfit, minimal makeup, natural facial traits'}). 4. DETAIL INTERAKSI PRODUK: Jelaskan visual produk secara mendalam, cara tangan memegang produk, angle kemasan, dan penggunaan produk (${productName} - ${productUsp}). 5. Latar belakang & Komposisi: Masukkan detail environment (${backgroundSetting}), depth of field, framing, serta parameter aspect ratio 9:16 vertical video (--ar 9:16). ATURAN PROMPT VIDEO (${videoEngine}): 1. Format Bahasa Inggris khusus yang disesuaikan penuh untuk AI Video Generator (${videoEngine}). 2. Jelaskan detail gerakan kamera (misal: natural micro-handheld shake, slow drift, smooth camera tracking, dynamic push-in, subtle tilt). 3. Rincikan perubahan aksi karakter, ekspresi wajah, interaksi produk, serta perubahan pencahayaan alami secara rinci. 4. ${videoPromptRule} ATURAN VOICE OVER (NASKAH): - Gunakan Bahasa Indonesia percakapan kasual UGC affiliate (seperti bicarakan rekomendasi ke teman sendiri, tanpa bahasa formal yang kaku).`; const userQuery = `Buatkan ${sceneCount} scene storyboard UGC untuk: - Produk: ${productName} - Deskripsi/USP Lengkap: ${productUsp} - Karakter Spesifikasi: ${characterDescription || 'Kreator UGC lokal kasual'} - Hook Pilihan: ${chosenHookFinal} - Target Audience: ${chosenAudienceFinal} - Background Setting: ${backgroundSetting} - Durasi per scene: ${sceneDuration} detik. - Target Engine Foto: ${promptEngine} (Bebas batasan karakter, buat ultra-detail) - Target Engine Video: ${videoEngine} ${isYoutubeCreateEngine ? '(MAKSIMAL 900 KARAKTER)' : '(Bebas batasan karakter, buat detail dan optimal)'} Kembalikan respon DALAM FORMAT JSON murni sesuai skema ini: { "storyboardTitle": "Judul Konsep Konten", "angleStrategy": "Penjelasan singkat strategi UGC", "caption": { "headline": "Teks Caption Hook untuk TikTok/Reels/Shopee", "body": "Isi caption lengkap dengan panggilan aksi (CTA)", "hashtags": ["#racuntiktok", "#reviewproduk", "#affiliate"] }, "scenes": [ { "sceneNumber": 1, "shotType": "Talking Head / POV Handheld / Product Close-up", "duration": "${sceneDuration}s", "voiceover": "Naskah voiceover Bahasa Indonesia kasual", "actionDescription": "Deskripsi aksi fisik dalam scene", "photoPrompt": "Ultra-detailed hyper-realistic UGC photo prompt in English optimized for ${promptEngine} with --ar 9:16 (No character length limit)", "videoPrompt": "Detailed UGC video prompt in English optimized for ${videoEngine} with exact camera movements ${isYoutubeCreateEngine ? '(strictly max 900 characters)' : '(no character length limit)'}" } ] }`; const payload = { contents: [{ role: 'user', parts: [{ text: userQuery }] }], systemInstruction: { parts: [{ text: systemPrompt }] }, generationConfig: { responseMimeType: "application/json", responseSchema: { type: "OBJECT", properties: { storyboardTitle: { type: "STRING" }, angleStrategy: { type: "STRING" }, caption: { type: "OBJECT", properties: { headline: { type: "STRING" }, body: { type: "STRING" }, hashtags: { type: "ARRAY", items: { type: "STRING" } } }, required: ["headline", "body", "hashtags"] }, scenes: { type: "ARRAY", items: { type: "OBJECT", properties: { sceneNumber: { type: "INTEGER" }, shotType: { type: "STRING" }, duration: { type: "STRING" }, voiceover: { type: "STRING" }, actionDescription: { type: "STRING" }, photoPrompt: { type: "STRING" }, videoPrompt: { type: "STRING" } }, required: ["sceneNumber", "shotType", "duration", "voiceover", "actionDescription", "photoPrompt", "videoPrompt"] } } }, required: ["storyboardTitle", "angleStrategy", "caption", "scenes"] } } }; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await response.json(); const text = data.candidates?.[0]?.content?.parts?.[0]?.text; const parsedResult = JSON.parse(text); setStoryboardData(parsedResult); showToast('🎉 Storyboard Hyper-Realistis berhasil dibuat!'); } catch (err) { console.error("Gagal membuat storyboard:", err); showToast('❌ Terjadi kesalahan saat memproses storyboard.'); } finally { setIsGeneratingStoryboard(false); } }; const handleGenerateSceneImagePreview = async (sceneIndex, promptText) => { setGeneratingImageIndex(sceneIndex); try { const apiKey = ""; const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-image:generateContent?key=${apiKey}`; const payload = { contents: [{ parts: [{ text: promptText }] }], generationConfig: { responseModalities: ["IMAGE"], imageConfig: { aspectRatio: "9:16" } } }; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const result = await response.json(); const part = result?.candidates?.[0]?.content?.parts?.find(p => p.inlineData); if (part) { const imageUrl = `data:${part.inlineData.mimeType};base64,${part.inlineData.data}`; setPreviewImages(prev => ({ ...prev, [sceneIndex]: imageUrl })); showToast(`📷 Preview Scene ${sceneIndex + 1} selesai!`); } else { showToast('❌ Gagal menghasilkan gambar preview.'); } } catch (err) { console.error("Gagal generate image preview:", err); showToast('❌ Error saat menghubungi server Gemini Image.'); } finally { setGeneratingImageIndex(null); } }; const handlePlayVoiceover = (index, text) => { if ('speechSynthesis' in window) { window.speechSynthesis.cancel(); if (playingAudioIndex === index) { setPlayingAudioIndex(null); return; } const utterance = new SpeechSynthesisUtterance(text); utterance.lang = 'id-ID'; utterance.rate = 1.05; utterance.pitch = 1.0; utterance.onend = () => setPlayingAudioIndex(null); utterance.onerror = () => setPlayingAudioIndex(null); setPlayingAudioIndex(index); window.speechSynthesis.speak(utterance); } else { showToast('Browser Anda tidak mendukung pratinjau suara.'); } }; const copyToClipboard = async (text, label) => { try { if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(text); showToast(`📋 ${label} disalin ke clipboard!`); return; } } catch (err) { console.warn("Clipboard API blocked by permissions policy, using fallback method:", err); } try { const textArea = document.createElement("textarea"); textArea.value = text; textArea.style.position = "fixed"; textArea.style.top = "0"; textArea.style.left = "0"; textArea.style.opacity = "0"; document.body.appendChild(textArea); textArea.focus(); textArea.select(); const successful = document.execCommand('copy'); document.body.removeChild(textArea); if (successful) { showToast(`📋 ${label} disalin ke clipboard!`); } else { showToast(`❌ Gagal menyalin. Silakan salin secara manual.`); } } catch (fallbackErr) { console.error("Fallback copy failed: ", fallbackErr); showToast(`❌ Gagal menyalin ke clipboard.`); } }; return (
{/* Toast Alert */} {toastMessage && (
{toastMessage}
)} {/* Top Header */}

UGC Storyboard & Prompt Studio AI

Pakar Prompt Foto & Video Hyper-Realistis untuk Seller & Affiliator

Gemini AI UGC Engine Active
{/* Main Workflow Area */}
{/* Workflow Steps Navigator */}
{[ { num: 1, title: 'Foto & Karakter', desc: 'Identifikasi Produk & Sheet' }, { num: 2, title: 'Target & Hook', desc: 'Audience, Hook & Setting' }, { num: 3, title: 'Opsi Storyboard', desc: 'Durasi, Scene & Engine' }, { num: 4, title: 'Hasil Storyboard', desc: 'Prompt Foto & Video UGC' }, ].map((step) => { const isActive = currentStep === step.num; const isDone = currentStep > step.num; return ( ); })}
{/* STEP 1: Upload Foto Produk & Character Sheet */} {currentStep === 1 && (
{/* Box 1: Product Upload & Details */}

1. Identifikasi Produk

Vision AI Enabled
{/* Upload Dropzone */}
{productImage ? (
Product
Klik / Drop untuk Ganti Foto
) : (

Upload Foto Produk

AI akan otomatis membaca visual & keunggulan produk

)}
{isAnalyzingProduct && (
AI sedang mengidentifikasi foto produk...
)} {/* Product Info Inputs */}
setProductName(e.target.value)} className="w-full bg-slate-950 border border-slate-700/80 rounded-xl p-3 text-sm focus:outline-none focus:border-indigo-500 transition-colors" />
{/* Box 2: Character Sheet Upload & Consistency */}

2. Konsistensi Karakter Sheet

Consistent UGC Profile
{/* Upload Dropzone */}
{characterImage ? (
Character
Klik / Drop untuk Ganti Karakter
) : (

Upload Character Sheet / Model Foto

AI akan mengekstrak detail fisik agar prompt antar scene konsisten

)}
{isAnalyzingCharacter && (
Mengekstrak profil dan konsistensi fisik model...
)} {/* Character Details Box */}
)} {/* STEP 2: Target Market, Hook & Background Setting */} {currentStep === 2 && (

Target Market, Hook & Background Setting

Pilih angle pemasaran yang sesuai dengan produk dan target pembeli

Step 2 of 4
{/* Target Market Option */}
{[ 'Wanita Muda & Gen-Z Enthusiast (18-28 thn)', 'Ibu Muda & Rumah Tangga (25-38 thn)', 'Pria Kasual / Tech & Grooming (20-35 thn)', 'Pekerja Kantoran & Profesional Sibuk', 'Mahasiswa & Pelajar On-Budget' ].map((aud) => ( ))}
setCustomAudience(e.target.value)} className="w-full bg-slate-950 border border-slate-700/80 rounded-xl p-3 text-xs focus:outline-none focus:border-indigo-500" />
{/* 4 Hook Options */}
{isGeneratingHooks ? (
AI sedang memformulasikan 4 hook bernilai konversi tinggi...
) : (
{hooksList.map((hookText, idx) => (
{ setSelectedHook(hookText); setCustomHook(''); }} className={`p-3.5 rounded-xl border text-xs cursor-pointer transition-all flex items-start gap-3 ${ selectedHook === hookText && !customHook ? 'bg-gradient-to-r from-indigo-950 to-slate-900 border-indigo-500 text-white shadow-md' : 'bg-slate-950 border-slate-800 text-slate-300 hover:border-slate-700' }`} > {idx + 1}
{hookText}
))}
)} setCustomHook(e.target.value)} className="w-full bg-slate-950 border border-slate-700/80 rounded-xl p-3 text-xs focus:outline-none focus:border-indigo-500" />
{/* Background Setting */}
{[ 'Kamar Tidur Estetik Modern / Desk Setup Minimalis', 'Dapur Bersih / Minimalist Kitchen Table', 'Kamar Mandi Estetik Skincare Area (Aesthetic Bathroom)', 'Interior Mobil Vlogger Style (Car Vlog Setting)', 'Kafe Kekinian / Modern Outdoor Workspace', 'Gym / Tempat Fitnes / Outdoor Park Activity' ].map((bg) => ( ))}
)} {/* STEP 3: Scene Options & Storyboard Configuration */} {currentStep === 3 && (

Konfigurasi Scene & Prompt Engine

Atur jumlah scene, durasi per detik, serta AI Generator pilihan Anda

Step 3 of 4
{/* Scene Count */}
{[4, 6, 8, 10].map((count) => ( ))}

Rekomendasi affiliate: 6 scene (durasi total ~30 detik)

{/* Duration per Scene */}
{[3, 5, 8, 10].map((dur) => ( ))}

Cocok untuk TikTok/Reels fast-paced engagement

{/* AI Generator Preference */}

✨ Prompt foto dibuat ultra-detail tanpa batasan karakter.

{videoEngine.toLowerCase().includes('youtube create') ? '⚠️ Youtube Create aktif: Prompt video dibatasi ketat maks 900 karakter.' : '✨ Prompt video dibuat optimal & detail tanpa batasan karakter.'}

{/* Summary Box */}
Ringkasan Konsep Konten UGC:
• Produk: {productName || 'Belum diisi'}
• Target: {customAudience || targetAudience}
• Scene: {sceneCount} Scene ({sceneCount * sceneDuration}s Total)
• Setting: {backgroundSetting}
• Photo AI: {promptEngine}
• Video AI: {videoEngine}
)} {/* STEP 4: Storyboard Output & Prompts Display */} {currentStep === 4 && (
{isGeneratingStoryboard ? (

AI Sedang Menyusun Storyboard UGC

Menyeimbangkan konsistensi karakter, sudut pengambilan foto mentah ({promptEngine}), prompt video ({videoEngine}), dan naskah Voiceover kasual khas TikTok affiliate...

) : storyboardData ? ( <> {/* Header Info & Strategy */}
✓ UGC Storyboard Ready

{storyboardData.storyboardTitle || 'Konsep UGC Viral'}

{storyboardData.angleStrategy}

{/* Scene-by-Scene Storyboard Cards */}
{storyboardData.scenes?.map((scene, idx) => (
{/* Scene Header */}
#{scene.sceneNumber}
{scene.shotType} ({scene.duration})
{/* Voiceover TTS Trigger */} {/* Generate Image Preview Trigger */}
{/* Content Grid: VO, Action & Prompts */}
{/* Script & Voiceover Info */}
🎙️ Voiceover Script (Bahasa Indonesia Kasual):

"{scene.voiceover}"

🎬 Aksi Visual Scene:

{scene.actionDescription}

{/* Generated Preview Image Display if available */} {previewImages[idx] && (
📸 Sample Image Preview (Generated by Gemini Flash):
{`Preview
)}
{/* Prompts Section */}
{/* Photo Prompt Box */}
📸 PROMPT FOTO ({promptEngine}): {scene.photoPrompt?.length || 0} Karakter

{scene.photoPrompt}

{/* Video Prompt Box */}
🎥 PROMPT VIDEO ({videoEngine}): 900 ? 'bg-rose-500/10 text-rose-300 border-rose-500/30' : 'bg-purple-500/10 text-purple-300 border-purple-500/20' }`}> {scene.videoPrompt?.length || 0} Karakter {videoEngine.toLowerCase().includes('youtube create') ? '/ Maks 900' : ''}

{scene.videoPrompt}

))}
{/* Caption & Hashtags Recommendations */} {storyboardData.caption && (

📝 Rekomendasi Caption & Hashtag TikTok / Reels / Shopee

{storyboardData.caption.headline}

{storyboardData.caption.body}

{storyboardData.caption.hashtags?.map((tag, tIdx) => ( {tag} ))}
)} ) : null}
)}
{/* Footer */}
); }