@@ -178,6 +178,71 @@ function VariantGroupEditor({
178178 </ div >
179179 )
180180}
181+
182+ function ProductListItem ( { product, isActive, onSelect, collectionName } ) {
183+ const rawPreviewImage =
184+ Array . isArray ( product . images ) && product . images . length > 0
185+ ? product . images [ 0 ]
186+ : product . imageUrl || ''
187+
188+ const previewImage = useMemo ( ( ) => normalizeImageUrl ( rawPreviewImage ) , [ rawPreviewImage ] )
189+
190+ return (
191+ < button
192+ type = "button"
193+ onClick = { ( ) => onSelect ( product . id ) }
194+ className = { `w-full rounded-lg border p-3 text-left transition-all duration-200 ${
195+ isActive
196+ ? 'border-[var(--admin-accent)] bg-[var(--admin-accent)]/10 shadow-[var(--admin-shadow)]'
197+ : 'border-[var(--admin-border-primary)] bg-[var(--admin-bg-elevated)] hover:border-[var(--admin-border-secondary)]'
198+ } `}
199+ >
200+ < div className = "flex gap-3" >
201+ { previewImage ? (
202+ < img
203+ src = { previewImage }
204+ alt = { product . name }
205+ className = "h-12 w-12 flex-shrink-0 rounded-md object-cover"
206+ />
207+ ) : (
208+ < div className = "flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-md bg-[var(--admin-bg-secondary)]" >
209+ < Package className = "h-6 w-6 text-[var(--admin-text-muted)]" />
210+ </ div >
211+ ) }
212+ < div className = "flex-1 space-y-1" >
213+ < div className = "flex items-center justify-between gap-3" >
214+ < span className = "font-medium text-[var(--admin-text-primary)] text-sm truncate" >
215+ { product . name || 'Untitled product' }
216+ </ span >
217+ < span className = "text-xs font-semibold text-[var(--admin-text-secondary)]" >
218+ { formatCurrency ( product . price , product . currency ) }
219+ </ span >
220+ </ div >
221+ < p className = "text-xs text-[var(--admin-text-muted)] truncate" >
222+ { product . tagline || product . description || 'No description provided.' }
223+ </ p >
224+ < div className = "flex flex-wrap items-center gap-1.5 text-[11px]" >
225+ { collectionName ? (
226+ < span className = "rounded-full bg-[var(--admin-bg-secondary)] px-2 py-0.5 text-[var(--admin-text-secondary)]" >
227+ { collectionName }
228+ </ span >
229+ ) : (
230+ < span className = "rounded-full bg-[var(--admin-bg-secondary)] px-2 py-0.5 text-[var(--admin-text-muted)]" >
231+ No collection
232+ </ span >
233+ ) }
234+ { product . archived && (
235+ < span className = "rounded-full bg-[var(--admin-text-muted)]/20 px-2 py-0.5 text-[var(--admin-text-secondary)]" >
236+ Archived
237+ </ span >
238+ ) }
239+ </ div >
240+ </ div >
241+ </ div >
242+ </ button >
243+ )
244+ }
245+
181246export function ProductWorkspace ( ) {
182247 const [ products , setProducts ] = useState ( [ ] )
183248 const [ collections , setCollections ] = useState ( [ ] )
@@ -694,68 +759,18 @@ export function ProductWorkspace() {
694759 </ div >
695760 ) : (
696761 filteredProducts . map ( ( product ) => {
697- const previewImage =
698- Array . isArray ( product . images ) && product . images . length > 0
699- ? product . images [ 0 ]
700- : product . imageUrl || ''
701762 const isActive = ! isCreating && selectedId === product . id
702763 const collectionName = product . collectionId
703764 ? collectionLookup . get ( product . collectionId )
704765 : null
705766 return (
706- < button
767+ < ProductListItem
707768 key = { product . id }
708- type = "button"
709- onClick = { ( ) => handleSelectProduct ( product . id ) }
710- className = { `w-full rounded-lg border p-3 text-left transition-all duration-200 ${
711- isActive
712- ? 'border-[var(--admin-accent)] bg-[var(--admin-accent)]/10 shadow-[var(--admin-shadow)]'
713- : 'border-[var(--admin-border-primary)] bg-[var(--admin-bg-elevated)] hover:border-[var(--admin-border-secondary)]'
714- } `}
715- >
716- < div className = "flex gap-3" >
717- { previewImage ? (
718- < img
719- src = { normalizeImageUrl ( previewImage ) }
720- alt = { product . name }
721- className = "h-12 w-12 flex-shrink-0 rounded-md object-cover"
722- />
723- ) : (
724- < div className = "flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-md bg-[var(--admin-bg-secondary)]" >
725- < Package className = "h-6 w-6 text-[var(--admin-text-muted)]" />
726- </ div >
727- ) }
728- < div className = "flex-1 space-y-1" >
729- < div className = "flex items-center justify-between gap-3" >
730- < span className = "font-medium text-[var(--admin-text-primary)] text-sm truncate" >
731- { product . name || 'Untitled product' }
732- </ span >
733- < span className = "text-xs font-semibold text-[var(--admin-text-secondary)]" >
734- { formatCurrency ( product . price , product . currency ) }
735- </ span >
736- </ div >
737- < p className = "text-xs text-[var(--admin-text-muted)] truncate" >
738- { product . tagline || product . description || 'No description provided.' }
739- </ p >
740- < div className = "flex flex-wrap items-center gap-1.5 text-[11px]" >
741- { collectionName ? (
742- < span className = "rounded-full bg-[var(--admin-bg-secondary)] px-2 py-0.5 text-[var(--admin-text-secondary)]" >
743- { collectionName }
744- </ span >
745- ) : (
746- < span className = "rounded-full bg-[var(--admin-bg-secondary)] px-2 py-0.5 text-[var(--admin-text-muted)]" >
747- No collection
748- </ span >
749- ) }
750- { product . archived && (
751- < span className = "rounded-full bg-[var(--admin-text-muted)]/20 px-2 py-0.5 text-[var(--admin-text-secondary)]" >
752- Archived
753- </ span >
754- ) }
755- </ div >
756- </ div >
757- </ div >
758- </ button >
769+ product = { product }
770+ isActive = { isActive }
771+ collectionName = { collectionName }
772+ onSelect = { handleSelectProduct }
773+ />
759774 )
760775 } )
761776 ) }
0 commit comments