@clauzier/admin-kit (0.12.0)

Published 2026-09-01 06:43:26 +00:00 by 0x346e3730

Installation

@clauzier:registry=
npm install @clauzier/admin-kit@0.12.0
"@clauzier/admin-kit": "0.12.0"

About this package

@clauzier/admin-kit

Back-office React Admin généré depuis les schémas servis par cms-core. Une coquille cliente ne décrit que son identité : titre, palette, URL d'API. Les ressources, les formulaires, les libellés, l'écran de réglages et le menu viennent du contrat /api/content-schemas — un type de contenu déclaré côté socle apparaît dans l'admin sans qu'une ligne y soit écrite.

Installation

bun add @clauzier/admin-kit

Les paquets react-admin, @mui/material, @mui/icons-material, @api-platform/admin, @tanstack/react-query, react, react-dom et react-router-dom sont des peerDependencies : deux instances de react-admin dans un même bundle donnent deux contextes et plus rien ne fonctionne. La coquille les déclare donc explicitement.

Utilisation

import { createRoot } from 'react-dom/client';
import { AdminKitApp } from '@clauzier/admin-kit/app';
import '@clauzier/admin-kit/styles/admin.css';
import './palette.css';

createRoot(document.getElementById('root')!).render(
  <AdminKitApp
    apiUrl="https://api.exemple.tld/api"
    title="Mon Club"
    subtitle="Espace d'administration"
    tokenKey="monclub_admin_token"
    palette={{
      primary: '#4f7cff',
      secondary: '#e63946',
      light: {
        background: '#f4f4f8',
        paper: '#ffffff',
        text: '#12121a',
        textMuted: '#5c5c70',
        border: '#dcdce6',
      },
      dark: {
        background: '#0b0b10',
        paper: '#14141c',
        text: '#f2f2f5',
        textMuted: '#8a8a99',
        border: '#24242e',
      },
      fontBody: "'Inter', system-ui, sans-serif",
    }}
  />,
);

Props de AdminKitApp

