import React, { useState, useRef } from ‘react’;
import { Camera, Zap, Download, Share2, RefreshCw, User, Users, Box, Smile, Loader2, Check, Star, Image as ImageIcon } from ‘lucide-react’;

// — Configuration de l’API —
// La clé est gérée automatiquement par l’environnement de prévisualisation ici.
const apiKey = «  »;

// — Styles de Jouets —
const TOY_STYLES = [
{
id: ‘brick’,
name: ‘Brick Master’,
shortName: ‘Brick’,
icon: ,
description: « Style figurine de construction »,
basePrompt: « Transforme le(s) sujet(s) en figurine(s) style LEGO 3D réaliste. Format VERTICAL (Portrait). On doit voir la boîte en carton ENTIÈRE posée sur une surface. La figurine est visible en entier sur le devant de la boîte. »
},
{
id: ‘pop’,
name: ‘Pop Head’,
shortName: ‘Pop’,
icon: ,
description: « Style grosse tête »,
basePrompt: « Transforme le(s) sujet(s) en figurine(s) vinyle style Funko Pop. Format VERTICAL (Portrait). On doit voir la boîte à fenêtre ENTIÈREMENT. La figurine est visible en entier à côté ou à l’intérieur de la boîte. »
},
{
id: ‘doll’,
name: ‘Fashion Doll’,
shortName: ‘Fashion’,
icon: ,
description: « Style poupée mannequin »,
basePrompt: « Transforme le(s) sujet(s) en poupée(s) mannequin réaliste. Format VERTICAL (Portrait). On doit voir la boîte d’emballage ENTIÈREMENT (haut et bas visibles). La poupée doit être visible de la tête aux pieds. »
},
{
id: ‘action’,
name: ‘Action Hero’,
shortName: ‘Action’,
icon: ,
description: « Style figurine d’action »,
basePrompt: « Transforme le(s) sujet(s) en figurine(s) d’action vintage style GI Joe. Format VERTICAL (Portrait). On doit voir la carte blister (emballage cartonné) ENTIÈREMENT sans coupure. La figurine est visible en entier sous sa bulle plastique. »
}
];

// — Utils —
const downloadImage = (dataUrl, filename) => {
const link = document.createElement(‘a’);
link.href = dataUrl;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};

// Composant Sélecteur Genre Compact
const GenderSelector = ({ gender, setGender }) => (


);

export default function App() {
const [images, setImages] = useState({ 1: null, 2: null });
const [base64Images, setBase64Images] = useState({ 1: null, 2: null });
const [numberOfPeople, setNumberOfPeople] = useState(‘1’);
const [userName1, setUserName1] = useState( »);
const [userName2, setUserName2] = useState( »);
const [gender1, setGender1] = useState(‘boy’);
const [gender2, setGender2] = useState(‘girl’);
const [isProcessing, setIsProcessing] = useState(false);
const [generatedToys, setGeneratedToys] = useState({});
const [currentStep, setCurrentStep] = useState( »);
const [error, setError] = useState( »);

const fileInputRef1 = useRef(null);
const fileInputRef2 = useRef(null);

const handleImageUpload = (e, index) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
setImages(prev => ({ …prev, [index]: URL.createObjectURL(file) }));
const base64 = reader.result.split(‘,’)[1];
setBase64Images(prev => ({ …prev, [index]: base64 }));
if (Object.keys(generatedToys).length > 0) setGeneratedToys({});
setError( »);
};
reader.readAsDataURL(file);
}
};

const handleReset = () => {
setImages({ 1: null, 2: null });
setBase64Images({ 1: null, 2: null });
setGeneratedToys({});
setUserName1( »);
setUserName2( »);
setError( »);
};

