npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@panel-kit/express

v0.1.6

Published

Express middleware for panel-kit — mounts admin API + serves UI

Readme

panel-kit

Admin panel auto-généré pour Node.js + Prisma + PostgreSQL (ou SQLite).
Branchez-le sur votre app Express en 5 minutes — CRUD complet, auth JWT, thème configurable.

Inspiré de Django Admin. Pensé pour les projets qui veulent un back-office sans sacrifier une semaine de dev.


Sommaire


Installation

npm install @panel-kit/express @panel-kit/core @panel-kit/ui

@panel-kit/ui contient la React app pré-buildée — elle est servie automatiquement par @panel-kit/express.


Démarrage rapide

// server.js
import express from 'express'
import { PrismaClient, Prisma } from '@prisma/client'
import { createPanel } from '@panel-kit/express'

const app    = express()
const prisma = new PrismaClient()

app.use(express.json())

app.use('/admin', createPanel({
  prisma,
  dmmf:   Prisma.dmmf,
  secret: process.env.ADMIN_SECRET,

  auth: {
    userModel:     'User',
    loginField:    'email',
    passwordField: 'passwordHash',
    isAdmin:       (user) => user.role === 'SUPER_ADMIN',
  },
}))

app.listen(3000)

Ouvrez http://localhost:3000/admin — panel-kit auto-détecte tous vos modèles Prisma et génère le CRUD.

Important : Prisma.dmmf vient du @prisma/client de votre projet, pas de panel-kit.
Il doit être passé explicitement pour que panel-kit lise votre schéma.


Référence createPanel()

Options racine

| Option | Type | Défaut | Description | |---|---|---|---| | prisma | PrismaClient | — | Requis. Votre instance Prisma. | | dmmf | Prisma.dmmf | — | Requis. Le DMMF de votre projet : import { Prisma } from '@prisma/client'. | | secret | string | — | Requis. Clé de signature JWT. Utilisez une valeur longue et aléatoire en prod. | | provider | 'postgresql' | 'sqlite' | 'postgresql' | Adapte les requêtes (ex: mode: 'insensitive' désactivé sur SQLite). | | models | object | {} | Config par modèle. Si omis, tous les modèles sont auto-enregistrés. | | exclude | string[] | [] | Modèles à exclure de l'auto-enregistrement. | | basePath | string | '/admin' | Chemin de base (doit correspondre au app.use()). | | auth | object | voir ci-dessous | Config d'authentification. | | theme | object | voir ci-dessous | Thème visuel. | | auditLog | boolean \| object | false | Active le journal d'audit. |


Options auth

auth: {
  userModel:     'User',         // Nom du modèle Prisma contenant les admins
  loginField:    'email',        // Champ utilisé comme identifiant
  passwordField: 'passwordHash', // Champ contenant le hash bcrypt
  isAdmin:       (user) => user.role === 'SUPER_ADMIN', // Qui a accès ?
  getPermissions: (user, modelName) => ({  // Optionnel — permissions fines
    view:   true,
    create: user.role !== 'VIEWER',
    edit:   user.role !== 'VIEWER',
    delete: user.role === 'SUPER_ADMIN',
  }),
}

getPermissions est appelé à chaque requête CRUD. Si absent, tout est autorisé pour les admins.


Options theme

theme: {
  appName:  'Mon Admin',   // Nom affiché dans la sidebar
  accent:   '#7C3AED',     // Couleur principale (boutons, liens actifs)
  rail:     '#1A0A2E',     // Fond de la sidebar
  logo:     '/logo.png',   // URL du logo (optionnel)
}

Config par modèle

Si models est omis, panel-kit enregistre tous les modèles avec une config par défaut.
Dès qu'un modèle est déclaré dans models, seuls les modèles déclarés sont exposés.

models: {
  User:     { /* config */ },
  Product:  { /* config */ },
  Category: {},  // zéro config — auto
}

list

Contrôle la page de liste (colonnes, recherche, filtres, tri, pagination).

User: {
  list: {
    fields:        ['id', 'email', 'firstName', 'role', 'createdAt'],
    search:        ['email', 'firstName', 'lastName'],  // champs cherchables
    filters:       ['isActive', 'role'],                // filtres latéraux
    sort:          { field: 'createdAt', dir: 'desc' }, // tri par défaut
    perPage:       25,                                  // lignes par page
    dateHierarchy: 'createdAt',                         // drill-down date
  },
}

dateHierarchy : ajoute un navigateur Année → Mois → Jour au-dessus de la liste.
Fonctionne sur n'importe quel champ DateTime.

form

Contrôle le formulaire de création / édition.

Product: {
  form: {
    readOnly: ['id', 'createdAt', 'updatedAt'],  // champs affichés mais non éditables
    hidden:   ['passwordHash', 'internalNote'],   // champs masqués complètement
    sections: [                                   // regroupement visuel (optionnel)
      { title: 'Informations', fields: ['name', 'description', 'categoryId'] },
      { title: 'Prix & Stock', fields: ['price', 'stock'] },
      { title: 'Statut',       fields: ['isActive'] },
    ],
  },
}

