|
1 | 1 | 'use client' |
2 | 2 |
|
3 | | -import type { Template } from '@/lib/helpers/workspace/media' |
4 | 3 | import { useWorkspaceMedia } from '@/lib/hooks/use-workspace-media' |
5 | | -import type { LucideProps } from 'lucide-react' |
6 | | -import type React from 'react' |
7 | | -import { useEffect, useState } from 'react' |
| 4 | +import { useEffect } from 'react' |
8 | 5 | import { MediaCanvas } from './ui/canvas' |
9 | 6 | import { ReferenceImagesPanel } from './ui/reference-images' |
10 | 7 | import { MediaSidebar } from './ui/sidebar' |
11 | 8 | import { MediaWizards } from './wizards' |
12 | | -import type { FrameSize } from './wizards/steps' |
13 | 9 |
|
14 | 10 | export function MediaWorkspace() { |
15 | | - const [referenceImages, setReferenceImages] = useState<string[]>([]) |
16 | | - const [currentVersion, setCurrentVersion] = useState(0) |
17 | | - const [allTemplates, setAllTemplates] = useState<Template[]>([]) |
18 | | - const [isLoadingTemplates, setIsLoadingTemplates] = useState(true) |
19 | | - |
20 | 11 | // Workspace media context |
21 | | - const { |
22 | | - dialogStep, |
23 | | - generatedImage, |
24 | | - imageError, |
25 | | - isGeneratingImage, |
26 | | - openTemplates, |
27 | | - resetImageGeneration, |
28 | | - selectedSize, |
29 | | - selectedTemplate, |
30 | | - setDialogStep, |
31 | | - setSelectedSize, |
32 | | - setSelectedTemplate, |
33 | | - setShowVersionHistory, |
34 | | - showVersionHistory, |
35 | | - } = useWorkspaceMedia() |
36 | | - |
37 | | - // TODO: Migrate to TanStack Query |
38 | | - // Load templates from API on mount |
39 | | - useEffect(() => { |
40 | | - async function fetchTemplates() { |
41 | | - try { |
42 | | - setIsLoadingTemplates(true) |
43 | | - const response = await fetch('/api/media/templates') |
44 | | - if (!response.ok) { |
45 | | - throw new Error('Failed to fetch templates') |
46 | | - } |
47 | | - const templates: Template[] = await response.json() |
48 | | - setAllTemplates(templates) |
49 | | - } catch (error) { |
50 | | - console.error('Error fetching templates:', error) |
51 | | - setAllTemplates([]) |
52 | | - } finally { |
53 | | - setIsLoadingTemplates(false) |
54 | | - } |
55 | | - } |
56 | | - |
57 | | - fetchTemplates() |
58 | | - }, []) |
59 | | - |
60 | | - // Reference images management |
61 | | - const removeReferenceImage = (index: number) => { |
62 | | - setReferenceImages(referenceImages.filter((_, i) => i !== index)) |
63 | | - } |
64 | | - |
65 | | - const addReferenceImage = (imageUrl: string) => { |
66 | | - if (referenceImages.length < 4) { |
67 | | - setReferenceImages([...referenceImages, imageUrl]) |
68 | | - } |
69 | | - } |
| 12 | + const { generatedImage, resetImageGeneration, selectedTemplate } = |
| 13 | + useWorkspaceMedia() |
70 | 14 |
|
71 | | - const addMultipleReferenceImages = (imageUrls: string[]) => { |
72 | | - const availableSlots = 4 - referenceImages.length |
73 | | - const imagesToAdd = imageUrls.slice(0, availableSlots) |
74 | | - setReferenceImages([...referenceImages, ...imagesToAdd]) |
75 | | - } |
76 | | - |
77 | | - const addImageFromLibrary = (image: string) => { |
78 | | - if (referenceImages.length < 4) { |
79 | | - setReferenceImages([...referenceImages, image]) |
80 | | - } |
81 | | - } |
82 | | - |
83 | | - // Reset generated image when template or size changes |
84 | | - // biome-ignore lint/correctness/useExhaustiveDependencies: intentional reset on template/size change |
| 15 | + // Reset generated image when template changes |
| 16 | + // biome-ignore lint/correctness/useExhaustiveDependencies: intentional reset on template change |
85 | 17 | useEffect(() => { |
86 | 18 | if (generatedImage) { |
87 | 19 | resetImageGeneration() |
88 | 20 | } |
89 | | - }, [selectedTemplate?.id, selectedSize?.id]) |
| 21 | + }, [selectedTemplate?.id]) |
90 | 22 |
|
91 | 23 | return ( |
92 | 24 | <div className="flex flex-col lg:flex-row size-full bg-background min-h-0"> |
93 | 25 | {/* Sidebar Media: Templates, Social Media and Brand Kit */} |
94 | | - <MediaSidebar onTemplatesClick={openTemplates} /> |
| 26 | + <MediaSidebar /> |
95 | 27 |
|
96 | 28 | {/* Main Media Content */} |
97 | 29 | <div className="flex-1 flex flex-col overflow-y-auto overflow-x-hidden min-h-0"> |
98 | 30 | <div className="flex-1 flex flex-col px-3 sm:px-4 lg:px-8 py-3 lg:py-5 gap-2 sm:gap-3 lg:gap-4"> |
99 | 31 | {/* Canvas Area */} |
100 | | - <MediaCanvas |
101 | | - selectedTemplate={selectedTemplate} |
102 | | - selectedSize={selectedSize} |
103 | | - generatedImage={generatedImage} |
104 | | - isGeneratingImage={isGeneratingImage} |
105 | | - imageError={imageError} |
106 | | - showVersionHistory={showVersionHistory} |
107 | | - currentVersion={currentVersion} |
108 | | - versions={versions} |
109 | | - onResetImage={resetImageGeneration} |
110 | | - onCloseVersionHistory={() => setShowVersionHistory(false)} |
111 | | - onVersionChange={setCurrentVersion} |
112 | | - /> |
| 32 | + <MediaCanvas /> |
113 | 33 | </div> |
114 | 34 | </div> |
115 | 35 |
|
116 | 36 | {/* Right Sidebar - Reference Images */} |
117 | | - <ReferenceImagesPanel |
118 | | - referenceImages={referenceImages} |
119 | | - onRemoveImage={removeReferenceImage} |
120 | | - onAddImage={addReferenceImage} |
121 | | - onAddMultipleImages={addMultipleReferenceImages} |
122 | | - /> |
| 37 | + <ReferenceImagesPanel /> |
123 | 38 |
|
124 | 39 | {/* Media Selection Wizards */} |
125 | | - <MediaWizards |
126 | | - dialogStep={dialogStep} |
127 | | - frameSizes={frameSizes} |
128 | | - allTemplates={allTemplates} |
129 | | - isLoadingTemplates={isLoadingTemplates} |
130 | | - libraryImages={libraryImages} |
131 | | - referenceImages={referenceImages} |
132 | | - onDialogClose={() => setDialogStep(null)} |
133 | | - onSizeSelect={setSelectedSize} |
134 | | - onTemplateSelect={setSelectedTemplate} |
135 | | - onImageSelect={addImageFromLibrary} |
136 | | - /> |
| 40 | + <MediaWizards /> |
137 | 41 | </div> |
138 | 42 | ) |
139 | 43 | } |
140 | | - |
141 | | -const frameSizes: FrameSize[] = [ |
142 | | - { |
143 | | - id: 'square', |
144 | | - name: 'Square', |
145 | | - ratio: '1:1', |
146 | | - description: |
147 | | - 'Instagram Post, LinkedIn Ad/Post/Video, Twitter/X Ad, Facebook Post', |
148 | | - aspectClass: 'aspect-square', |
149 | | - }, |
150 | | - { |
151 | | - id: 'landscape', |
152 | | - name: 'Landscape', |
153 | | - ratio: '16:9', |
154 | | - description: 'Twitter/X Post', |
155 | | - aspectClass: 'aspect-video', |
156 | | - }, |
157 | | - { |
158 | | - id: 'portrait', |
159 | | - name: 'Portrait', |
160 | | - ratio: '9:16', |
161 | | - description: 'Instagram Story/Video/Reel, TikTok Video', |
162 | | - aspectClass: 'aspect-[9/16]', |
163 | | - }, |
164 | | - { |
165 | | - id: 'classic', |
166 | | - name: 'Classic', |
167 | | - ratio: '4:5', |
168 | | - description: 'Instagram Post', |
169 | | - aspectClass: 'aspect-[4/5]', |
170 | | - }, |
171 | | - { |
172 | | - id: 'ultrawide', |
173 | | - name: 'Ultra Wide', |
174 | | - ratio: '21:11', |
175 | | - description: 'Facebook Ad', |
176 | | - aspectClass: 'aspect-[21/11]', |
177 | | - }, |
178 | | - { |
179 | | - id: 'poster', |
180 | | - name: 'Poster', |
181 | | - ratio: '2:3', |
182 | | - description: 'Pinterest Pin', |
183 | | - aspectClass: 'aspect-[2/3]', |
184 | | - }, |
185 | | - { |
186 | | - id: 'photo', |
187 | | - name: 'Photo', |
188 | | - ratio: '5:4', |
189 | | - description: 'Facebook Post', |
190 | | - aspectClass: 'aspect-[5/4]', |
191 | | - }, |
192 | | - { |
193 | | - id: 'wide', |
194 | | - name: 'Wide', |
195 | | - ratio: '21:9', |
196 | | - description: 'Banner, Header', |
197 | | - aspectClass: 'aspect-[21/9]', |
198 | | - }, |
199 | | -] |
200 | | - |
201 | | -const libraryImages = [ |
202 | | - 'https://images.unsplash.com/photo-1579546929518-9e396f3cc809?w=200', |
203 | | - 'https://images.unsplash.com/photo-1557804506-669a67965ba0?w=200', |
204 | | - 'https://images.unsplash.com/photo-1557804506-669a67965ba0?w=200', |
205 | | - 'https://images.unsplash.com/photo-1579546929662-711aa81148cf?w=200', |
206 | | - 'https://images.unsplash.com/photo-1501139083538-0139583c060f?w=200', |
207 | | - 'https://images.unsplash.com/photo-1509048191080-d2984bad6ae5?w=200', |
208 | | - 'https://images.unsplash.com/photo-1501139083538-0139583c060f?w=200', |
209 | | - 'https://images.unsplash.com/photo-1509048191080-d2984bad6ae5?w=200', |
210 | | -] |
211 | | - |
212 | | -const versions = [ |
213 | | - { id: 1, thumbnail: 'version-1' }, |
214 | | - { id: 2, thumbnail: 'version-2' }, |
215 | | - { id: 3, thumbnail: 'version-3' }, |
216 | | -] |
217 | | - |
218 | | -export interface MediaCategory { |
219 | | - id: string |
220 | | - name: string |
221 | | - icon: React.ForwardRefExoticComponent< |
222 | | - Omit<LucideProps, 'ref'> & React.RefAttributes<SVGSVGElement> |
223 | | - > |
224 | | - color: string |
225 | | -} |
0 commit comments