"use client";
import { useState, useEffect, useRef, useCallback } from "react";
import { uploadFile, generateMarketingStudioAd } from "../muapi.js";
const SCROLLBAR_STYLE = `
.custom-scrollbar-thin::-webkit-scrollbar {
height: 4px;
}
.custom-scrollbar-thin::-webkit-scrollbar-track {
background: transparent;
}
.custom-scrollbar-thin::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
}
.custom-scrollbar-thin::-webkit-scrollbar-thumb:hover {
background: rgba(217, 255, 0, 0.3);
}
`;
// ── Icons ────────────────────────────────────────────────────────────────────
const CheckSvg = () => (
);
const PlusSvg = () => (
);
const CloseSvg = () => (
);
const ProductIcon = () => (
);
const AvatarIcon = () => (
);
const RefIcon = () => (
);
// ── Assets ───────────────────────────────────────────────────────────────────
const ASSETS = {
avatar: [
{ id: "aa252283-8591-4d14-91a8-41ce54187992", name: "Priya", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Priya.webp" },
{ id: "ba6c9b18-f79c-4dab-9649-88a181d0a038", name: "Elena", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Elena.webp" },
{ id: "30e2cadd-987c-4a7a-81c3-094d4fb3a65e", name: "Kai", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Kai.webp" },
{ id: "fbed59e1-4b8d-4625-9140-ef2044e0be72", name: "Sora", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Sora.webp" },
{ id: "bcd9e6ee-c000-48e6-9f4b-a20fc2a674f7", name: "Minji", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Minji.webp" },
{ id: "1da384ed-3856-45e4-bf4c-a496c7aa95ff", name: "Margot", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Margot.webp" },
{ id: "b799c8f5-fb6e-4905-b33b-cdefac153ec3", name: "Niko", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Niko.webp" },
{ id: "b6971dd4-55fa-4e64-b318-392b16504284", name: "Jin", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/Jin.webp" }
],
ugc: [
{ id: 1, name: "UGC", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/ugc.mp4" },
{ id: 2, name: "Tutorial", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/ugc_how_to.mp4" },
{ id: 3, name: "Unboxing", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/ugc_unboxing.mp4" },
{ id: 4, name: "Hyper Motion", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/hyper-motion-mini.mp4" },
{ id: 5, name: "Product Review", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/product_review.mp4" },
{ id: 6, name: "TV Spot", url: "https://d3adwkbyhxyrtq.cloudfront.net/web-app/tv-spot-mini.mp4" }
]
};
const OPTIONS = {
ratio: ["9:16", "3:4", "4:3", "16:9", "1:1"],
res: ["720p", "1080p"],
duration: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
};
// ── Components ───────────────────────────────────────────────────────────────
function UploadSlot({ icon, url, progress, label, onUpload, onClear, multiple = false, images = [] }) {
const inputRef = useRef(null);
return (
inputRef.current?.click()}
title={`Upload ${label}`}
className={`relative w-10 h-10 rounded-full border transition-all flex items-center justify-center cursor-pointer ${
url ? 'border-primary/40 bg-primary/5' : 'border-white/5 bg-white/5 hover:bg-white/10 hover:border-white/20'
}`}
>
onUpload(e)}
/>
{progress > 0 && progress < 100 ? (
{progress}%
) : url ? (
) : (
{icon}
)}
{/* Clear Button (Single) */}
{url && !multiple && (
)}
);
}
function Dropdown({ isOpen, title, items, selectedId, onSelect, onClose, isVideo = false }) {
const ref = useRef(null);
useEffect(() => {
if (!isOpen) return;
const handler = (e) => {
if (ref.current && !ref.current.contains(e.target)) onClose();
};
window.addEventListener("click", handler);
return () => window.removeEventListener("click", handler);
}, [isOpen, onClose]);
if (!isOpen) return null;
return (
{title}
{items.map(item => (
onSelect(item)}
className={`relative rounded overflow-hidden border-2 transition-all group cursor-pointer ${
selectedId === item.id || selectedId === item.url ? 'border-primary shadow-glow' : 'border-white/5 hover:border-white/20'
}`}
>
{isVideo ? (
) : (

)}
{item.name}
{(selectedId === item.id || selectedId === item.url) && (
)}
))}
);
}
function SimpleDropdown({ isOpen, title, options, selected, onSelect, onClose }) {
const ref = useRef(null);
useEffect(() => {
if (!isOpen) return;
const handler = (e) => {
if (ref.current && !ref.current.contains(e.target)) onClose();
};
window.addEventListener("click", handler);
return () => window.removeEventListener("click", handler);
}, [isOpen, onClose]);
if (!isOpen) return null;
return (
{title}
{options.map(opt => (
))}
);
}
// ── Main Component ───────────────────────────────────────────────────────────
export default function MarketingStudio({ apiKey, droppedFiles, onFilesHandled }) {
const PERSIST_KEY = "hg_marketing_studio_persistent";
const [prompt, setPrompt] = useState("");
const [productImage, setProductImage] = useState(null);
const [avatarImage, setAvatarImage] = useState(null);
const [additionalImages, setAdditionalImages] = useState([]);
const [params, setParams] = useState({
ratio: "9:16",
format: ASSETS.ugc[0].name,
videoUrl: ASSETS.ugc[0].url,
res: "1080p",
duration: 5
});
const [history, setHistory] = useState([]);
const [isGenerating, setIsGenerating] = useState(false);
const [dropdown, setDropdown] = useState(null); // 'format' | 'avatar' | 'ratio' | 'res' | 'duration'
const [uploadProgress, setUploadProgress] = useState({ product: 0, avatar: 0, additional: 0 });
const [fullscreenUrl, setFullscreenUrl] = useState(null);
const textareaRef = useRef(null);
// ── Persistence ───────────────────────────────────────────────────────────
useEffect(() => {
try {
const stored = localStorage.getItem(PERSIST_KEY);
if (stored) {
const data = JSON.parse(stored);
if (data.prompt) setPrompt(data.prompt);
if (data.params) setParams(data.params);
if (data.productImage) setProductImage(data.productImage);
if (data.avatarImage) setAvatarImage(data.avatarImage);
if (data.additionalImages) setAdditionalImages(data.additionalImages);
if (data.history) setHistory(data.history);
}
} catch (err) { console.warn("Load failed", err); }
}, []);
useEffect(() => {
const timer = setTimeout(() => {
const state = { prompt, params, productImage, avatarImage, additionalImages, history };
localStorage.setItem(PERSIST_KEY, JSON.stringify(state));
}, 500);
return () => clearTimeout(timer);
}, [prompt, params, productImage, avatarImage, additionalImages, history]);
// ── Handlers ───────────────────────────────────────────────────────────────
const downloadFile = async (url, filename) => {
try {
const response = await fetch(url);
const blob = await response.blob();
const blobUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = blobUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(blobUrl);
} catch {
window.open(url, "_blank");
}
};
const handleUpload = async (e, target) => {
const files = Array.from(e.target.files);
if (!files.length) return;
if (target === 'additional') {
const remaining = 6 - additionalImages.length;
const toUpload = files.slice(0, remaining);
for (const file of toUpload) {
try {
const url = await uploadFile(apiKey, file, (pct) => setUploadProgress(p => ({ ...p, additional: pct })));
setAdditionalImages(prev => [...prev, url].slice(0, 6));
} catch (err) { alert(err.message); }
}
} else {
const file = files[0];
try {
const url = await uploadFile(apiKey, file, (pct) => setUploadProgress(p => ({ ...p, [target]: pct })));
if (target === 'product') setProductImage(url);
else setAvatarImage(url);
} catch (err) { alert(err.message); }
}
setUploadProgress(p => ({ ...p, [target]: 0 }));
};
const handleGenerate = async () => {
if (!prompt.trim()) return alert("Please enter an ad script.");
if (!productImage) return alert("Please upload a product image.");
setIsGenerating(true);
try {
const result = await generateMarketingStudioAd(apiKey, {
prompt,
aspect_ratio: params.ratio,
duration: params.duration,
resolution: params.res,
images_list: [productImage, avatarImage, ...additionalImages].filter(Boolean),
video_files: params.videoUrl ? [params.videoUrl] : []
});
if (result?.url) {
const entry = {
id: Date.now(),
url: result.url,
prompt,
format: params.format,
timestamp: new Date().toISOString()
};
setHistory(prev => [entry, ...prev]);
setFullscreenUrl(result.url);
}
} catch (err) {
alert("Generation failed: " + err.message);
} finally {
setIsGenerating(false);
}
};
const handleTextareaInput = (e) => {
const el = e.target;
el.style.height = "auto";
el.style.height = Math.min(el.scrollHeight, 250) + "px";
};
// ── Render ─────────────────────────────────────────────────────────────────
return (
{/* ── MAIN CONTENT AREA ── */}
{history.length > 0 ? (
{history.map(entry => (
))}
) : (
START CREATING WITH
MARKETING STUDIO
Describe your scene, upload your product, and watch high-converting AI video ads come to life.
)}
{/* ── BOTTOM PROMPT BAR ── */}
{additionalImages.length > 0 && (
{additionalImages.map((img, idx) => (
))}
)}
{/* Top Row: Full-width Textarea */}
{/* Bottom Row: Uploads + Controls + Generate */}
{/* Asset Uploads Group */}
}
url={productImage}
progress={uploadProgress.product}
onUpload={(e) => handleUpload(e, 'product')}
onClear={() => setProductImage(null)}
/>
}
url={avatarImage}
progress={uploadProgress.avatar}
onUpload={(e) => handleUpload(e, 'avatar')}
onClear={() => setAvatarImage(null)}
/>
}
url={additionalImages[0]}
progress={uploadProgress.additional}
multiple
images={additionalImages}
onUpload={(e) => handleUpload(e, 'additional')}
onClear={(idx) => {
if (idx !== undefined) {
setAdditionalImages(prev => prev.filter((_, i) => i !== idx));
} else {
setAdditionalImages([]);
}
}}
/>
{/* Format Button */}
setParams({ ...params, format: item.name, videoUrl: item.url })}
onClose={() => setDropdown(null)}
isVideo
/>
{/* Avatar Preset Button */}
setAvatarImage(item.url)}
onClose={() => setDropdown(null)}
/>
{/* Simple Controls */}
{['ratio', 'res', 'duration'].map(key => (
setParams({ ...params, [key]: val })}
onClose={() => setDropdown(null)}
/>
))}
{/* Fullscreen Preview */}
{fullscreenUrl && (
setFullscreenUrl(null)}>
)}
);
}