diff --git a/src/app/api/mockup/generate/route.ts b/src/app/api/mockup/generate/route.ts index f3172cd..4f6eaf6 100644 --- a/src/app/api/mockup/generate/route.ts +++ b/src/app/api/mockup/generate/route.ts @@ -6,10 +6,17 @@ import { getTemplateImagePath, listTemplates } from '@/lib/mockupTemplates' import type { MockupGenerateRequest } from '@/types/mockup' function buildPrompt(req: MockupGenerateRequest): string { + let carrelageContext = 'tiles covering the floor or wall of a modern room' + if (req.family === 'Carrelage' && req.placement === 'mur') { + carrelageContext = 'wall tiles (faïence) covering the wall of a modern bathroom or kitchen — applied only to the wall, not the floor' + } else if (req.family === 'Carrelage' && req.placement === 'sol') { + carrelageContext = 'floor tiles covering the floor of a modern room — applied only to the floor, not the walls' + } + const familyContext: Record = { 'Menuiserie': 'door installed in a residential entrance or hallway, visible wall and floor context', 'Baies vitrées': 'bay window or sliding door installed in a living room with garden view', - 'Carrelage': 'tiles covering the floor or wall of a modern room', + 'Carrelage': carrelageContext, } const context = familyContext[req.family] ?? 'product installed in an appropriate room setting' const dimClause = req.dimensions diff --git a/src/app/api/mockup/templates/route.ts b/src/app/api/mockup/templates/route.ts index fd116ef..17b43f9 100644 --- a/src/app/api/mockup/templates/route.ts +++ b/src/app/api/mockup/templates/route.ts @@ -17,6 +17,7 @@ export async function POST(req: NextRequest) { const form = await req.formData() const name = form.get('name') as string const family = form.get('family') as string + const placement = (form.get('placement') as string) || undefined const description = (form.get('description') as string) ?? '' const imageFile = form.get('image') as File @@ -26,6 +27,6 @@ export async function POST(req: NextRequest) { const buffer = Buffer.from(await imageFile.arrayBuffer()) const ext = imageFile.name.split('.').pop() ?? 'jpg' - const template = await saveTemplate({ name, family, description, filename: '' }, buffer, ext) + const template = await saveTemplate({ name, family, placement, description, filename: '' }, buffer, ext) return Response.json(template) } diff --git a/src/components/mockup/MockupModal.tsx b/src/components/mockup/MockupModal.tsx index 9f74d65..0acf950 100644 --- a/src/components/mockup/MockupModal.tsx +++ b/src/components/mockup/MockupModal.tsx @@ -35,6 +35,7 @@ export function MockupModal({ product, onClose }: Props) { const [selectedTemplate, setSelectedTemplate] = useState(null) const [family, setFamily] = useState('') + const [placement, setPlacement] = useState('') const [selectedMeta, setSelectedMeta] = useState([]) const [cutoutB64, setCutoutB64] = useState(null) const [selectedImageUrl, setSelectedImageUrl] = useState(null) @@ -67,9 +68,13 @@ export function MockupModal({ product, onClose }: Props) { }).finally(() => setLoadingMeta(false)) }, [product.shopifyId]) - const filteredTemplates = templates.filter(t => - !family || t.family === family || t.family === 'Tous' - ) + const isCarrelage = family.toLowerCase().includes('carrelage') + + const filteredTemplates = templates.filter(t => { + if (t.family !== family && t.family !== 'Tous') return false + if (isCarrelage && placement && t.placement && t.placement !== placement) return false + return true + }) const dimensionStr = selectedMeta.length > 0 ? selectedMeta.map(key => { @@ -137,6 +142,7 @@ export function MockupModal({ product, onClose }: Props) { productImageBase64: cutoutB64, productName: product.title, family: family || 'Tous', + placement: placement || undefined, dimensions: dimensionStr, instruction: instruction || undefined, }), @@ -218,14 +224,29 @@ export function MockupModal({ product, onClose }: Props) { )} - {/* 2. Famille */} + {/* 2. Famille + placement */}

2. Famille

