refactor(admin): improve icons page sidebar layout and category display

- store first icon reference alongside count in category map
- add hasSidebar flag to conditionally adjust layout classes
- make sidebar full-width on mobile with horizontal scrolling category list
- reduce icon size and spacing to accommodate sidebar presence
- adjust grid columns breakpoints when sidebar is visible
This commit is contained in:
2026-04-26 09:38:03 -04:00
parent 491219f976
commit 668890fa7f
+31 -22
View File
@@ -16,7 +16,9 @@ export default function IconsPage() {
const map = new Map(); const map = new Map();
for (const [, Icon] of ALL_ICONS) { for (const [, Icon] of ALL_ICONS) {
const cat = Icon.category; const cat = Icon.category;
if (cat) map.set(cat, (map.get(cat) ?? 0) + 1); if (!cat) continue;
if (!map.has(cat)) map.set(cat, { count: 0, FirstIcon: Icon });
map.get(cat).count += 1;
} }
return Array.from(map.entries()).sort(([a], [b]) => a.localeCompare(b)); return Array.from(map.entries()).sort(([a], [b]) => a.localeCompare(b));
}, []); }, []);
@@ -37,6 +39,8 @@ export default function IconsPage() {
toast.success(`${name} copié`); toast.success(`${name} copié`);
}; };
const hasSidebar = categories.length > 0;
return ( return (
<div className="flex flex-col gap-6"> <div className="flex flex-col gap-6">
<AdminHeader <AdminHeader
@@ -44,7 +48,7 @@ export default function IconsPage() {
description={`${ALL_ICONS.length} icônes disponibles`} description={`${ALL_ICONS.length} icônes disponibles`}
/> />
<div className="flex gap-4 items-start"> <div className={`flex gap-4 items-start ${hasSidebar ? 'flex-col sm:flex-row' : ''}`}>
<div className="flex-1 min-w-0 flex flex-col gap-6"> <div className="flex-1 min-w-0 flex flex-col gap-6">
<div className="relative"> <div className="relative">
<input <input
@@ -69,16 +73,20 @@ export default function IconsPage() {
Aucune icône trouvée pour &ldquo;{query}&rdquo; Aucune icône trouvée pour &ldquo;{query}&rdquo;
</p> </p>
) : ( ) : (
<div className="grid grid-cols-4 sm:grid-cols-6 md:grid-cols-8 lg:grid-cols-10 xl:grid-cols-12 2xl:grid-cols-[repeat(16,minmax(0,1fr))] gap-2"> <div className={`grid gap-2 ${
hasSidebar
? 'grid-cols-4 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 xl:grid-cols-10 2xl:grid-cols-[repeat(13,minmax(0,1fr))]'
: 'grid-cols-4 sm:grid-cols-6 md:grid-cols-8 lg:grid-cols-10 xl:grid-cols-12 2xl:grid-cols-[repeat(16,minmax(0,1fr))]'
}`}>
{filtered.map(([name, IconComponent]) => ( {filtered.map(([name, IconComponent]) => (
<button <button
key={name} key={name}
onClick={() => handleCopy(name)} onClick={() => handleCopy(name)}
title={name} title={name}
className="aspect-square flex flex-col items-center justify-center gap-2 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-neutral-100 dark:bg-neutral-900 hover:border-blue-500 dark:hover:border-blue-500 transition-colors duration-100 group cursor-pointer p-2" className="aspect-square flex flex-col items-center justify-center gap-1.5 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-neutral-100 dark:bg-neutral-900 hover:border-blue-500 dark:hover:border-blue-500 transition-colors duration-100 group cursor-pointer p-2"
> >
<IconComponent className="w-7 h-7 text-black dark:text-white" /> <IconComponent className="w-6 h-6 text-black dark:text-white" />
<span className="text-[9px] text-neutral-500 dark:text-neutral-400 leading-tight text-center break-all line-clamp-2 group-hover:text-neutral-600 dark:group-hover:text-neutral-300 w-full px-1"> <span className="text-[9px] text-neutral-500 dark:text-neutral-400 leading-tight text-center break-all line-clamp-2 group-hover:text-neutral-600 dark:group-hover:text-neutral-300 w-full px-0.5">
{name.replace('Icon', '')} {name.replace('Icon', '')}
</span> </span>
</button> </button>
@@ -87,39 +95,40 @@ export default function IconsPage() {
)} )}
</div> </div>
{categories.length > 0 && ( {hasSidebar && (
<div className="w-[185px] shrink-0 sticky top-4 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 overflow-hidden"> <div className="w-full sm:w-[200px] sm:shrink-0 sm:sticky sm:top-4 rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 overflow-hidden order-first sm:order-none">
<div className="px-3 py-2.5 border-b border-neutral-200 dark:border-neutral-800"> <div className="px-3 py-2.5 border-b border-neutral-200 dark:border-neutral-800">
<span className="text-xs font-medium text-neutral-500 dark:text-neutral-400 uppercase tracking-wide">Catégories</span> <span className="text-xs font-medium text-neutral-500 dark:text-neutral-400 uppercase tracking-wide">Catégories</span>
</div> </div>
<div className="py-1"> <div className="py-1 flex sm:flex-col flex-row flex-wrap sm:flex-nowrap gap-0">
<button <button
onClick={() => setSelectedCategory(null)} onClick={() => setSelectedCategory(null)}
className={`w-full flex items-center justify-between px-3 py-1.5 text-sm rounded-md mx-1 transition-colors ${ className={`flex items-center justify-between px-3 py-1.5 text-sm rounded-md m-1 sm:mx-1 sm:my-0 sm:w-[calc(100%-8px)] transition-colors ${
selectedCategory === null selectedCategory === null
? 'bg-blue-50 dark:bg-blue-950 text-blue-700 dark:text-blue-300 font-medium' ? 'bg-neutral-100 dark:bg-neutral-800 text-neutral-900 dark:text-white'
: 'text-neutral-700 dark:text-neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-800' : 'text-neutral-600 dark:text-neutral-400 hover:bg-neutral-100 dark:hover:bg-neutral-800 hover:text-neutral-900 dark:hover:text-white'
}`} }`}
style={{ width: 'calc(100% - 8px)' }}
> >
<span>Tout</span> <span className="truncate">Tout</span>
<span className={`text-xs tabular-nums ${selectedCategory === null ? 'text-blue-500 dark:text-blue-400' : 'text-neutral-400 dark:text-neutral-500'}`}> <span className="text-xs tabular-nums shrink-0 ml-2 text-neutral-400 dark:text-neutral-500">
{ALL_ICONS.length} {ALL_ICONS.length}
</span> </span>
</button> </button>
{categories.map(([cat, count]) => ( {categories.map(([cat, { count, FirstIcon }]) => (
<button <button
key={cat} key={cat}
onClick={() => setSelectedCategory(cat)} onClick={() => setSelectedCategory(cat)}
className={`w-full flex items-center justify-between px-3 py-1.5 text-sm rounded-md mx-1 transition-colors ${ className={`flex items-center justify-between px-3 py-1.5 text-sm rounded-md m-1 sm:mx-1 sm:my-0 sm:w-[calc(100%-8px)] transition-colors ${
selectedCategory === cat selectedCategory === cat
? 'bg-blue-50 dark:bg-blue-950 text-blue-700 dark:text-blue-300 font-medium' ? 'bg-neutral-100 dark:bg-neutral-800 text-neutral-900 dark:text-white'
: 'text-neutral-700 dark:text-neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-800' : 'text-neutral-600 dark:text-neutral-400 hover:bg-neutral-100 dark:hover:bg-neutral-800 hover:text-neutral-900 dark:hover:text-white'
}`} }`}
style={{ width: 'calc(100% - 8px)' }}
> >
<span className="truncate text-left">{cat}</span> <span className="flex items-center gap-2 min-w-0">
<span className={`text-xs tabular-nums shrink-0 ml-1 ${selectedCategory === cat ? 'text-blue-500 dark:text-blue-400' : 'text-neutral-400 dark:text-neutral-500'}`}> <FirstIcon className="w-4 h-4 shrink-0 text-neutral-500 dark:text-neutral-400" />
<span className="truncate">{cat}</span>
</span>
<span className="text-xs tabular-nums shrink-0 ml-2 text-neutral-400 dark:text-neutral-500">
{count} {count}
</span> </span>
</button> </button>