feat(admin): add permission-based widget visibility on dashboard

- add optional `permission` field to `registerWidget` api
- filter widgets in `DashboardPage` based on user permissions
- register users widget with `users.view` permission requirement
- document `permission` parameter in admin README
This commit is contained in:
2026-04-25 09:31:54 -04:00
parent 01a08b0005
commit 74bc3073a7
4 changed files with 8 additions and 6 deletions
+2 -1
View File
@@ -160,13 +160,14 @@ Le composant reçoit `data` (retour du fetcher) et `loading` (booléen). Si le f
| `id` | `string` | Identifiant unique du widget | | `id` | `string` | Identifiant unique du widget |
| `fetcher` | `async () => object` | Fonction serveur qui retourne les données | | `fetcher` | `async () => object` | Fonction serveur qui retourne les données |
**`registerWidget({ id, Component, order? })`** **`registerWidget({ id, Component, order?, permission? })`**
| Paramètre | Type | Description | | Paramètre | Type | Description |
|-----------|------|-------------| |-----------|------|-------------|
| `id` | `string` | Identifiant unique (doit correspondre au fetcher) | | `id` | `string` | Identifiant unique (doit correspondre au fetcher) |
| `Component` | `ReactComponent` | Composant client affiché dans le tableau de bord | | `Component` | `ReactComponent` | Composant client affiché dans le tableau de bord |
| `order` | `number` | Position dans la grille (défaut : `0`) | | `order` | `number` | Position dans la grille (défaut : `0`) |
| `permission` | `string` | Clé de permission requise pour voir ce widget (ex. `'users.view'`). Le widget est masqué si l'utilisateur ne possède pas cette permission. |
--- ---
@@ -3,9 +3,10 @@
import { getWidgets, registerPage } from '../registry.js'; import { getWidgets, registerPage } from '../registry.js';
import AdminHeader from '../components/AdminHeader.js'; import AdminHeader from '../components/AdminHeader.js';
export default function DashboardPage({ stats }) { export default function DashboardPage({ user, stats }) {
const loading = stats === null || stats === undefined; const loading = stats === null || stats === undefined;
const widgets = getWidgets(); const permissions = user?.permissions ?? [];
const widgets = getWidgets().filter(w => !w.permission || permissions.includes(w.permission));
return ( return (
<div className="flex flex-col gap-4 sm:gap-6 lg:gap-8"> <div className="flex flex-col gap-4 sm:gap-6 lg:gap-8">
+2 -2
View File
@@ -25,8 +25,8 @@ export function registerWidgetFetcher(id, fetcher) {
widgetFetchers.set(id, fetcher); widgetFetchers.set(id, fetcher);
} }
export function registerWidget({ id, Component, order = 0 }) { export function registerWidget({ id, Component, order = 0, permission }) {
widgetComponents.set(id, { Component, order }); widgetComponents.set(id, { Component, order, permission });
} }
export function getWidgets() { export function getWidgets() {
+1 -1
View File
@@ -20,4 +20,4 @@ function UsersWidget({ data, loading }) {
); );
} }
registerWidget({ id: 'users', Component: UsersWidget, order: 10 }); registerWidget({ id: 'users', Component: UsersWidget, order: 10, permission: 'users.view' });