{families.length > 0 ? ( - +
+ + {isCarrelage && ( +
+

Placement

+
+ {[{ val: 'sol', label: 'Sol' }, { val: 'mur', label: 'Mur (faïence)' }].map(({ val, label }) => ( + + ))} +
+
+ )} +
) : (

{loadingMeta ? 'Chargement…' : 'Aucune famille disponible'}

)} diff --git a/src/components/mockup/TemplateLibrary.tsx b/src/components/mockup/TemplateLibrary.tsx index d344d2c..57ba824 100644 --- a/src/components/mockup/TemplateLibrary.tsx +++ b/src/components/mockup/TemplateLibrary.tsx @@ -2,6 +2,8 @@ import { useState, useEffect, useRef } from 'react' import type { MockupTemplate } from '@/types/mockup' +const CARRELAGE_PLACEMENTS = ['sol', 'mur'] + export function TemplateLibrary() { const [templates, setTemplates] = useState([]) const [families, setFamilies] = useState(['Tous']) @@ -10,6 +12,7 @@ export function TemplateLibrary() { const [error, setError] = useState(null) const [name, setName] = useState('') const [family, setFamily] = useState('Tous') + const [placement, setPlacement] = useState('') const [description, setDescription] = useState('') const fileRef = useRef(null) @@ -31,10 +34,13 @@ export function TemplateLibrary() { useEffect(load, []) + const isCarrelage = family.toLowerCase().includes('carrelage') + const upload = async (e: React.FormEvent) => { e.preventDefault() const file = fileRef.current?.files?.[0] if (!file || !name || !family) return + if (isCarrelage && !placement) return setUploading(true) setError(null) try { @@ -42,10 +48,11 @@ export function TemplateLibrary() { form.append('name', name) form.append('family', family) form.append('description', description) + if (isCarrelage && placement) form.append('placement', placement) form.append('image', file) const res = await fetch('/api/mockup/templates', { method: 'POST', body: form }) if (!res.ok) throw new Error((await res.json()).error) - setName(''); setDescription('') + setName(''); setDescription(''); setPlacement('') if (fileRef.current) fileRef.current.value = '' load() } catch (e) { @@ -63,6 +70,16 @@ export function TemplateLibrary() { const allUsedFamilies = Array.from(new Set(templates.map(t => t.family))) const displayFamilies = families.filter(f => allUsedFamilies.includes(f) || f === 'Tous') + const groupedTemplates = (familyName: string) => { + const byFamily = templates.filter(t => t.family === familyName) + const isCarr = familyName.toLowerCase().includes('carrelage') + if (!isCarr) return [{ label: null, items: byFamily }] + return CARRELAGE_PLACEMENTS.map(p => ({ + label: p === 'mur' ? 'Mur (faïence)' : 'Sol', + items: byFamily.filter(t => t.placement === p), + })).filter(g => g.items.length > 0) + } + return (
{/* Upload form */} @@ -73,21 +90,36 @@ export function TemplateLibrary() {
setName(e.target.value)} - placeholder="ex: Entrée moderne" + placeholder="ex: Salle de bain moderne" className="w-full bg-slate-700 border border-slate-600 rounded px-2 py-1.5 text-sm text-white focus:outline-none focus:ring-1 focus:ring-indigo-500" />
- { setFamily(e.target.value); setPlacement('') }} className="w-full bg-slate-700 border border-slate-600 rounded px-2 py-1.5 text-sm text-white focus:outline-none focus:ring-1 focus:ring-indigo-500"> {families.map(f => )}
+ + {isCarrelage && ( +
+ +
+ {CARRELAGE_PLACEMENTS.map(p => ( + + ))} +
+
+ )} +
setDescription(e.target.value)} - placeholder="ex: Pièce lumineuse avec parquet" + placeholder="ex: Salle de bain lumineuse" className="w-full bg-slate-700 border border-slate-600 rounded px-2 py-1.5 text-sm text-white focus:outline-none focus:ring-1 focus:ring-indigo-500" />
@@ -95,7 +127,7 @@ export function TemplateLibrary() {
{error &&

{error}

} - @@ -110,27 +142,37 @@ export function TemplateLibrary() { ) : (
{displayFamilies.map(f => { - const byFamily = templates.filter(t => t.family === f) - if (byFamily.length === 0) return null + const groups = groupedTemplates(f) + const total = groups.reduce((n, g) => n + g.items.length, 0) + if (total === 0) return null return (
-

+

{f} - {byFamily.length} template{byFamily.length > 1 ? 's' : ''} + {total} template{total > 1 ? 's' : ''}

-
- {byFamily.map(t => ( -
- {t.name} -
-

{t.name}

- {t.description &&

{t.description}

} +
+ {groups.map((group, gi) => ( +
+ {group.label && ( +

{group.label}

+ )} +
+ {group.items.map(t => ( +
+ {t.name} +
+

{t.name}

+ {t.description &&

{t.description}

} +
+ +
+ ))}
-
))}
diff --git a/src/types/mockup.ts b/src/types/mockup.ts index 68fe557..a8794eb 100644 --- a/src/types/mockup.ts +++ b/src/types/mockup.ts @@ -2,6 +2,7 @@ export interface MockupTemplate { id: string name: string family: string + placement?: string filename: string description: string } @@ -12,6 +13,7 @@ export interface MockupGenerateRequest { productName: string dimensions?: string family: string + placement?: string instruction?: string }