feat: make AI background removal optional with resize-only upload and batch detour selection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import { useState } from 'react'
|
||||
import { useState, useCallback } from 'react'
|
||||
import { Header } from '@/components/layout/Header'
|
||||
import { DropZone } from '@/components/images/DropZone'
|
||||
import { ImageCard } from '@/components/images/ImageCard'
|
||||
@@ -10,8 +10,36 @@ import { useImages } from '@/hooks/useImages'
|
||||
import type { ImageItem } from '@/types/images'
|
||||
|
||||
function ImagesPageInner() {
|
||||
const { items, addFiles, setFeather, setAlphaThreshold, rotate, uploadOne, uploadAll, removeItem, assignProduct, reprocess } = useImages()
|
||||
const { items, addFiles, setFeather, setAlphaThreshold, rotate, uploadOne, uploadAll, removeItem, assignProduct, reprocess, detour } = useImages()
|
||||
const [lightboxItem, setLightboxItem] = useState<ImageItem | null>(null)
|
||||
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 (
|
||||
<>
|
||||
@@ -31,12 +59,39 @@ function ImagesPageInner() {
|
||||
<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}
|
||||
@@ -45,6 +100,7 @@ function ImagesPageInner() {
|
||||
onOpenLightbox={setLightboxItem}
|
||||
onAssignProduct={assignProduct}
|
||||
onReprocess={reprocess}
|
||||
onDetour={detour}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user