11"use client" ;
22
3- import { Home , ImageIcon , Settings , User } from "lucide-react" ;
3+ import {
4+ Download ,
5+ Home ,
6+ ImageIcon ,
7+ Settings ,
8+ User ,
9+ WandSparklesIcon ,
10+ } from "lucide-react" ;
411import { useState } from "react" ;
512import CustomPromptCard from "./components/CustomPromptCard" ;
613import GeneratedCard from "./components/GeneratedCard" ;
@@ -20,6 +27,11 @@ export default function ProductImageGenerator({
2027 const [ productAnalysis , setProductAnalysis ] =
2128 useState < ProductAnalysis | null > ( null ) ;
2229
30+ // Track all generated images
31+ const [ generatedImages , setGeneratedImages ] = useState < {
32+ [ key : string ] : { runId : string ; prompt : string ; imageUrl ?: string } ;
33+ } > ( { } ) ;
34+
2335 // Track custom generations for bottom row
2436 const [ customGenerations , setCustomGenerations ] = useState < {
2537 runIds : ( string | null ) [ ] ;
@@ -39,15 +51,151 @@ export default function ProductImageGenerator({
3951 }
4052 } ;
4153
54+ const handleGenerationComplete = (
55+ runId : string ,
56+ prompt : string ,
57+ imageUrl ?: string ,
58+ key ?: string
59+ ) => {
60+ console . log ( "Generation completed:" , { runId, prompt, imageUrl, key } ) ;
61+ setGeneratedImages ( ( prev ) => {
62+ const updated = {
63+ ...prev ,
64+ [ key || runId ] : { runId, prompt, imageUrl } ,
65+ } ;
66+ console . log ( "Updated generated images:" , updated ) ;
67+ return updated ;
68+ } ) ;
69+ } ;
70+
4271 const handleCustomGenerationComplete = (
4372 runId : string ,
4473 prompt : string ,
45- index : number
74+ index : number ,
75+ imageUrl ?: string
4676 ) => {
4777 setCustomGenerations ( ( prev ) => ( {
4878 runIds : prev . runIds . map ( ( id , i ) => ( i === index ? runId : id ) ) ,
4979 prompts : prev . prompts . map ( ( p , i ) => ( i === index ? prompt : p ) ) ,
5080 } ) ) ;
81+ handleGenerationComplete ( runId , prompt , imageUrl , `custom-${ index } ` ) ;
82+ } ;
83+
84+ const handlePresetGenerationComplete = (
85+ runId : string ,
86+ promptId : string ,
87+ promptTitle : string ,
88+ imageUrl ?: string
89+ ) => {
90+ handleGenerationComplete ( runId , promptTitle , imageUrl , promptId ) ;
91+ } ;
92+
93+ const handleDownloadAll = async ( ) => {
94+ console . log ( "Download button clicked!" ) ;
95+ console . log ( "Generated images:" , generatedImages ) ;
96+ console . log ( "Total generated images:" , totalGeneratedImages ) ;
97+
98+ if ( totalGeneratedImages === 0 ) {
99+ console . log ( "No images to download" ) ;
100+ return ;
101+ }
102+
103+ try {
104+ // For a single image, download directly
105+ if ( totalGeneratedImages === 1 ) {
106+ const imageData = Object . values ( generatedImages ) [ 0 ] ;
107+ console . log ( "Single image data:" , imageData ) ;
108+
109+ if ( imageData . imageUrl ) {
110+ console . log ( "Downloading single image:" , imageData . imageUrl ) ;
111+
112+ // Try to fetch the image first to handle CORS
113+ try {
114+ const response = await fetch ( imageData . imageUrl ) ;
115+ const blob = await response . blob ( ) ;
116+ const url = window . URL . createObjectURL ( blob ) ;
117+
118+ const link = document . createElement ( "a" ) ;
119+ link . href = url ;
120+ link . download = `${ imageData . prompt
121+ . replace ( / \s + / g, "-" )
122+ . toLowerCase ( ) } -${ Date . now ( ) } .png`;
123+ document . body . appendChild ( link ) ;
124+ link . click ( ) ;
125+ document . body . removeChild ( link ) ;
126+
127+ // Clean up the blob URL
128+ window . URL . revokeObjectURL ( url ) ;
129+ } catch ( fetchError ) {
130+ console . log ( "Fetch failed, trying direct download:" , fetchError ) ;
131+ // Fallback to direct download
132+ const link = document . createElement ( "a" ) ;
133+ link . href = imageData . imageUrl ;
134+ link . download = `${ imageData . prompt
135+ . replace ( / \s + / g, "-" )
136+ . toLowerCase ( ) } -${ Date . now ( ) } .png`;
137+ document . body . appendChild ( link ) ;
138+ link . click ( ) ;
139+ document . body . removeChild ( link ) ;
140+ }
141+ } else {
142+ console . log ( "No image URL found for single image" ) ;
143+ }
144+ return ;
145+ }
146+
147+ // For multiple images, download each individually
148+ console . log ( "Downloading multiple images..." ) ;
149+ Object . entries ( generatedImages ) . forEach ( ( [ key , imageData ] , index ) => {
150+ console . log ( `Image ${ index + 1 } :` , key , imageData ) ;
151+
152+ if ( imageData . imageUrl ) {
153+ setTimeout ( async ( ) => {
154+ try {
155+ console . log (
156+ `Downloading image ${ index + 1 } :` ,
157+ imageData . imageUrl
158+ ) ;
159+
160+ // Try to fetch the image first to handle CORS
161+ const response = await fetch ( imageData . imageUrl ! ) ;
162+ const blob = await response . blob ( ) ;
163+ const url = window . URL . createObjectURL ( blob ) ;
164+
165+ const link = document . createElement ( "a" ) ;
166+ link . href = url ;
167+ link . download = `${ imageData . prompt
168+ . replace ( / \s + / g, "-" )
169+ . toLowerCase ( ) } -${ Date . now ( ) } -${ index + 1 } .png`;
170+ document . body . appendChild ( link ) ;
171+ link . click ( ) ;
172+ document . body . removeChild ( link ) ;
173+
174+ // Clean up the blob URL
175+ window . URL . revokeObjectURL ( url ) ;
176+ } catch ( fetchError ) {
177+ console . log (
178+ `Fetch failed for image ${ index + 1 } , trying direct download:` ,
179+ fetchError
180+ ) ;
181+ // Fallback to direct download
182+ const link = document . createElement ( "a" ) ;
183+ link . href = imageData . imageUrl ! ;
184+ link . download = `${ imageData . prompt
185+ . replace ( / \s + / g, "-" )
186+ . toLowerCase ( ) } -${ Date . now ( ) } -${ index + 1 } .png`;
187+ document . body . appendChild ( link ) ;
188+ link . click ( ) ;
189+ document . body . removeChild ( link ) ;
190+ }
191+ } , index * 1000 ) ; // Stagger downloads by 1 second
192+ } else {
193+ console . log ( `No image URL found for image ${ index + 1 } ` ) ;
194+ }
195+ } ) ;
196+ } catch ( error ) {
197+ console . error ( "Failed to download images:" , error ) ;
198+ }
51199 } ;
52200
53201 const promptTitles = {
@@ -56,6 +204,10 @@ export default function ProductImageGenerator({
56204 "hero-shot" : "Hero Shot" ,
57205 } ;
58206
207+ // Calculate total generated images
208+ const totalGeneratedImages = Object . keys ( generatedImages ) . length ;
209+ const hasGeneratedImages = totalGeneratedImages > 0 ;
210+
59211 // Determine which custom cards have completed generations
60212 const completedCustomCards = customGenerations . runIds . filter (
61213 ( runId ) => runId !== null
@@ -71,14 +223,14 @@ export default function ProductImageGenerator({
71223 uploadedImageUrl && productAnalysis ? true : false ;
72224
73225 return (
74- < div className = "min-h-screen bg-gray-100/10 " >
226+ < div className = "min-h-screen bg-gray-100/20 " >
75227 { /* Fixed Header */ }
76228 < header className = "sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60" >
77229 < div className = "container mx-auto px-4" >
78230 < div className = "flex h-14 items-center justify-between" >
79231 < div className = "flex items-center space-x-4" >
80232 < div className = "flex items-center space-x-2" >
81- < ImageIcon className = "h-5 w-5 text-purple-500" />
233+ < WandSparklesIcon className = "h-5 w-5 text-purple-500" />
82234 < h1 className = "text-xl font-bold text-foreground" > ImageFlow</ h1 >
83235 </ div >
84236 </ div >
@@ -105,14 +257,35 @@ export default function ProductImageGenerator({
105257 < main className = "container mx-auto px-4 py-16" >
106258 < div className = "max-w-7xl" >
107259 { /* Page Title */ }
108- < div className = "mb-8" >
109- < h2 className = "text-3xl font-bold text-foreground mb-2" >
110- Product Image Generator
111- </ h2 >
112- < p className = "text-muted-foreground" >
113- Upload a product image and generate professional marketing shots
114- for your online store.
115- </ p >
260+ < div className = "mb-8 flex justify-between items-end" >
261+ < div >
262+ < h2 className = "text-3xl font-bold text-foreground mb-2" >
263+ Product Image Generator
264+ </ h2 >
265+ < p className = "text-muted-foreground" >
266+ Upload a product image and generate professional marketing shots
267+ for your online store.
268+ </ p >
269+ </ div >
270+ < div >
271+ < Button
272+ variant = { hasGeneratedImages ? "default" : "outline" }
273+ disabled = { ! hasGeneratedImages }
274+ onClick = { handleDownloadAll }
275+ className = {
276+ ! hasGeneratedImages
277+ ? "opacity-50 cursor-not-allowed"
278+ : "cursor-pointer"
279+ }
280+ >
281+ < Download className = "h-4 w-4 mr-1" />
282+ { hasGeneratedImages
283+ ? `Download ${ totalGeneratedImages } image${
284+ totalGeneratedImages === 1 ? "" : "s"
285+ } `
286+ : "Download images" }
287+ </ Button >
288+ </ div >
116289 </ div >
117290
118291 { /* Top Row - Upload + 3 Generated Images */ }
@@ -127,18 +300,21 @@ export default function ProductImageGenerator({
127300 productAnalysis = { productAnalysis }
128301 promptId = "isolated-table"
129302 promptTitle = { promptTitles [ "isolated-table" ] }
303+ onGenerationComplete = { handlePresetGenerationComplete }
130304 />
131305 < GeneratedCard
132306 baseImageUrl = { uploadedImageUrl }
133307 productAnalysis = { productAnalysis }
134308 promptId = "lifestyle-scene"
135309 promptTitle = { promptTitles [ "lifestyle-scene" ] }
310+ onGenerationComplete = { handlePresetGenerationComplete }
136311 />
137312 < GeneratedCard
138313 baseImageUrl = { uploadedImageUrl }
139314 productAnalysis = { productAnalysis }
140315 promptId = "hero-shot"
141316 promptTitle = { promptTitles [ "hero-shot" ] }
317+ onGenerationComplete = { handlePresetGenerationComplete }
142318 />
143319 </ div >
144320
@@ -151,8 +327,13 @@ export default function ProductImageGenerator({
151327 key = { `custom-prompt-${ index } ` }
152328 baseImageUrl = { uploadedImageUrl }
153329 productAnalysis = { productAnalysis }
154- onGenerationComplete = { ( runId , prompt ) =>
155- handleCustomGenerationComplete ( runId , prompt , index )
330+ onGenerationComplete = { ( runId , prompt , imageUrl ) =>
331+ handleCustomGenerationComplete (
332+ runId ,
333+ prompt ,
334+ index ,
335+ imageUrl
336+ )
156337 }
157338 />
158339 ) ;
0 commit comments