102 lines
4.1 KiB
TypeScript
102 lines
4.1 KiB
TypeScript
import { useState } from 'react';
|
|
import { FolderOpen, Inbox, FolderPlus, Pencil, Trash2 } from 'lucide-react';
|
|
import { useDeleteCollection } from '../hooks/useMemes';
|
|
import { CollectionModal } from './CollectionModal';
|
|
import type { Collection } from '../api/client';
|
|
|
|
interface Props {
|
|
collections: Collection[];
|
|
activeId: number | null;
|
|
onSelect: (id: number) => void;
|
|
isAdmin: boolean;
|
|
}
|
|
|
|
export function CollectionBar({ collections, activeId, onSelect, isAdmin }: Props) {
|
|
const [showCreate, setShowCreate] = useState(false);
|
|
const [renaming, setRenaming] = useState<Collection | null>(null);
|
|
const deleteCollection = useDeleteCollection();
|
|
|
|
async function handleDelete(col: Collection) {
|
|
if (!confirm(`Delete folder "${col.name}"? Its memes will be moved to Unsorted.`)) return;
|
|
// If the deleted folder is active, switch to Unsorted first
|
|
if (activeId === col.id) {
|
|
const unsorted = collections.find((c) => c.is_default);
|
|
if (unsorted) onSelect(unsorted.id);
|
|
}
|
|
await deleteCollection.mutateAsync(col.id);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className="flex gap-2 overflow-x-auto pb-0.5 scrollbar-none items-stretch">
|
|
{collections.map((col) => {
|
|
const isActive = activeId === col.id;
|
|
const isDefault = col.is_default === 1;
|
|
|
|
return (
|
|
<div key={col.id} className="relative group flex-shrink-0">
|
|
<button
|
|
onClick={() => onSelect(col.id)}
|
|
className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-medium transition-all border ${
|
|
isActive
|
|
? 'bg-accent/15 border-accent/50 text-purple-200'
|
|
: 'bg-zinc-900 border-zinc-800 text-zinc-400 hover:border-zinc-600 hover:text-zinc-200 hover:bg-zinc-800/80'
|
|
}`}
|
|
>
|
|
{isDefault ? (
|
|
<Inbox size={15} className={isActive ? 'text-accent' : 'text-zinc-500'} />
|
|
) : (
|
|
<FolderOpen size={15} className={isActive ? 'text-accent' : 'text-zinc-500'} />
|
|
)}
|
|
<span className="whitespace-nowrap">{col.name}</span>
|
|
<span className={`text-xs ${isActive ? 'text-purple-400' : 'text-zinc-600'}`}>
|
|
{col.meme_count}
|
|
</span>
|
|
</button>
|
|
|
|
{/* Admin controls — appear on hover for non-default collections */}
|
|
{isAdmin && !isDefault && (
|
|
<div className="absolute -top-2 -right-1 hidden group-hover:flex gap-0.5 z-10">
|
|
<button
|
|
onClick={(e) => { e.stopPropagation(); setRenaming(col); }}
|
|
className="p-1 rounded bg-zinc-800 border border-zinc-700 text-zinc-400 hover:text-zinc-200 hover:bg-zinc-700 transition-colors"
|
|
title="Rename folder"
|
|
>
|
|
<Pencil size={11} />
|
|
</button>
|
|
<button
|
|
onClick={(e) => { e.stopPropagation(); handleDelete(col); }}
|
|
className="p-1 rounded bg-zinc-800 border border-zinc-700 text-zinc-400 hover:text-red-400 hover:bg-zinc-700 transition-colors"
|
|
title="Delete folder"
|
|
>
|
|
<Trash2 size={11} />
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
|
|
{/* New folder button (admin only) */}
|
|
{isAdmin && (
|
|
<button
|
|
onClick={() => setShowCreate(true)}
|
|
className="flex-shrink-0 flex items-center gap-1.5 px-3 py-2.5 rounded-xl text-sm text-zinc-600 border border-dashed border-zinc-700 hover:border-zinc-500 hover:text-zinc-400 transition-colors"
|
|
title="New folder"
|
|
>
|
|
<FolderPlus size={15} />
|
|
<span className="whitespace-nowrap hidden sm:inline">New folder</span>
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{showCreate && (
|
|
<CollectionModal mode="create" onClose={() => setShowCreate(false)} />
|
|
)}
|
|
{renaming && (
|
|
<CollectionModal mode="rename" collection={renaming} onClose={() => setRenaming(null)} />
|
|
)}
|
|
</>
|
|
);
|
|
}
|