const generateSingleToy = async (style) => {
try {
let subjectsDescription =  »;
let finalBoxName =  »;
let styleAdaptation =  »;
let imageParts = [];

if (numberOfPeople === ‘1’) {
imageParts.push({ inlineData: { mimeType: « image/jpeg », data: base64Images[1] } });
const genderTerm = gender1 === ‘girl’ ? ‘femme/fille’ : ‘homme/garçon’;
subjectsDescription = `Le sujet est un(e) seul(e) ${genderTerm} (voir image fournie).`;
finalBoxName = userName1.trim() || ‘TOY’;
const dollContext = gender1 === ‘girl’ ? ‘style Barbie’ : ‘style Ken’;
styleAdaptation = `Adapte le style (${dollContext} pour le style Fashion Doll) au genre ${genderTerm}.`;
} else {
if (base64Images[1]) imageParts.push({ inlineData: { mimeType: « image/jpeg », data: base64Images[1] } });
if (base64Images[2]) imageParts.push({ inlineData: { mimeType: « image/jpeg », data: base64Images[2] } });
const genderTerm1 = gender1 === ‘girl’ ? ‘femme/fille’ : ‘homme/garçon’;
const genderTerm2 = gender2 === ‘girl’ ? ‘femme/fille’ : ‘homme/garçon’;
subjectsDescription = `Il y a DEUX sujets fournis en images séparées. Sujet 1: ${genderTerm1}. Sujet 2: ${genderTerm2}. Crée un duo de figurines.`;
finalBoxName = `${userName1.trim()} & ${userName2.trim()}` || ‘TOYS DUO’;
let dollContext = ‘style duo de poupées’;
if (gender1 === ‘girl’ && gender2 === ‘girl’) dollContext = ‘style duo Barbie & Barbie’;
else if (gender1 === ‘boy’ && gender2 === ‘boy’) dollContext = ‘style duo Ken & Ken’;
else dollContext = ‘style duo Barbie & Ken’;
styleAdaptation = `Adapte le style (${dollContext} pour le style Fashion Doll).`;
}

let finalPrompt = `${style.basePrompt} `;
finalPrompt += `${subjectsDescription} `;
// IMPORTANT: On ne demande plus à l’IA d’écrire « Noël à Aurillac », on le fait via le sticker
finalPrompt += `IMPORTANT: Le texte « ${finalBoxName} » doit être écrit CLAIREMENT et LISIBLEMENT sur la boîte. Ne PAS ajouter d’autre texte promotionnel sur la boîte (pas de date, pas de lieu). `;
finalPrompt += `CADRAGE: Vue VERTICALE (Portrait). La boîte doit être visible en entier avec de l’espace en bas à droite pour coller un sticker. `;
finalPrompt += `${styleAdaptation} Garde les cheveux et vêtements reconnaissables mais « jouetifiés ».`;

const parts = [{ text: finalPrompt }, …imageParts];

const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image-preview:generateContent?key=${apiKey}`,
{
method: ‘POST’,
headers: { ‘Content-Type’: ‘application/json’ },
body: JSON.stringify({
contents: [{ parts }],
generationConfig: {
responseModalities: [« IMAGE »],
aspectRatio: « 3:4 »
},
safetySettings: [
{ category: « HARM_CATEGORY_HARASSMENT », threshold: « BLOCK_ONLY_HIGH » },
{ category: « HARM_CATEGORY_HATE_SPEECH », threshold: « BLOCK_ONLY_HIGH » },
{ category: « HARM_CATEGORY_SEXUALLY_EXPLICIT », threshold: « BLOCK_ONLY_HIGH » },
{ category: « HARM_CATEGORY_DANGEROUS_CONTENT », threshold: « BLOCK_ONLY_HIGH » }
]
})
}
);

if (!response.ok) throw new Error(‘API Error’);
const data = await response.json();
const base64Result = data.candidates?.[0]?.content?.parts?.find(p => p.inlineData)?.inlineData?.data;

if (base64Result) return `data:image/jpeg;base64,${base64Result}`;
throw new Error(‘Pas d\’image générée’);
} catch (err) {
console.error(err);
return null;
}
};

const handleGenerateAll = async () => {
if (!base64Images[1]) return;
if (numberOfPeople === ‘2’ && !base64Images[2]) { setError(« Deuxième photo manquante ! »); return; }
if (numberOfPeople === ‘1’ && !userName1.trim()) { setError(« Prénom requis ! »); return; }
if (numberOfPeople === ‘2’ && (!userName1.trim() || !userName2.trim())) { setError(« Prénoms requis ! »); return; }

setIsProcessing(true);
setError( »);
setGeneratedToys({});

setCurrentStep(numberOfPeople === ‘2’ ? ‘Analyse du duo…’ : ‘Analyse du sujet…’);
await new Promise(r => setTimeout(r, 500));

for (const style of TOY_STYLES) {
setCurrentStep(`Fabrication : ${style.name}…`);
const resultUrl = await generateSingleToy(style);
if (resultUrl) {
setGeneratedToys(prev => ({ …prev, [style.id]: resultUrl }));
}
}
setIsProcessing(false);
setCurrentStep( »);
};

const getFileName = () => {
const n1 = userName1.trim();
const n2 = userName2.trim();
if (numberOfPeople === ‘1’) return n1 || ‘toy’;
return (n1 && n2) ? `${n1}-${n2}` : ‘duo’;
};

// Upload UI Component
const UploadBox = ({ index, label, image, inputRef }) => (

{label}

inputRef.current?.click()}
className= »relative w-full aspect-square bg-black rounded-xl border-2 border-dashed border-zinc-800 hover:border-yellow-400 cursor-pointer overflow-hidden group transition-all »
>
{image ? (
<>
Upload

</>
) : (

Ajouter Photo

)}

handleImageUpload(e, index)} accept= »image/* » className= »hidden » />

);

return (

{/* HEADER MOBILE */}

T

ToyFace

NOËL 2025

{/* SIDEBAR (GAUCHE) */}

{/* MAIN CONTENT (DROITE / BAS) – YELLOW */}

VOS JOUETS

Collection Noël Aurillac 2025

{!images[1] ? (

Configurez votre jouet
dans le menu

) : (

{TOY_STYLES.map((style) => (

{generatedToys[style.id] ? (
<>
{style.name}{/* STICKER AVEC L’IMAGE FOURNIE PAR L’UTILISATEUR (Overlay) */}
{/* Important: Le fichier logo.jpg doit être dans le même dossier */}
Logo Aurillac Noël 2025 e.target.style.display = ‘none’} // Cache l’image si elle n’est pas trouvée
/>
</>
) : (

{style.icon}
{style.name}
{isProcessing && !generatedToys[style.id] && (

Fabrication…

)}

)}

{generatedToys[style.id] && (


)}

))}

)}

 

);
}