fin du refactp
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import dynamic from 'next/dynamic'
|
||||
import type { FolderEntry } from '@/lib/client-types'
|
||||
import { formatBytes } from '@/lib/format-bytes'
|
||||
import WarningBanner from './WarningBanner'
|
||||
|
||||
const ModelViewer = dynamic(() => import('../ModelViewer'), { ssr: false })
|
||||
|
||||
interface FolderCardProps {
|
||||
entry: FolderEntry
|
||||
index: number
|
||||
onToggleViewer: (index: number) => void
|
||||
onRemove: (index: number) => void
|
||||
}
|
||||
|
||||
export default function FolderCard({ entry, index, onToggleViewer, onRemove }: FolderCardProps) {
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center gap-3 bg-black-800 border border-white/20 rounded-xl px-4 py-3">
|
||||
{/* Status icon */}
|
||||
<div className="shrink-0">
|
||||
{entry.status === 'success' ? (
|
||||
<div className="w-8 h-8 rounded-full bg-green-900/30 flex items-center justify-center">
|
||||
<svg className="w-4 h-4 text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
) : entry.status === 'error' ? (
|
||||
<div className="w-8 h-8 rounded-full bg-red-900/30 flex items-center justify-center">
|
||||
<svg className="w-4 h-4 text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</div>
|
||||
) : entry.status === 'uploading' ? (
|
||||
<div className="w-8 h-8 rounded-full bg-gray-700 flex items-center justify-center">
|
||||
<svg className="w-4 h-4 text-gray-300 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
|
||||
</svg>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => entry.modelUrl ? onToggleViewer(index) : undefined}
|
||||
aria-label={entry.viewerOpen ? 'Fermer la preview 3D' : 'Ouvrir la preview 3D'}
|
||||
className={`shrink-0 w-8 h-8 rounded-full flex items-center justify-center transition ${
|
||||
entry.modelUrl ? 'bg-black-700 hover:bg-gray-700 cursor-pointer' : 'bg-black-700 cursor-default'
|
||||
}`}
|
||||
>
|
||||
<svg
|
||||
className={`w-4 h-4 text-gray-500 transition-transform ${entry.viewerOpen ? 'rotate-180' : ''}`}
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Info */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-gray-200 font-mono truncate">{entry.folderName}/</span>
|
||||
<span className="shrink-0 text-xs px-1.5 py-0.5 rounded-full bg-gray-700 text-gray-300">Dossier</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-0.5">
|
||||
<span className="text-xs text-gray-500">modele : {entry.modelFile.name}</span>
|
||||
{entry.status === 'error' && entry.error && (
|
||||
<span className="text-xs text-red-400 truncate">{entry.error}</span>
|
||||
)}
|
||||
{entry.status === 'success' && entry.filename && (
|
||||
<span className="text-xs text-green-400 font-mono">{entry.filename}</span>
|
||||
)}
|
||||
</div>
|
||||
{entry.status === 'uploading' && (
|
||||
<div className="mt-1.5 w-full h-1 bg-black-700 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-gray-400 rounded-full transition-all duration-200"
|
||||
style={{ width: `${entry.progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Remove button */}
|
||||
{entry.status !== 'uploading' && (
|
||||
<button
|
||||
onClick={() => onRemove(index)}
|
||||
aria-label="Supprimer le dossier"
|
||||
className="shrink-0 text-gray-600 hover:text-red-400 transition"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Warning banner */}
|
||||
{entry.status !== 'success' && (
|
||||
<WarningBanner warnings={entry.warnings} />
|
||||
)}
|
||||
|
||||
{/* 3D preview */}
|
||||
{entry.modelUrl && entry.status !== 'success' && (
|
||||
<div
|
||||
className={`transition-all duration-300 ease-in-out overflow-hidden ${
|
||||
entry.viewerOpen ? 'max-h-[500px] opacity-100 mt-2' : 'max-h-0 opacity-0 pointer-events-none'
|
||||
}`}
|
||||
>
|
||||
<ModelViewer
|
||||
url={entry.modelUrl}
|
||||
filename={entry.modelFile.name}
|
||||
size={formatBytes(entry.modelFile.size)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user