Sans sections, tous les champs sont affichés à plat dans l'ordre déclaré.

Widgets auto-détectés depuis le type Prisma :

| Type Prisma | Widget | |---|---| | String | Champ texte | | Int, Float, Decimal | Champ numérique | | Boolean | Toggle | | DateTime | Date/heure picker | | Json | Éditeur JSON | | Enum | Select avec toutes les valeurs | | Relation (@relation) | Autocomplete avec recherche |

actions

Actions personnalisées déclenchées sur une sélection de la liste.

Product: {
  actions: [
    {
      label:   'Désactiver la sélection',
      context: 'list',   // 'list' = action sur sélection multiple
      onClick: async (ids, { prisma }) => {
        await prisma.product.updateMany({
          where: { id: { in: ids } },
          data:  { isActive: false },
        })
      },
    },
  ],
}

inlines

Sous-tables éditables directement dans le formulaire parent (TabularInline).

Order: {
  inlines: [
    {
      model:     'OrderItem',     // modèle enfant (doit être enregistré)
      fk:        'orderId',       // clé étrangère sur l'enfant
      label:     'Lignes de commande',
      extra:     1,               // lignes vides affichées par défaut
      canAdd:    true,            // autoriser l'ajout de lignes
      canDelete: true,            // autoriser la suppression de lignes
    },
  ],
}

Dashboard

Le dashboard affiche automatiquement les compteurs de chaque modèle.
Marquez un modèle featured: true pour le faire apparaître dans le KPI strip en haut.

models: {
  Order:   { featured: true, list: { ... } },
  Product: { featured: true, list: { ... } },
  User:    { featured: true, list: { ... } },
}

Les cartes KPI affichent : nombre total d'enregistrements + date du dernier ajout.
Un seul appel API au chargement — pas de N requêtes séparées.

Vous pouvez injecter vos propres widgets via le slot dashboard :

<AdminProvider>
  <AdminShell slots={{
    dashboard: <MyKpiCards />,
    headerActions: <ExportButton />,
  }} />
</AdminProvider>

Permissions

Pour un contrôle fin par modèle et par action, déclarez getPermissions dans auth :

auth: {
  // ...
  getPermissions: (user, modelName) => {
    if (user.role === 'SUPER_ADMIN') {
      return { view: true, create: true, edit: true, delete: true }
    }
    if (modelName === 'User') {
      return { view: true, create: false, edit: false, delete: false }
    }
    return { view: true, create: true, edit: true, delete: false }
  },
}
  • Backend : chaque route CRUD vérifie les permissions avant d'exécuter.
  • Frontend : les boutons/colonnes inutilisables sont masqués automatiquement.
  • Par défaut (sans getPermissions) : tout est autorisé pour les admins.

Audit log

Enregistre chaque CREATE, UPDATE et DELETE avec le diff des champs modifiés.

createPanel({
  // ...
  auditLog: true,  // ou { model: 'MyAuditLog' } pour un modèle personnalisé
})

Ajoutez ce modèle à votre schema.prisma :

model PanelAuditLog {
  id        String   @id @default(uuid())
  action    String   // CREATE | UPDATE | DELETE
  model     String
  recordId  String
  userId    String?
  changes   Json?    // { field: { from, to } }
  createdAt DateTime @default(now())
}

Si le modèle est absent du schéma, panel-kit affiche un avertissement et continue sans planter.


Recherche globale

Disponible via Ctrl+K (ou Cmd+K sur Mac).
Cherche en parallèle dans tous les modèles qui ont au moins un champ search déclaré.

User: {
  list: {
    search: ['email', 'firstName', 'lastName'],  // ces champs sont inclus
  },
}

Export CSV / JSON

Disponible dans chaque liste via le menu Exporter.
Exporte les données filtrées et triées de la page courante.

  • CSV : compatible Excel, séparateur virgule, encodage UTF-8 avec BOM.
  • JSON : tableau d'objets bruts.

Architecture

panel-kit/
├── packages/
│   ├── core/          # Registry, CRUD resolver, schema parser, audit log
│   ├── express/       # Middleware Express — monte l'API + sert l'UI
│   └── ui/            # React app pré-buildée (sidebar, listes, formulaires)
└── playground/        # Exemple complet avec SQLite — prêt à lancer

Flux d'une requête :

Browser → GET /admin/*         → serve-ui.js sert index.html (SPA)
Browser → GET /admin/api/meta  → liste des modèles + config UI
Browser → POST /admin/api/auth/login → JWT
Browser → GET /admin/api/user  → listRecords() → Prisma → PostgreSQL

Démarrer le playground :

cd playground
npm install
npx prisma migrate dev
node src/seed.js
node src/server.js
# → http://localhost:3000/admin
# email: [email protected]  /  password: admin123