feat(admin): add devkit developer tools section

- add `ZEN_DEVKIT` env variable to enable/disable devkit
- add `isDevkitEnabled()` utility and export it from public api
- register devkit nav section and items conditionally when devkit is enabled
- add devkit route handling in admin page client and server
- add DevkitPage, ComponentsPage, and IconsPage client components
This commit is contained in:
2026-04-24 20:27:30 -04:00
parent 345218641c
commit 970092fccb
9 changed files with 293 additions and 3 deletions
@@ -0,0 +1,73 @@
'use client';
import { useState, useMemo } from 'react';
import * as Icons from '@zen/core/shared/icons';
import { useToast } from '@zen/core/toast';
import AdminHeader from '../components/AdminHeader.js';
const ALL_ICONS = Object.entries(Icons);
export default function IconsPage() {
const [query, setQuery] = useState('');
const toast = useToast();
const filtered = useMemo(() => {
if (!query.trim()) return ALL_ICONS;
const q = query.trim().toLowerCase();
return ALL_ICONS.filter(([name]) => name.toLowerCase().includes(q));
}, [query]);
const handleCopy = (name) => {
navigator.clipboard.writeText(`<${name} className="w-5 h-5" />`);
toast.success(`${name} copié`);
};
return (
<div className="flex flex-col gap-6">
<AdminHeader
title="Icônes"
description={`${ALL_ICONS.length} icônes disponibles`}
/>
<div className="relative">
<input
type="text"
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Rechercher une icône..."
className="w-full rounded-lg border border-neutral-200 dark:border-neutral-800 bg-white dark:bg-neutral-900 px-4 py-2.5 text-sm text-neutral-900 dark:text-white placeholder-neutral-400 dark:placeholder-neutral-500 focus:outline-none focus:ring-2 focus:ring-blue-700/40"
/>
{query && (
<button
onClick={() => setQuery('')}
className="absolute right-3 top-1/2 -translate-y-1/2 text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-200 text-lg leading-none"
>
×
</button>
)}
</div>
{filtered.length === 0 ? (
<p className="text-sm text-neutral-500 dark:text-neutral-400 text-center py-12">
Aucune icône trouvée pour &ldquo;{query}&rdquo;
</p>
) : (
<div className="grid grid-cols-4 sm:grid-cols-6 lg:grid-cols-8 xl:grid-cols-10 gap-2">
{filtered.map(([name, IconComponent]) => (
<button
key={name}
onClick={() => handleCopy(name)}
title={name}
className="flex flex-col items-center gap-1.5 rounded-lg border border-transparent hover:border-neutral-200 dark:hover:border-neutral-700 hover:bg-neutral-50 dark:hover:bg-neutral-800/50 p-3 transition-colors duration-100 group cursor-pointer"
>
<IconComponent className="w-5 h-5 text-neutral-700 dark:text-neutral-300 group-hover:text-blue-700 dark:group-hover:text-blue-400 transition-colors" />
<span className="text-[9px] text-neutral-400 dark:text-neutral-500 leading-tight text-center break-all line-clamp-2 group-hover:text-neutral-600 dark:group-hover:text-neutral-300">
{name.replace('Icon', '')}
</span>
</button>
))}
</div>
)}
</div>
);
}