import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Sparkles, Upload, FileText, Image as ImageIcon, Box, Check, Loader2, ArrowRight, RefreshCw, ChevronLeft } from 'lucide-react'; import { api } from '../lib/api'; const STEPS = [ { id: 1, title: 'Upload PDF', icon: Upload }, { id: 2, title: 'AI Review', icon: FileText }, { id: 3, title: 'Concept Art', icon: ImageIcon }, { id: 4, title: '3D Generation', icon: Box }, ]; export default function SparkPlug() { const navigate = useNavigate(); const [currentStep, setCurrentStep] = useState(1); const [isLoading, setIsLoading] = useState(false); const [loadingMessage, setLoadingMessage] = useState(''); // Step 1: PDF File const [pdfFile, setPdfFile] = useState(null); // Step 2: Extracted Text const [extractedPrompt, setExtractedPrompt] = useState(''); // Step 3: Generated Images (Multi-view) const [generatedImages, setGeneratedImages] = useState([]); // Step 4: 3D Model const [modelProgress, setModelProgress] = useState(0); const handleFileUpload = async (e: React.ChangeEvent) => { if (e.target.files && e.target.files[0]) { setPdfFile(e.target.files[0]); } }; const processStep1 = async () => { if (!pdfFile) return; setLoadingMessage('Extracting details with Gemini AI...'); setIsLoading(true); try { // Simulate reading PDF with Gemini const geminiKey = localStorage.getItem('GEMINI_API_KEY'); if (!geminiKey) { alert("Please configure your Gemini API Key in the Dashboard Settings first."); setIsLoading(false); return; } // We would normally send the PDF to the backend or use Gemini SDK directly here. // Mocking the extraction for now based on the Indica Shampoo PDF context: await new Promise(r => setTimeout(r, 2000)); setExtractedPrompt("Product: Indica Easy Herbal Shield Colour Shampoo\nType: Sachet/Packet\nColor Palette: Green, White\nKey Elements: Woman with black hair, Amla & Aloe illustration, 10 Minutes Herbal Care text.\n\nDescription: A glossy green rectangular sachet with vivid branding and product imagery for an herbal hair colour shampoo."); setCurrentStep(2); } catch (err) { console.error(err); alert("Failed to process PDF."); } finally { setIsLoading(false); } }; const processStep2 = async () => { if (!extractedPrompt) return; setLoadingMessage('Generating multi-view concepts with Nano Banana...'); setIsLoading(true); try { const nanoBananaKey = localStorage.getItem('NANO_BANANA_API_KEY'); if (!nanoBananaKey) { alert("Please configure your Nano Banana API Key in the Dashboard Settings first."); setIsLoading(false); return; } // Mocking the image generation with Nano Banana await new Promise(r => setTimeout(r, 3000)); // Placeholder images for the 4 views (front, back, left, right) setGeneratedImages([ "https://images.unsplash.com/photo-1629198688000-71f23e745b6e?w=400&q=80", // front "https://images.unsplash.com/photo-1629198725848-18e55e975cc3?w=400&q=80", // back "https://images.unsplash.com/photo-1629198725838-511bb7405234?w=400&q=80", // left "https://images.unsplash.com/photo-1629198688000-71f23e745b6e?w=400&q=80", // right ]); setCurrentStep(3); } catch (err) { console.error(err); alert("Failed to generate image."); } finally { setIsLoading(false); } }; const processStep3 = async () => { if (generatedImages.length === 0) return; setLoadingMessage('Constructing 3D Model with Meshy API...'); setIsLoading(true); try { const meshyKey = localStorage.getItem('MESHY_API_KEY'); if (!meshyKey) { alert("Please configure your Meshy API Key in the Dashboard Settings first."); setIsLoading(false); return; } // Simulate Meshy generation for (let i = 1; i <= 10; i++) { await new Promise(r => setTimeout(r, 500)); setModelProgress(i * 10); } setCurrentStep(4); } catch (err) { console.error(err); alert("Failed to generate 3D model."); } finally { setIsLoading(false); } }; const processStep4 = async () => { setLoadingMessage('Saving project to your workspace...'); setIsLoading(true); try { // Mock generated GLB URL from Meshy const mockGlbUrl = "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BoxTextured/glTF-Binary/BoxTextured.glb"; // Create project and save with model injected into scene_data const res = await api.saveProject({ name: "Spark Plug Output", scene_data: { objects: [ { id: "model_" + Date.now(), type: "model", name: "Generated Product Model", visible: true, position: [0, 0, 0], rotation: [0, 0, 0], scale: [1, 1, 1], url: mockGlbUrl } ] } }); if (res.id) { navigate(`/editor/${res.id}`); } } catch (err) { console.error('Failed to create project', err); alert('Failed to save project'); } finally { setIsLoading(false); } }; return (
{/* Header */}
Spark Plug | Idea to 3D Generation
{/* Stepper */}
{STEPS.map((step) => { const Icon = step.icon; const isCompleted = currentStep > step.id; const isCurrent = currentStep === step.id; return (
{isCompleted ? : }
{step.title}
); })}
{/* Content Area */}
{isLoading && (

{loadingMessage}

This might take a few moments...

)} {currentStep === 1 && (

Upload Mintel Spark PDF

Upload a product concept PDF. Spark Plug will extract the product details using Gemini AI to prepare for 3D generation.

{pdfFile ? pdfFile.name : 'Click or drag PDF here'}
{pdfFile && ( )}
)} {currentStep === 2 && (

Review Extracted Concept

Gemini AI has analyzed the PDF. Review and modify the prompt below before generating images.