diff --git a/src/features/admin/devkit/IconsPage.client.js b/src/features/admin/devkit/IconsPage.client.js
index a79beaf..2d5da3f 100644
--- a/src/features/admin/devkit/IconsPage.client.js
+++ b/src/features/admin/devkit/IconsPage.client.js
@@ -9,13 +9,28 @@ const ALL_ICONS = Object.entries(Icons);
export default function IconsPage() {
const [query, setQuery] = useState('');
+ const [selectedCategory, setSelectedCategory] = useState(null);
const toast = useToast();
+ const categories = useMemo(() => {
+ const map = new Map();
+ for (const [, Icon] of ALL_ICONS) {
+ const cat = Icon.category;
+ if (cat) map.set(cat, (map.get(cat) ?? 0) + 1);
+ }
+ return Array.from(map.entries()).sort(([a], [b]) => a.localeCompare(b));
+ }, []);
+
const filtered = useMemo(() => {
- if (!query.trim()) return ALL_ICONS;
+ let list = ALL_ICONS;
+ if (selectedCategory) list = list.filter(([, Icon]) => Icon.category === selectedCategory);
+ if (!query.trim()) return list;
const q = query.trim().toLowerCase();
- return ALL_ICONS.filter(([name]) => name.toLowerCase().includes(q));
- }, [query]);
+ return list.filter(([name, Icon]) =>
+ name.toLowerCase().includes(q) ||
+ Icon.keywords?.some(k => k.toLowerCase().includes(q))
+ );
+ }, [query, selectedCategory]);
const handleCopy = (name) => {
navigator.clipboard.writeText(`<${name} className="w-5 h-5" />`);
@@ -29,45 +44,90 @@ export default function IconsPage() {
description={`${ALL_ICONS.length} icônes disponibles`}
/>
-
-
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 && (
-
+
+
+
+ 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 && (
+
+ )}
+
+
+ {filtered.length === 0 ? (
+
+ Aucune icône trouvée pour “{query}”
+
+ ) : (
+
+ {filtered.map(([name, IconComponent]) => (
+
+ ))}
+
+ )}
+
+
+ {categories.length > 0 && (
+
+
+ Catégories
+
+
+
+ {categories.map(([cat, count]) => (
+
+ ))}
+
+
)}
-
- {filtered.length === 0 ? (
-
- Aucune icône trouvée pour “{query}”
-
- ) : (
-
- {filtered.map(([name, IconComponent]) => (
-
- ))}
-
- )}
);
}
diff --git a/src/shared/icons/README.md b/src/shared/icons/README.md
new file mode 100644
index 0000000..35d7926
--- /dev/null
+++ b/src/shared/icons/README.md
@@ -0,0 +1,87 @@
+# Icons
+
+Les icônes sont des composants React SVG regroupés par catégorie. Tous les exports passent par `index.js`.
+
+## Structure
+
+```
+src/shared/icons/
+ index.js → re-exporte tous les fichiers catégorie
+ X.js → fichier contenant les icones d'une catégorie
+ README.md
+```
+
+## Ajouter une icône
+
+1. Ajoute le composant dans le fichier catégorie approprié
+2. Ajoute `.keywords` juste après la définition (voir convention ci-dessous)
+3. Si aucune catégorie n'existe, crée un nouveau fichier et ajoute `export * from './nouveau-fichier.js'` dans `index.js`
+
+## Convention des mots-clés
+
+Chaque icône doit avoir une propriété `.keywords` attachée directement sur le composant :
+
+```js
+export const Add01Icon = (props) => (
+
+);
+Add01Icon.keywords = ['add', 'plus', 'create', 'new', 'insert', 'ajouter', 'nouveau', 'créer', 'insérer'];
+```
+
+**Règles :**
+- Toujours en **anglais et en français**
+- Mots au sens large : inclure les synonymes et usages contextuels
+- Pas de doublons inutiles entre variantes du même icône (ex. `Add01Icon` et `Add02Icon` partagent les mêmes keywords)
+
+## Commandes utiles
+
+### Lister toutes les icônes d'un fichier
+
+```bash
+grep -n "^export const" src/shared/icons/add-remove.js
+```
+
+### Ajouter des mots-clés en masse
+
+Plutôt que d'éditer manuellement, un script Python insère les `.keywords` après chaque composant :
+
+```python
+KEYWORDS = {
+ 'MyNewIcon': ['keyword1', 'keyword2', 'mot1', 'mot2'],
+ # ...
+}
+
+path = 'src/shared/icons/mon-fichier.js'
+
+with open(path, 'r') as f:
+ content = f.read()
+
+for name, kw in KEYWORDS.items():
+ if f'{name}.keywords' in content:
+ continue
+ kw_str = ', '.join(f"'{k}'" for k in kw)
+ keywords_line = f'{name}.keywords = [{kw_str}];'
+ start_idx = content.find(f'export const {name} = (props) => (')
+ close_idx = content.find('\n);\n', start_idx)
+ insert_pos = close_idx + len('\n);\n')
+ content = content[:insert_pos] + keywords_line + '\n' + content[insert_pos:]
+
+with open(path, 'w') as f:
+ f.write(content)
+```
+
+## Recherche par mots-clés
+
+```js
+import * as Icons from '@/shared/icons'
+
+function searchIcons(query) {
+ const q = query.toLowerCase()
+ return Object.entries(Icons)
+ .filter(([name, Icon]) =>
+ name.toLowerCase().includes(q) ||
+ Icon.keywords?.some(k => k.includes(q))
+ )
+ .map(([name, Icon]) => ({ name, Icon }))
+}
+```
\ No newline at end of file
diff --git a/src/shared/icons/add-remove.js b/src/shared/icons/add-remove.js
new file mode 100644
index 0000000..46c264e
--- /dev/null
+++ b/src/shared/icons/add-remove.js
@@ -0,0 +1,254 @@
+export const Add01Icon = (props) => (
+
+);
+Add01Icon.keywords = ['add', 'plus', 'create', 'new', 'insert', 'cross', 'ajouter', 'nouveau', 'créer', 'insérer'];
+Add01Icon.category = 'Add + Remove';
+
+export const Add02Icon = (props) => (
+
+);
+Add02Icon.keywords = ['add', 'plus', 'create', 'new', 'insert', 'cross', 'ajouter', 'nouveau', 'créer', 'insérer'];
+Add02Icon.category = 'Add + Remove';
+
+export const AddCircleIcon = (props) => (
+
+);
+AddCircleIcon.keywords = ['add', 'plus', 'create', 'new', 'insert', 'circle', 'round', 'ajouter', 'nouveau', 'créer', 'insérer', 'cercle'];
+AddCircleIcon.category = 'Add + Remove';
+
+export const AddCircleHalfDotIcon = (props) => (
+
+);
+AddCircleHalfDotIcon.keywords = ['add', 'plus', 'create', 'new', 'insert', 'circle', 'half', 'dot', 'ajouter', 'nouveau', 'créer', 'insérer', 'cercle', 'moitié', 'point'];
+AddCircleHalfDotIcon.category = 'Add + Remove';
+
+export const AddSquareIcon = (props) => (
+
+);
+AddSquareIcon.keywords = ['add', 'plus', 'create', 'new', 'insert', 'square', 'ajouter', 'nouveau', 'créer', 'insérer', 'carré'];
+AddSquareIcon.category = 'Add + Remove';
+
+export const Cancel01Icon = (props) => (
+
+);
+Cancel01Icon.keywords = ['cancel', 'close', 'dismiss', 'remove', 'cross', 'x', 'annuler', 'fermer', 'supprimer', 'croix'];
+Cancel01Icon.category = 'Add + Remove';
+
+export const Cancel02Icon = (props) => (
+
+);
+Cancel02Icon.keywords = ['cancel', 'close', 'dismiss', 'remove', 'cross', 'x', 'annuler', 'fermer', 'supprimer', 'croix'];
+Cancel02Icon.category = 'Add + Remove';
+
+export const CancelCircleIcon = (props) => (
+
+);
+CancelCircleIcon.keywords = ['cancel', 'close', 'dismiss', 'remove', 'cross', 'x', 'circle', 'round', 'annuler', 'fermer', 'supprimer', 'croix', 'cercle'];
+CancelCircleIcon.category = 'Add + Remove';
+
+export const CancelCircleHalfDotIcon = (props) => (
+
+);
+CancelCircleHalfDotIcon.keywords = ['cancel', 'close', 'dismiss', 'remove', 'cross', 'x', 'circle', 'half', 'dot', 'annuler', 'fermer', 'supprimer', 'croix', 'cercle', 'moitié', 'point'];
+CancelCircleHalfDotIcon.category = 'Add + Remove';
+
+export const CancelSquareIcon = (props) => (
+
+);
+CancelSquareIcon.keywords = ['cancel', 'close', 'dismiss', 'remove', 'cross', 'x', 'square', 'annuler', 'fermer', 'supprimer', 'croix', 'carré'];
+CancelSquareIcon.category = 'Add + Remove';
+
+export const Delete01Icon = (props) => (
+
+);
+Delete01Icon.keywords = ['delete', 'trash', 'bin', 'remove', 'garbage', 'waste', 'supprimer', 'corbeille', 'poubelle', 'déchets'];
+Delete01Icon.category = 'Add + Remove';
+
+export const Delete02Icon = (props) => (
+
+);
+Delete02Icon.keywords = ['delete', 'trash', 'bin', 'remove', 'garbage', 'waste', 'supprimer', 'corbeille', 'poubelle', 'déchets'];
+Delete02Icon.category = 'Add + Remove';
+
+export const Delete03Icon = (props) => (
+
+);
+Delete03Icon.keywords = ['delete', 'trash', 'bin', 'remove', 'garbage', 'waste', 'supprimer', 'corbeille', 'poubelle', 'déchets'];
+Delete03Icon.category = 'Add + Remove';
+
+export const Delete04Icon = (props) => (
+
+);
+Delete04Icon.keywords = ['delete', 'trash', 'bin', 'remove', 'garbage', 'waste', 'supprimer', 'corbeille', 'poubelle', 'déchets'];
+Delete04Icon.category = 'Add + Remove';
+
+export const DeletePutBackIcon = (props) => (
+
+);
+DeletePutBackIcon.keywords = ['delete', 'trash', 'bin', 'restore', 'put back', 'recover', 'undo', 'supprimer', 'corbeille', 'restaurer', 'récupérer', 'annuler'];
+DeletePutBackIcon.category = 'Add + Remove';
+
+export const DeleteThrowIcon = (props) => (
+
+);
+DeleteThrowIcon.keywords = ['delete', 'trash', 'throw', 'bin', 'discard', 'waste', 'supprimer', 'jeter', 'corbeille', 'poubelle', 'éliminer'];
+DeleteThrowIcon.category = 'Add + Remove';
+
+export const DiamondMinusIcon = (props) => (
+
+);
+DiamondMinusIcon.keywords = ['diamond', 'minus', 'remove', 'subtract', 'shape', 'diamant', 'moins', 'soustraire', 'forme'];
+DiamondMinusIcon.category = 'Add + Remove';
+
+export const DiamondPlusIcon = (props) => (
+
+);
+DiamondPlusIcon.keywords = ['diamond', 'plus', 'add', 'insert', 'shape', 'diamant', 'ajouter', 'insérer', 'forme'];
+DiamondPlusIcon.category = 'Add + Remove';
+
+export const Eraser01Icon = (props) => (
+
+);
+Eraser01Icon.keywords = ['eraser', 'erase', 'clear', 'delete', 'undo', 'remove', 'gomme', 'effacer', 'vider', 'supprimer'];
+Eraser01Icon.category = 'Add + Remove';
+
+export const EraserAddIcon = (props) => (
+
+);
+EraserAddIcon.keywords = ['eraser', 'add', 'erase', 'clear', 'plus', 'create', 'gomme', 'ajouter', 'effacer', 'créer'];
+EraserAddIcon.category = 'Add + Remove';
+
+export const Remove01Icon = (props) => (
+
+);
+Remove01Icon.keywords = ['remove', 'minus', 'subtract', 'reduce', 'dash', 'retirer', 'soustraire', 'réduire', 'moins'];
+Remove01Icon.category = 'Add + Remove';
+
+export const Remove02Icon = (props) => (
+
+);
+Remove02Icon.keywords = ['remove', 'minus', 'subtract', 'reduce', 'dash', 'retirer', 'soustraire', 'réduire', 'moins'];
+Remove02Icon.category = 'Add + Remove';
+
+export const RemoveCircleIcon = (props) => (
+
+);
+RemoveCircleIcon.keywords = ['remove', 'minus', 'subtract', 'circle', 'round', 'retirer', 'soustraire', 'cercle', 'moins'];
+RemoveCircleIcon.category = 'Add + Remove';
+
+export const RemoveCircleHalfDotIcon = (props) => (
+
+);
+RemoveCircleHalfDotIcon.keywords = ['remove', 'minus', 'subtract', 'circle', 'half', 'dot', 'retirer', 'soustraire', 'cercle', 'moitié', 'point'];
+RemoveCircleHalfDotIcon.category = 'Add + Remove';
+
+export const RemoveSquareIcon = (props) => (
+
+);
+RemoveSquareIcon.keywords = ['remove', 'minus', 'subtract', 'square', 'retirer', 'soustraire', 'carré', 'moins'];
+RemoveSquareIcon.category = 'Add + Remove';
+
+export const RestoreBinIcon = (props) => (
+
+);
+RestoreBinIcon.keywords = ['restore', 'bin', 'recover', 'undo', 'trash', 'put back', 'restaurer', 'corbeille', 'récupérer', 'annuler', 'poubelle'];
+RestoreBinIcon.category = 'Add + Remove';
+
+export const UnavailableIcon = (props) => (
+
+);
+UnavailableIcon.keywords = ['unavailable', 'disabled', 'blocked', 'forbidden', 'not available', 'off', 'no', 'indisponible', 'désactivé', 'bloqué', 'interdit', 'non disponible'];
+UnavailableIcon.category = 'Add + Remove';
+
+export const WasteIcon = (props) => (
+
+);
+WasteIcon.keywords = ['waste', 'trash', 'bin', 'delete', 'garbage', 'remove', 'poubelle', 'corbeille', 'supprimer', 'déchets'];
+WasteIcon.category = 'Add + Remove';
+
+export const WasteRestoreIcon = (props) => (
+
+);
+WasteRestoreIcon.keywords = ['waste', 'trash', 'restore', 'bin', 'recover', 'undo', 'poubelle', 'restaurer', 'corbeille', 'récupérer', 'annuler'];
+WasteRestoreIcon.category = 'Add + Remove';
diff --git a/src/shared/icons/index.js b/src/shared/icons/index.js
index be391bc..d1db723 100644
--- a/src/shared/icons/index.js
+++ b/src/shared/icons/index.js
@@ -1,3 +1,5 @@
+export * from './add-remove.js'
+
export const ArrowDown01Icon = (props) => (
);
-
export const UserCircle02Icon = (props) => (