Communitygithub.com

0necontroller/skills

Concevoir des systèmes d'interface, l'architecture de mise en page, des cartes de statistiques avec dégradés subtils, des barres latérales pliables, des fils d'Ariane, des filtres de date et des tables de données TanStack pour des tableaux de bord modernes.

Qu'est-ce que skills ?

Cette compétence est conçue pour les agents d'IA construisant des tableaux de bord modernes et réactifs avec Next.js (App Router), Tailwind CSS (v3 ou v4) et shadcn/ui. Elle fournit des directives complètes, des architectures de composants et des modèles prêts à copier-coller pour les écrans d'administration et d'analyse haut de gamme. La compétence automatise la vérification des prérequis, comme la configuration de shadcn/ui, l'installation des composants nécessaires (barre latérale, fil d'Ariane, table, etc.) et l'installation des packages npm (@tanstack/react-table, date-fns, lucide-react). Elle couvre la création d'un thème cohérent avec des variables CSS personnalisées, une mise en page d'administration avec un SidebarProvider et une barre latérale pliable, un en-tête de page avec fil d'Ariane et badges, des cartes de statistiques avec dégradés subtils, des filtres de date avec Popover et Calendar, et des tables de données TanStack avec pagination, tri et filtrage intégrés. L'objectif est de permettre à l'agent de générer des interfaces de tableau de bord professionnelles avec une navigation intuitive, une typographie adaptative et des indicateurs de performance visuels sans effort manuel.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/0necontroller/skills/tree/HEAD/skills/dashboard-ui

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Compétence : Interface de Tableau de Bord

Directives complètes, architectures de composants et modèles prêts à copier-coller pour construire des tableaux de bord d'administration et d'analyse haut de gamme et réactifs avec Next.js (App Router), Tailwind CSS (v3 ou v4) et shadcn/ui.


1. Prérequis & Installation des Composants shadcn/ui

Avant de générer ou de modifier des composants de tableau de bord, l'agent d'IA DOIT effectuer les vérifications préalables suivantes :

Étape 1 : Vérifier la configuration shadcn/ui (components.json)

Vérifiez si components.json existe à la racine du projet :

  • Si components.json est manquant : N'essayez PAS d'écrire ou d'installer des composants aveuglément. Invitez l'utilisateur :

    "La configuration shadcn/ui (components.json) est introuvable dans ce projet. Veuillez d'abord initialiser shadcn/ui (ex. npx shadcn@latest init) afin que les répertoires de composants et les alias de chemin soient correctement configurés."

  • Si components.json existe : Passez à la vérification des composants.

Étape 2 : Installer les Composants shadcn/ui Requis

Vérifiez si les primitives requises existent dans le répertoire désigné des composants d'interface utilisateur (ex. @/components/ui/). Exécutez npx shadcn@latest add <composant> pour tout composant manquant :

# Mise en page de base et barre latérale
npx shadcn@latest add sidebar collapsible separator sheet tooltip avatar dropdown-menu

# En-tête de page et fil d'Ariane
npx shadcn@latest add breadcrumb badge button

# Cartes de statistiques et d'analyse
npx shadcn@latest add card skeleton

# Filtres de date
npx shadcn@latest add popover calendar

# Tables de données et contrôles de formulaire
npx shadcn@latest add table input select

Ou installez tous les essentiels du tableau de bord en une seule commande :

npx shadcn@latest add sidebar collapsible separator sheet tooltip avatar dropdown-menu breadcrumb badge button card skeleton popover calendar table input select

Étape 3 : Vérifier et Installer les Packages npm de Base

Toutes les tables de données de cette compétence sont construites avec TanStack Table (@tanstack/react-table) combiné avec shadcn/ui <Table>. Assurez-vous que les packages suivants sont présents dans package.json :

  • @tanstack/react-table : Gestion d'état des tables, définitions de colonnes, pagination et tri.
  • date-fns : Analyse, formatage et calcul des dates pour les filtres de date.
  • lucide-react : Ensemble d'icônes standard.

S'ils sont manquants, installez-les :

npm install @tanstack/react-table date-fns lucide-react
# ou : pnpm add @tanstack/react-table date-fns lucide-react
# ou : bun add @tanstack/react-table date-fns lucide-react

2. Fondations du Système de Conception et du Thème

Tous les composants du tableau de bord reposent sur des variables CSS sémantiques et des jetons visuels subtils et à contraste élevé.

Jetons de Thème (globals.css)

:root {
  --background: 0 0% 100%;
  --foreground: 0 0% 3.9%;
  --card: 0 0% 100%;
  --card-foreground: 0 0% 3.9%;
  --popover: 0 0% 100%;
  --popover-foreground: 0 0% 3.9%;
  --primary: 330 43% 46%;
  --primary-foreground: 0 0% 98%;
  --muted: 0 0% 96.1%;
  --muted-foreground: 0 0% 45.1%;
  --border: 0 0% 89.8%;
  --radius: 0.5rem;

  /* Jetons de la Barre Latérale */
  --sidebar: hsl(0 0% 98%);
  --sidebar-foreground: hsl(240 5.3% 26.1%);
  --sidebar-primary: hsl(240 5.9% 10%);
  --sidebar-primary-foreground: hsl(0 0% 98%);
  --sidebar-accent: hsl(240 4.8% 95.9%);
  --sidebar-accent-foreground: hsl(240 5.9% 10%);
  --sidebar-border: hsl(220 13% 91%);
  --sidebar-ring: hsl(217.2 91.2% 59.8%);
}

.dark {
  --background: 0 0% 3.9%;
  --foreground: 0 0% 98%;
  --card: 0 0% 3.9%;
  --card-foreground: 0 0% 98%;
  --muted: 0 0% 14.9%;
  --muted-foreground: 0 0% 63.9%;
  --border: 0 0% 14.9%;

  /* Jetons de la Barre Latérale pour le Mode Sombre */
  --sidebar: hsl(240 5.9% 10%);
  --sidebar-foreground: hsl(240 4.8% 95.9%);
  --sidebar-primary: hsl(224.3 76.3% 48%);
  --sidebar-accent: hsl(240 3.7% 15.9%);
  --sidebar-accent-foreground: hsl(240 4.8% 95.9%);
  --sidebar-border: hsl(240 3.7% 15.9%);
}

3. Mise en Page d'Administration et Architecture de la Barre Latérale

2.1 Point d'Entrée de la Mise en Page (app/admin/layout.tsx)

La mise en page racine de l'administration enveloppe les pages dans SidebarProvider, insère la AppSidebar pliable et contrôle l'en-tête avec des déclencheurs mobiles.

import { AppSidebar } from '@/components/admin/sidebar/app-sidebar';
import {
  SidebarInset,
  SidebarProvider,
  SidebarTrigger
} from '@/components/ui/sidebar';

export default function AdminLayout({ children }: { children: React.ReactNode }) {
  return (
    <SidebarProvider>
      <AppSidebar />
      <SidebarInset>
        {/* En-tête mobile : fixe en haut avec flou sur les petits écrans, transparent sur le bureau */}
        <header className="bg-background/95 fixed top-0 right-0 left-0 z-20 flex h-16 shrink-0 items-center gap-2 border-b shadow-xs backdrop-blur transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12 md:static md:border-none md:bg-transparent md:shadow-none">
          <div className="flex w-full items-center justify-between gap-2 px-4">
            <SidebarTrigger className="-ml-1" />
            <div className="font-semibold text-sm tracking-tight md:hidden">
              Portail d'Administration
            </div>
            <div className="w-8 md:hidden" />
          </div>
        </header>

        {/* Zone de Contenu Principal */}
        <main className="flex flex-1 flex-col gap-4 p-4 pt-20 md:pt-0">
          {children}
        </main>
      </SidebarInset>
    </SidebarProvider>
  );
}

2.2 Barre Latérale de l'Application (components/admin/sidebar/app-sidebar.tsx)

Utilise variant="inset" avec collapsible="icon" pour se réduire à un dock avec seulement des icônes.

'use client';

import {
  PieChart,
  ShoppingBag,
  Users,
  Settings2,
  Package
} from 'lucide-react';
import {
  Sidebar,
  SidebarContent,
  SidebarFooter,
  SidebarHeader,
  SidebarRail
} from '@/components/ui/sidebar';
import { NavMain } from './nav-main';
import { NavUser } from './nav-user';
import { TeamSwitcher } from './team-switcher';

const navData = [
  {
    title: 'Tableau de Bord',
    url: '/admin',
    icon: PieChart,
    items: [{ title: 'Aperçu', url: '/admin' }]
  },
  {
    title: 'Commandes',
    url: '/admin/orders',
    icon: ShoppingBag,
    items: [
      { title: 'Toutes les Commandes', url: '/admin/orders' },
      { title: 'Transactions', url: '/admin/orders/transactions' }
    ]
  },
  {
    title: 'Inventaire',
    url: '/admin/inventory',
    icon: Package,
    items: [
      { title: 'Produits', url: '/admin/inventory/products' },
      { title: 'Stock Faible', url: '/admin/inventory/low-stock' }
    ]
  },
  {
    title: 'Utilisateurs',
    url: '/admin/users',
    icon: Users,
    items: [{ title: 'Clients', url: '/admin/users' }]
  },
  {
    title: 'Paramètres',
    url: '/admin/settings',
    icon: Settings2,
    items: [{ title: 'Général', url: '/admin/settings' }]
  }
];

export function AppSidebar(props: React.ComponentProps<typeof Sidebar>) {
  return (
    <Sidebar variant="inset" collapsible="icon" {...props}>
      <SidebarHeader>
        <TeamSwitcher />
      </SidebarHeader>
      <SidebarContent>
        <NavMain items={navData} />
      </SidebarContent>
      <SidebarFooter>
        <NavUser user={{ name: 'Admin', email: 'admin@company.com' }} />
      </SidebarFooter>
      <SidebarRail />
    </Sidebar>
  );
}

2.3 Navigation Pliable avec État Actif et Pastilles de Compteur (nav-main.tsx)

Met en évidence les liens actifs, fait pivoter les chevrons en douceur et affiche les compteurs de notification avec tabular-nums.

'use client';

import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { ChevronRight, type LucideIcon } from 'lucide-react';
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger
} from '@/components/ui/collapsible';
import {
  SidebarGroup,
  SidebarGroupLabel,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
  SidebarMenuSub,
  SidebarMenuSubButton,
  SidebarMenuSubItem
} from '@/components/ui/sidebar';

