{section.items.map((item) => { const current = item.href ? isActive(item.href, pathname) : false; diff --git a/src/components/shell/SettingsRail.tsx b/src/components/shell/SettingsRail.tsx new file mode 100644 index 0000000..fa7e13f --- /dev/null +++ b/src/components/shell/SettingsRail.tsx @@ -0,0 +1,106 @@ +'use client'; + +import Link from 'next/link'; + +import { + isActive, + placeholderHref, + type NavSection, +} from '@/components/shell/navigation'; +import { cx } from '@/lib/cx'; + +/** + * Rail latéral des réglages — et **seulement** des réglages. + * + * Partout ailleurs, la section tient en une poignée d'entrées et se présente en + * onglets, qui laissent toute la largeur au contenu. Les réglages en comptent + * une vingtaine : en onglets, ils occuperaient trois rangées au-dessus de + * chaque écran, et la liste changerait de forme d'une page à l'autre selon la + * longueur des libellés. + * + * Une liste verticale les tient dans un ordre stable, groupés par domaine. Ce + * n'est pas la même navigation qu'ailleurs parce que ce n'est pas le même + * usage : on traverse les réglages, on ne fait pas d'aller-retour entre eux + * pendant qu'on travaille. + */ + +/** + * Regroupement des entrées, dans l'ordre d'un paramétrage. + * + * L'identité de l'entreprise, puis ce qui organise le travail, puis ce qui le + * paie, puis ce qui l'encadre. Un client qui installe l'instance descend la + * liste ; c'est cet ordre-là, pas l'alphabet. + */ +const GROUPS: ReadonlyArray<{ label: string; ids: readonly string[] }> = [ + { label: 'Société', ids: ['compte', 'sites', 'convention'] }, + { label: 'Organisation', ids: ['emplois', 'etiquettes', 'types-absence'] }, + { label: 'Travail', ids: ['preferences', 'impression', 'productivite'] }, + { label: 'Paie', ids: ['paie-reglages'] }, + { label: 'Accès', ids: ['roles', 'securite', 'email'] }, + { + label: 'Conformité', + ids: ['registre', 'conservation', 'rgpd', 'modeles'], + }, +]; + +export function SettingsRail({ + section, + pathname, +}: { + section: NavSection; + pathname: string; +}) { + const byId = new Map(section.items.map((item) => [item.id, item])); + + // Une entrée ajoutée à la navigation sans être rangée dans un groupe reste + // visible, en fin de liste : la perdre serait pire que de mal la classer. + const grouped = new Set(GROUPS.flatMap((group) => group.ids)); + const rest = section.items.filter((item) => !grouped.has(item.id)); + + const groups = [ + ...GROUPS.map((group) => ({ + label: group.label, + items: group.ids + .map((id) => byId.get(id)) + .filter((item) => item !== undefined), + })), + ...(rest.length > 0 ? [{ label: 'Autres', items: rest }] : []), + ].filter((group) => group.items.length > 0); + + return ( + + ); +}