Files
gestion-materiaux-destock/src/app/(dashboard)/images/page.tsx
T
2026-06-25 08:30:45 +02:00

141 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState, useCallback } from 'react'
import { Header } from '@/components/layout/Header'
import { DropZone } from '@/components/images/DropZone'
import { ImageCard } from '@/components/images/ImageCard'
import { Lightbox } from '@/components/images/Lightbox'
import { GlobalActions } from '@/components/images/GlobalActions'
import { MockupModalFromImage } from '@/components/mockup/MockupModalFromImage'
import { ErrorBoundary } from '@/components/common/ErrorBoundary'
import { useImages } from '@/hooks/useImages'
import type { ImageItem } from '@/types/images'
function ImagesPageInner() {
const { items, addFiles, setFeather, setAlphaThreshold, rotate, uploadOne, uploadAll, removeItem, assignProduct, reprocess, detour } = useImages()
const [lightboxItem, setLightboxItem] = useState<ImageItem | null>(null)
const [mockupItem, setMockupItem] = useState<ImageItem | null>(null)
const openMockup = useCallback((id: string) => {
const item = items.find(i => i.id === id)
if (item) setMockupItem(item)
}, [items])
const [selected, setSelected] = useState<Set<string>>(new Set())
const toggleSelect = useCallback((id: string, value: boolean) => {
setSelected(prev => {
const next = new Set(prev)
if (value) next.add(id)
else next.delete(id)
return next
})
}, [])
const selectAll = useCallback(() => {
const detourable = items.filter(i =>
i.status === 'converted' || i.status === 'not_found' || i.status === 'error'
)
setSelected(new Set(detourable.map(i => i.id) as string[]))
}, [items])
const clearSelection = useCallback(() => setSelected(new Set()), [])
const detourSelected = useCallback(() => {
Array.from(selected).forEach(id => detour(id))
setSelected(new Set())
}, [selected, detour])
const detourable = items.filter(i =>
i.status === 'converted' || i.status === 'not_found' || i.status === 'error'
)
return (
<>
<Header
title="🖼️ Images"
actions={
<span className="text-xs text-slate-500">
{items.length > 0 ? `${items.length} image${items.length > 1 ? 's' : ''}` : ''}
</span>
}
/>
<div className="p-6 flex flex-col gap-4 flex-1 overflow-y-auto">
<DropZone onFiles={addFiles} />
{items.length > 0 && (
<GlobalActions items={items} onUploadAll={uploadAll} />
)}
{/* Barre de sélection multiple pour détourage par lot */}
{detourable.length > 0 && (
<div className="flex items-center gap-3 px-3 py-2 bg-slate-800 rounded-lg border border-slate-700">
<span className="text-xs text-slate-400">{detourable.length} image{detourable.length > 1 ? 's' : ''} sans détourage</span>
<button onClick={selectAll} className="text-xs text-indigo-400 hover:text-indigo-300">
Tout sélectionner
</button>
{selected.size > 0 && (
<>
<span className="text-xs text-slate-500">·</span>
<span className="text-xs text-white font-medium">{selected.size} sélectionnée{selected.size > 1 ? 's' : ''}</span>
<button
onClick={detourSelected}
className="ml-auto px-3 py-1 text-xs bg-purple-700 hover:bg-purple-600 text-white font-semibold rounded"
>
Détourer la sélection
</button>
<button onClick={clearSelection} className="text-xs text-slate-500 hover:text-slate-300">
</button>
</>
)}
</div>
)}
{items.length > 0 && (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
{items.map((item) => (
<ImageCard
key={item.id}
item={item}
selected={selected.has(item.id)}
onSelect={toggleSelect}
onFeatherChange={setFeather}
onAlphaThresholdChange={setAlphaThreshold}
onRotate={rotate}
onUpload={uploadOne}
onRemove={removeItem}
onOpenLightbox={setLightboxItem}
onAssignProduct={assignProduct}
onReprocess={reprocess}
onDetour={detour}
onMockup={openMockup}
/>
))}
</div>
)}
{items.length === 0 && (
<div className="text-center py-12 text-slate-600">
<p className="text-4xl mb-3">📂</p>
<p className="text-sm">Déposez des images pour commencer</p>
<p className="text-xs mt-1">Le nom du fichier (sans extension) doit correspondre à une référence produit Shopify</p>
</div>
)}
</div>
<Lightbox item={lightboxItem} onClose={() => setLightboxItem(null)} />
{mockupItem && (
<MockupModalFromImage item={mockupItem} onClose={() => setMockupItem(null)} />
)}
</>
)
}
export default function ImagesPage() {
return (
<ErrorBoundary>
<ImagesPageInner />
</ErrorBoundary>
)
}