interface NavItem {
  title: string;
  url: string;
  icon?: LucideIcon;
  count?: number;
  items?: { title: string; url: string; count?: number }[];
}

export function NavMain({ items }: { items: NavItem[] }) {
  const pathname = usePathname();

  const isPathActive = (target: string) => {
    if (!target) return false;
    if (pathname === target) return true;
    if (target === '/admin') return false;
    return pathname.startsWith(`${target}/`);
  };

  return (
    <SidebarGroup>
      <SidebarGroupLabel>Plateforme</SidebarGroupLabel>
      <SidebarMenu>
        {items.map((item) => {
          const subItemActive = item.items?.some((s) => isPathActive(s.url));
          const isActive = subItemActive || isPathActive(item.url);

          return (
            <Collapsible
              key={item.title}
              asChild
              defaultOpen={isActive}
              className="group/collapsible"
            >
              <SidebarMenuItem>
                <CollapsibleTrigger asChild>
                  <SidebarMenuButton tooltip={item.title} isActive={isActive}>
                    {item.icon && <item.icon />}
                    <span>{item.title}</span>
                    {item.count && item.count > 0 && (
                      <span className="bg-primary/10 text-primary mr-1 ml-auto flex h-5 min-w-5 items-center justify-center rounded-full px-1.5 text-[11px] font-semibold tabular-nums">
                        {item.count}
                      </span>
                    )}
                    <ChevronRight
                      className={`transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90 ${
                        item.count ? '' : 'ml-auto'
                      }`}
                    />
                  </SidebarMenuButton>
                </CollapsibleTrigger>
                <CollapsibleContent>
                  <SidebarMenuSub>
                    {item.items?.map((subItem) => {
                      const subIsActive = isPathActive(subItem.url);
                      return (
                        <SidebarMenuSubItem key={subItem.title}>
                          <SidebarMenuSubButton asChild isActive={subIsActive}>
                            <Link
                              href={subItem.url}
                              className="flex w-full items-center justify-between"
                            >
                              <span>{subItem.title}</span>
                              {subItem.count && subItem.count > 0 && (
                                <span className="bg-primary ml-auto flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-medium text-white tabular-nums">
                                  {subItem.count}
                                </span>
                              )}
                            </Link>
                          </SidebarMenuSubButton>
                        </SidebarMenuSubItem>
                      );
                    })}
                  </SidebarMenuSub>
                </CollapsibleContent>
              </SidebarMenuItem>
            </Collapsible>
          );
        })}
      </SidebarMenu>
    </SidebarGroup>
  );
}

