veza/apps/web/src/features/studio/components/cloud-file-browser/FileTableRow.tsx

158 lines
4.7 KiB
TypeScript
Raw Normal View History

import React from 'react';
import { Button } from '@/components/ui/button';
import { Tooltip } from '@/components/ui/tooltip';
import {
CheckSquare,
Square,
Folder,
Music,
Image as ImageIcon,
File,
Share2,
MoreVertical,
Wand2,
} from 'lucide-react';
import { cn } from '@/lib/utils';
import type { CloudFileNode } from './types';
export interface FileTableRowProps {
file: CloudFileNode;
selected: boolean;
onToggleSelect: (id: string) => void;
onFileClick: (file: CloudFileNode) => void;
onAction?: (action: string, file: CloudFileNode) => void;
isLoading?: boolean;
}
function FileTypeIcon({ type }: { type: CloudFileNode['type'] }) {
if (type === 'folder') return <Folder className="w-5 h-5 text-warning" />;
if (type === 'audio') return <Music className="w-5 h-5 text-muted-foreground" />;
if (type === 'image') return <ImageIcon className="w-5 h-5 text-primary" />;
if (['document', 'archive', 'project'].includes(type)) {
return <File className="w-5 h-5 text-muted-foreground" />;
}
return <File className="w-5 h-5 text-muted-foreground" />;
}
export function FileTableRow({
file,
selected,
onToggleSelect,
onFileClick,
onAction,
isLoading = false,
}: FileTableRowProps) {
if (isLoading) {
return (
<tr className="animate-pulse">
<td className="p-4 w-10">
<div className="h-4 w-4 rounded bg-muted" />
</td>
<td className="p-4">
<div className="flex items-center gap-4">
<div className="h-5 w-5 rounded bg-muted shrink-0" />
<div className="h-4 w-32 rounded bg-muted" />
</div>
</td>
<td className="p-4">
<div className="flex gap-1">
<div className="h-4 w-12 rounded bg-muted" />
<div className="h-4 w-10 rounded bg-muted" />
</div>
</td>
<td className="p-4">
<div className="h-4 w-12 rounded bg-muted font-mono text-xs" />
</td>
<td className="p-4">
<div className="h-4 w-20 rounded bg-muted text-xs" />
</td>
<td className="p-4 text-right">
<div className="h-8 w-8 rounded bg-muted ml-auto" />
</td>
</tr>
);
}
return (
<tr
className={cn(
'group border-b border-border/50 hover:bg-muted/50 transition-colors duration-150',
selected && 'bg-primary/5'
)}
>
<td className="p-4">
<button
type="button"
onClick={() => onToggleSelect(file.id)}
className="cursor-pointer text-muted-foreground hover:text-foreground"
aria-label={selected ? 'Désélectionner' : 'Sélectionner'}
>
{selected ? (
<CheckSquare className="w-4 h-4 text-primary" />
) : (
<Square className="w-4 h-4" />
)}
</button>
</td>
<td className="p-4">
<button
type="button"
className="flex items-center gap-4 cursor-pointer text-left w-full"
onClick={() => onFileClick(file)}
>
<FileTypeIcon type={file.type} />
<span className="font-medium text-foreground group-hover:text-foreground transition-colors">
{file.name}
</span>
</button>
</td>
<td className="p-4">
<div className="flex gap-1 flex-wrap">
{file.tags?.map((t) => (
<span
key={t}
className="text-xs bg-muted px-1.5 py-0.5 rounded text-muted-foreground border border-border"
>
{t}
</span>
))}
</div>
</td>
<td className="p-4 text-muted-foreground font-mono text-xs">{file.size}</td>
<td className="p-4 text-muted-foreground text-xs">{file.modified}</td>
<td className="p-4 text-right">
<div className="flex justify-end gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
{file.type === 'audio' && (
<Tooltip content="Process with AI">
<Button
variant="ghost"
size="icon"
className="p-1.5 text-muted-foreground"
onClick={() => onAction?.('ai', file)}
>
<Wand2 className="w-4 h-4" />
</Button>
</Tooltip>
)}
<Button
variant="ghost"
size="icon"
className="p-1.5"
onClick={() => onAction?.('share', file)}
>
<Share2 className="w-4 h-4" />
</Button>
<Button
variant="ghost"
size="icon"
className="p-1.5"
onClick={() => onAction?.('more', file)}
>
<MoreVertical className="w-4 h-4" />
</Button>
</div>
</td>
</tr>
);
}