Prop Type Défaut Rôle
apiUrl string point d'entrée de l'API (…/api)
title string titre de l'admin et de l'écran de connexion
subtitle string sous-titre de l'écran de connexion
tokenKey string clé localStorage du jeton Sanctum
palette AdminPalette couleurs de marque et surfaces des deux modes (voir Thème)
defaultTheme 'light' | 'dark' 'dark' mode au premier chargement
messages object {} surcharges de traduction, fusionnées avec celles du kit et de react-admin
children ReactNode <Resource> et <CustomRoutes> supplémentaires, transmis tels quels
dashboard DashboardComponent Dashboard composant de la page d'accueil
menuExtras readonly MenuExtra[] [] entrées de menu ajoutées à une section (Administration par défaut)
mediaPage boolean true page Médias (grille, téléversement, suppression, copie d'URL)

Ce que le kit construit

  • Écran de connexion : lit GET /auth/methods et propose ce que l'API annonce — formulaire email/mot de passe, bouton « Se connecter avec Discord » (CMS_ADMIN_URL posé côté API), ou les deux. Le retour Discord arrive sur /?code&nonce : le nonce est comparé à celui gardé en localStorage (<tokenKey>.nonce), le code échangé via POST /auth/exchange, la query retirée de l'URL. ?error=forbidden (compte non admin) et les autres erreurs s'affichent sur l'écran de connexion.
  • Tableau de bord (/) : carte « Publication du site » (état de la reconstruction, bouton « Reconstruire le site », désactivé et expliqué quand le socle n'est pas configuré) et raccourcis vers chaque écran. La carte se masque face à un socle qui n'expose pas GET /site/rebuild.
  • Menu par sections : Pages / Contenu / Site / Administration. Un singleton et un type category: site vont dans Site, category: content dans Contenu ; sans catégorie, menu rejoint Site et le reste Contenu.
  • Réglages (/<type> pour chaque singleton, « Réglages du site » pour site) : un onglet par groupe du contrat, « Général » pour les champs sans groupe, création de la fiche à la première sauvegarde.
  • Textes d'interface (ui: labels) : une ligne par clé avec sa description, le défaut en placeholder et les variables disponibles ; seules les surcharges non vides sont enregistrées.
  • Saisie libre à suggestions (ui: suggest) : un champ text dont les options se proposent en liste déroulante sans fermer la saisie — toute autre valeur reste acceptée, et la valeur enregistrée revient à l'identique à la ré-ouverture.
  • Catalogue de rôles (ui: roles) : liste { slug, label, discord_role_id, admin, public } éditée en lignes ; l'identifiant se dérive du libellé tant qu'il n'a pas été saisi à la main, un doublon bloque l'enregistrement. « Administration » ouvre le back-office aux porteurs du rôle Discord et le referme à ceux qui le perdent ; « Annuaire public » en fait une section de la page des membres, dans l'ordre de la liste.
  • Lots d'un giveaway (ui: prizes) : liste { prize, winner } éditée en lignes ; un gagnant vide vaut « tirage pas encore fait ».
  • Accès d'une page : section « Accès » de l'éditeur de pages, visibilité public ou members, et pour cette dernière les rôles autorisés choisis dans le catalogue de la fiche Site (vide : tout membre connecté).
  • Écran Utilisateurs : mode de connexion (Discord ou mot de passe), présence sur le serveur Discord, rôles attribués à la main (envoyés en PUT /users/{id} sous roles) et rôles synchronisés depuis Discord en lecture seule.
  • Le toast « Contenu publié — le site sera reconstruit » ne promet une reconstruction que si GET /site/rebuild répond configured: true.

Points d'extension

import { AdminKitApp, registerInput } from '@clauzier/admin-kit';
import { CustomRoutes, Resource } from 'react-admin';
import { Route } from 'react-router-dom';
import BarChartIcon from '@mui/icons-material/BarChart';

registerInput('color', (field, source) => <ColorInput source={source} label={field.label} />);

<AdminKitApp
  {...identite}
  dashboard={MonAccueil}
  menuExtras={[{ label: 'Statistiques', to: '/stats', icon: <BarChartIcon /> }]}
>
  <Resource name="commandes" list={CommandesList} />
  <CustomRoutes>
    <Route path="/stats" element={<Stats />} />
  </CustomRoutes>
</AdminKitApp>;
  • registerInput(type, factory) : un éditeur dédié pour un type ou un ui de champ, sans patcher le socle.
  • useAdminKit() : apiUrl, token(), siteClient, menuExtras, mediaPage depuis n'importe quel écran rendu sous <AdminKitApp>.
  • useMemberRoles() (@clauzier/admin-kit/members) : le catalogue de rôles de la fiche Site, mis en cache par react-query.
  • menuSections(schemas, options) est pur : une coquille qui remplace le menu ou l'accueil part des mêmes sections.

Thème

Le socle ne fournit aucune palette par défaut : une valeur de repli ferait hériter l'identité d'un client à tous les autres. createAdminTheme construit le thème MUI et émet les mêmes valeurs en custom properties --admin-*, pour que la feuille de style et les composants partent d'une source unique.

tests/socle.test.ts échoue si une couleur littérale, un var(--x, repli) ou le nom d'un client apparaît dans src/.

Traductions

L'interface est en français (ra-language-french + les chaînes du kit). Une coquille surcharge n'importe quelle chaîne, y compris celles de react-admin, en passant un arbre partiel :

<AdminKitApp messages={{ ra: { action: { create: 'Ajouter' } } }}  />

Contrat de schémas

Toutes les clés au-delà du socle publié sont optionnelles et lues défensivement : un type de champ inconnu se replie sur une saisie texte plutôt que de casser l'écran. Le kit reste donc utilisable face à une API plus ancienne que lui.

Clé Effet
label_plural libellé du menu — un pluriel français ne se dérive pas en ajoutant un s
singleton une seule fiche, slug forcé au type : écran de réglages au lieu d'une liste
category content ou site : section du menu ; inconnu → null, repli sur le type
groups [{ key, label }] ordonnés : un onglet par groupe dans l'écran de réglages
help texte d'aide sous le champ
ui force un éditeur dédié (menu, labels, suggest, roles, prizes…)
group groupe du champ ; absent ou inconnu → « Général »
in_list affiche le champ en colonne de liste
reference_type type cible d'un champ reference, alimente la liste déroulante
options pour ui: labels, les clés de libellé dans l'ordre d'affichage
option_labels pour ui: labels, description d'une clé ; sans point, titre d'un espace de noms
defaults pour ui: labels, défaut de chaque clé, montré en placeholder
capabilities.search affiche la boîte de recherche ; sans le paramètre q côté API elle ne ferait rien
capabilities.statuses statuts acceptés par le socle

Endpoints consommés

Route Usage
GET /content-schemas contrat des types de contenu
GET /block-schemas contrat des blocs de page (absent → éditeur sans blocs)
GET /auth/methods moyens de connexion à afficher ({ password, discord })
POST /login, POST /logout session Sanctum
GET /auth/discord/redirect?target=admin&nonce=… connexion Discord du back-office (retour sur /?code&nonce)
POST /auth/exchange échange du code de retour Discord contre le jeton
/content_entities, /pages, /blocks, /media ressources API Platform (Hydra)
POST /media, DELETE /media/{id} téléversement avec progression, suppression
GET /site catalogue de rôles (data.member_roles)
GET /site/rebuild état de la reconstruction (404 → carte masquée)
POST /site/rebuild demande de reconstruction (409 si non configurée)
GET/POST /users, PUT/DELETE /users/{id} écran Utilisateurs

Développement

bun install
bun run lint && bun run format:check && bun run typecheck && bun run test

Dependencies

Dependencies

ID Version
ra-i18n-polyglot ^5.15.0
ra-input-rich-text ^5.15.1
ra-language-french ^5.15.0

Development dependencies

ID Version
@eslint/js ^10.0.0
@testing-library/react ^16.3.0
@types/node ^26.0.0
@types/react ^19.0.0
@types/react-dom ^19.0.0
eslint ^10.0.0
eslint-plugin-react-hooks ^7.0.0
jsdom ^28.0.0
prettier ^3.9.0
typescript ~5.9.0
typescript-eslint ^8.40.0
vitest ^4.0.0

Peer dependencies

ID Version
@api-platform/admin ^4.0.0
@mui/icons-material ^9.0.0
@mui/material ^9.0.0
@tanstack/react-query ^5.83.0
react ^19.0.0
react-admin ^5.15.0
react-dom ^19.0.0
react-router-dom ^7.0.0
Details
npm
2026-09-01 06:43:26 +00:00
17
UNLICENSED
46 KiB
Assets (1)
Versions (24) View all
0.24.1 2026-09-10
0.24.0 2026-09-10
0.23.0 2026-09-09
0.22.0 2026-09-07
0.21.0 2026-09-04