4. En-tête de Page, Fil d'Ariane et Hiérarchie des Titres

Chaque page commence par une ligne d'en-tête propre qui combine un fil d'Ariane hiérarchique, un titre <h1> accessible, un sous-titre et une barre d'outils d'action/filtre.

import Link from 'next/link';
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator
} from '@/components/ui/breadcrumb';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Plus } from 'lucide-react';

interface PageHeaderProps {
  breadcrumbs?: { title: string; href?: string }[];
  title: string;
  subtitle?: string;
  statusBadge?: React.ReactNode;
  children?: React.ReactNode; // Boutons d'action ou filtres
}

export function PageHeader({
  breadcrumbs,
  title,
  subtitle,
  statusBadge,
  children
}: PageHeaderProps) {
  return (
    <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
      <div>
        {breadcrumbs && breadcrumbs.length > 0 && (
          <Breadcrumb className="mb-2">
            <BreadcrumbList>
              {breadcrumbs.map((crumb, idx) => {
                const isLast = idx === breadcrumbs.length - 1;
                return (
                  <span key={crumb.title} className="inline-flex items-center gap-1.5">
                    <BreadcrumbItem>
                      {isLast || !crumb.href ? (
                        <BreadcrumbPage>{crumb.title}</BreadcrumbPage>
                      ) : (
                        <BreadcrumbLink asChild>
                          <Link href={crumb.href}>{crumb.title}</Link>
                        </BreadcrumbLink>
                      )}
                    </BreadcrumbItem>
                    {!isLast && <BreadcrumbSeparator />}
                  </span>
                );
              })}
            </BreadcrumbList>
          </Breadcrumb>
        )}

        <div className="flex items-center gap-3">
          <h1 className="text-2xl font-semibold tracking-tight md:text-3xl md:font-bold">
            {title}
          </h1>
          {statusBadge}
        </div>

        {subtitle && (
          <p className="text-muted-foreground text-sm mt-1">
            {subtitle}
          </p>
        )}
      </div>

      {children && (
        <div className="flex flex-wrap items-center gap-2">
          {children}
        </div>
      )}
    </div>
  );
}

5. Cartes de Statistiques avec Dégradés Subtils

4.1 Le Motif de Dégradé et de Conteneur de Requête

Pour obtenir un aspect haut de gamme sans encombrement visuel :

  1. Appliquez le sélecteur générique parent *:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card *:data-[slot=card]:bg-gradient-to-t en mode clair.
  2. Revenez à dark:*:data-[slot=card]:bg-card pour des cartes propres et mates en mode sombre.
  3. Ajoutez @container/card sur la carte pour adapter dynamiquement la typographie du titre (@[250px]/card:text-3xl).
  4. Affichez les indicateurs de tendance via des abs...

Skills associés