import React, { useState, useEffect, useRef, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; // Import Zustand store import { useAuthStore } from '../stores/authStore'; import type { UserData } from '../stores/authStore'; import { CONFIG } from '../utils/config'; // Import utilities import { generateActionFigure } from '../utils/imageGenerationUtils'; // Import components import UserInfoDisplay from '../components/UserInfoDisplay'; import ImageModal from '../components/ImageModal'; import ShareableImage from '../components/ShareableImage'; import { useSEO } from '../hooks/useSEO'; // API key is now handled by the backend Cloud Function const Create = () => { const navigate = useNavigate(); const { user, userData, isAuthenticated, queryUserData } = useAuthStore(); // SEO optimization for Create page useSEO({ title: 'Create AI Action Figures - AiFigure Generator | Free AI Art Creation Tool', description: 'Create stunning AI-generated action figures with our advanced AI technology. Upload reference images, customize your prompts, and generate unique action figures for free. Join thousands of artists creating amazing AI collectibles.', keywords: 'create AI action figures, AI art generator tool, custom figurine creator, AI toy generator, free AI image creation, action figure maker, AI collectibles generator, custom toy design', ogTitle: 'Create AI Action Figures - AiFigure Generator | Free AI Art Creation Tool', ogDescription: 'Create stunning AI-generated action figures with our advanced AI technology. Upload reference images, customize your prompts, and generate unique action figures for free.', ogImage: 'https://aifigure.com/create-og-image.jpg', ogUrl: window.location.href, twitterTitle: 'Create AI Action Figures - AiFigure Generator | Free AI Art Creation Tool', twitterDescription: 'Create stunning AI-generated action figures with our advanced AI technology. Upload reference images, customize your prompts, and generate unique action figures for free.', twitterImage: 'https://aifigure.com/create-og-image.jpg', canonical: window.location.href, type: 'website', }); // State variables const [uploadedImage, setUploadedImage] = useState(null); const [generatedImageUrl, setGeneratedImageUrl] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [isDragOver, setIsDragOver] = useState(false); const [customPrompt, setCustomPrompt] = useState(''); const [selectedStyle, setSelectedStyle] = useState(''); const [previewUrl, setPreviewUrl] = useState(''); const figureStyles = useMemo<{ key: string; prompt: string; thumb: string }[]>(() => ([ { prompt: 'Transform this person into a Western statue maquette. Fixed-pose collectible sculpture with dramatic stance, fashion modal body proportions, sharp sculpted details, realistic painted textures, matte and glossy shading, high-quality resin material. Displayed on a base with themed design. Strong studio lighting, premium product showcase photography.', key: 'Realistic', thumb: '/hottoy.png' }, { prompt: 'Transform this person into a Nendoroid chibi figure. Big round head, tiny body, glossy plastic toy look, smooth vinyl textures, cute anime proportions, large expressive eyes, small nose and mouth, simplified details, bright clean colors, standing on a small display base. ', key: 'Nendoroid', thumb: '/t1.png' }, { prompt: 'Funko Pop, (stictly with solid eyes, really big head, small body)', key: 'Funko Pop', thumb: 't2.png' }, { prompt: 'LEGO minifig, (stictly c shaped hands, stictly classic cylinder LEGO minifigure face, with lego blocks standing dock. lego logo on the box, details on outfits)', key: 'LEGO', thumb: '/lego.png' }, { prompt: 'Transform this person into 3 piece designer bikini. big breast, long legs', key: 'Bikini Mode', thumb: '/bikini.png' }, ]), []); useEffect(() => { if (uploadedImage) { const url = URL.createObjectURL(uploadedImage); setPreviewUrl(url); return () => URL.revokeObjectURL(url); } else { setPreviewUrl(''); } }, [uploadedImage]); // Modal state const [isModalOpen, setIsModalOpen] = useState(false); const [modalImageUrl, setModalImageUrl] = useState(''); const [modalAltText, setModalAltText] = useState(''); const fileInputRef = useRef(null); useEffect(() => { console.log('🏠 Create component useEffect triggered, checking auth state...'); console.log('🔍 useEffect dependencies:', { isAuthenticated, user: user?.email, userData: userData ? { tokens: userData.tokens, subscriptionTier: userData.subscriptionTier } : null }); if (!isAuthenticated) { console.log('❌ User not authenticated, redirecting to auth...'); navigate('/auth'); return; } console.log('👤 User authenticated:', user?.email || user?.phoneNumber); console.log('📊 User data from centralized store:', userData); // If user is authenticated but userData is null, query it again if (isAuthenticated && user && !userData) { console.log('🔄 User authenticated but userData is null, querying again...'); // Use the store's queryUserData function (it handles state updates internally) queryUserData(user.uid).then((freshUserData) => { if (freshUserData) { console.log('✅ Re-queried userData successfully'); } else { console.log('⚠️ No user document found on re-query, setting defaults'); const defaultData: UserData = { tokens: CONFIG.app.defaultTokens, subscriptionTier: 'free', generatedImages: [], publicImages: [] }; // Update the store with default data useAuthStore.setState({ userData: defaultData }); } }).catch((error) => { console.error('❌ Failed to re-query userData:', error); // Set defaults even on error to prevent infinite loading const defaultData: UserData = { tokens: CONFIG.app.defaultTokens, subscriptionTier: 'free', generatedImages: [], publicImages: [] }; useAuthStore.setState({ userData: defaultData }); }); } }, [isAuthenticated, user, userData, navigate]); // Event handlers const handleFileSelect = (file: File) => { setUploadedImage(file); setGeneratedImageUrl(''); setError(''); }; const handleDragOver = (isOver: boolean) => { setIsDragOver(isOver); }; const handleError = (errorMessage: string) => { setError(errorMessage); }; const handleFileChange = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { handleFileSelect(file); } }; const openFileDialog = () => { fileInputRef.current?.click(); }; // Modal handlers const openModal = (imageUrl: string, altText: string = "Image") => { setModalImageUrl(imageUrl); setModalAltText(altText); setIsModalOpen(true); }; const closeModal = () => { setIsModalOpen(false); setModalImageUrl(''); setModalAltText(''); }; // Image generation function const generateImage = async () => { if (!user || !uploadedImage) return; console.log('🚀 Starting image generation process...'); console.log('👤 User authenticated:', !!user); console.log('🖼️ Image uploaded:', !!uploadedImage); console.log('🔑 User ID:', user.uid); try { setLoading(true); setError(''); setGeneratedImageUrl(''); // Check tokens const tokens = userData?.tokens || 0; if (tokens < 5) { setError('You need at least 5 tokens to generate an image. Please purchase more tokens or upgrade your plan.'); navigate('/subscription'); return; } // Append selected style to prompt only when sending const styletext = "stictly 2 arms and 2 legs. close up on the figure. the monitor should be cropped outside the photo frame as it is not the most important part." const promptForRequest = selectedStyle ? (customPrompt ? `${customPrompt}. ${styletext} ${selectedStyle}` : `${styletext} ${selectedStyle}`) : customPrompt; console.log('📝 Final prompt:', promptForRequest); console.log('👤 User details:', { uid: user.uid, email: user.email, displayName: user.displayName, providerId: user.providerData?.[0]?.providerId }); // Generate image using backend Cloud Function const imageUrl = await generateActionFigure({ uploadedImage, customPrompt: promptForRequest, userId: user.uid }); setGeneratedImageUrl(imageUrl); // Note: Optimistic updates are now handled by the centralized Firestore listener // The real-time listener in the auth store will automatically update the userData console.log('📊 Centralized listener will update userData automatically'); console.log('✅ Image generation completed successfully'); } catch (error: any) { console.error('❌ Generation failed:', error); // Provide more specific error messages if (error.message?.includes('not authenticated') || error.message?.includes('Authentication failed')) { setError('Authentication error. Please sign out and sign in again to refresh your session.'); } else if (error.message?.includes('User not authenticated')) { setError('You are not signed in. Please sign in and try again.'); } else { setError(error.message || 'An error occurred during image generation. Please try again.'); } } finally { setLoading(false); } }; return (
{/* Main container with a card-like design */}
{/* Header and Description */}

AI Action Figure Creator

Transform your picture into a miniature anime action figure with AI magic

{/* User info and token display */} {/* Main content area - changes based on state */}
{/* Show upload area only when no image is generated and not loading */} {!generatedImageUrl && !loading && ( <> {/* Image upload area with drag and drop */}
{ e.preventDefault(); handleDragOver(true); }} onDragLeave={(e) => { e.preventDefault(); handleDragOver(false); }} onDrop={(e) => { e.preventDefault(); handleDragOver(false); if (e.dataTransfer.files.length > 0) { const file = e.dataTransfer.files[0]; if (file.type.startsWith('image/')) { handleFileSelect(file); } else { handleError('Please upload a valid image file (JPEG or PNG).'); } } }} onClick={openFileDialog} className={`mb-4 relative border-2 border-dashed rounded-xl p-8 text-center cursor-pointer transition-all duration-300 ${isDragOver ? 'border-blue-400 bg-blue-500/10' : 'border-gray-600 hover:border-gray-500 hover:bg-gray-800/50' }`} >

{uploadedImage ? 'Image Selected' : 'Drop your image here'}

{uploadedImage ? `Selected: ${uploadedImage.name}` : 'or click to browse files (JPEG, PNG)' }

{uploadedImage && (
Preview
)}
{/* Figure Style Selection */}
Choose a figure style (optional)
{figureStyles.map((style) => ( ))}
{/* Custom prompt input */}