Interface d’administration

Sur cette page

Les plugins peuvent étendre le panneau d’administration avec des pages personnalisées et des widgets de tableau de bord. Ce sont des composants React rendus aux côtés des fonctionnalités d’administration principales.

Point d’entrée admin

Les plugins avec une UI d’administration exportent des composants depuis un point d’entrée admin :

import { SEOSettingsPage } from "./components/SEOSettingsPage";
import { SEODashboardWidget } from "./components/SEODashboardWidget";

// Dashboard widgets
export const widgets = {
	"seo-overview": SEODashboardWidget,
};

// Admin pages
export const pages = {
	"/settings": SEOSettingsPage,
};

Configurez le point d’entrée dans package.json :

{
	"exports": {
		".": "./dist/index.js",
		"./admin": "./dist/admin.js"
	}
}

Référencez-le dans la définition du plugin :

definePlugin({
	id: "seo",
	version: "1.0.0",

	admin: {
		entry: "@my-org/plugin-seo/admin",
		pages: [{ path: "/settings", label: "SEO Settings", icon: "settings" }],
		widgets: [{ id: "seo-overview", title: "SEO Overview", size: "half" }],
	},
});

Pages d’administration

Les pages d’administration sont des composants React qui reçoivent le contexte du plugin via des hooks.

Définition de page

Définissez les pages dans admin.pages :

admin: {
	pages: [
		{
			path: "/settings", // URL path (relative to plugin base)
			label: "Settings", // Sidebar label
			icon: "settings", // Icon name (optional)
		},
		{
			path: "/reports",
			label: "Reports",
			icon: "chart",
		},
	];
}

Les pages sont montées sur /_emdash/admin/plugins/<plugin-id>/<path>.

Composant de page

import { useState, useEffect } from "react";
import { usePluginAPI } from "@emdash-cms/admin";

export function SettingsPage() {
  const api = usePluginAPI();
  const [settings, setSettings] = useState<Record<string, unknown>>({});
  const [saving, setSaving] = useState(false);

  useEffect(() => {
    api.get("settings").then(setSettings);
  }, []);

  const handleSave = async () => {
    setSaving(true);
    await api.post("settings/save", settings);
    setSaving(false);
  };

  return (
    <div>
      <h1>Plugin Settings</h1>

      <label>
        Site Title
        <input
          type="text"
          value={settings.siteTitle || ""}
          onChange={(e) => setSettings({ ...settings, siteTitle: e.target.value })}
        />
      </label>

      <label>
        <input
          type="checkbox"
          checked={settings.enabled ?? true}
          onChange={(e) => setSettings({ ...settings, enabled: e.target.checked })}
        />
        Enabled
      </label>

      <button onClick={handleSave} disabled={saving}>
        {saving ? "Saving..." : "Save Settings"}
      </button>
    </div>
  );
}

Hook API du plugin

Utilisez usePluginAPI() pour appeler les routes de votre plugin :

import { usePluginAPI } from "@emdash-cms/admin";

function MyComponent() {
	const api = usePluginAPI();

	// GET request to plugin route
	const data = await api.get("status");

	// POST request with body
	await api.post("settings/save", { enabled: true });

	// With URL parameters
	const result = await api.get("history?limit=50");
}

Le hook ajoute automatiquement le préfixe d’ID de plugin aux URL des routes.

Widgets du tableau de bord

Les widgets apparaissent sur le tableau de bord d’administration et fournissent un aperçu rapide.

Définition du widget

Définissez les widgets dans admin.widgets :

admin: {
	widgets: [
		{
			id: "seo-overview", // Unique widget ID
			title: "SEO Overview", // Widget title (optional)
			size: "half", // "full" | "half" | "third"
		},
	];
}

Composant widget

import { useState, useEffect } from "react";
import { usePluginAPI } from "@emdash-cms/admin";

export function SEOWidget() {
  const api = usePluginAPI();
  const [data, setData] = useState({ score: 0, issues: [] });

  useEffect(() => {
    api.get("analyze").then(setData);
  }, []);

  return (
    <div className="widget-content">
      <div className="score">{data.score}%</div>
      <ul>
        {data.issues.map((issue, i) => (
          <li key={i}>{issue.message}</li>
        ))}
      </ul>
    </div>
  );
}

Tailles des widgets

TailleDescription
fullLargeur complète du tableau de bord
halfMoitié de la largeur
thirdUn tiers de la largeur

Les widgets se réorganisent automatiquement selon la largeur de l’écran.

Structure d’export

Le point d’entrée admin exporte deux objets :

import { SettingsPage } from "./components/SettingsPage";
import { ReportsPage } from "./components/ReportsPage";
import { StatusWidget } from "./components/StatusWidget";
import { OverviewWidget } from "./components/OverviewWidget";

// Pages keyed by path
export const pages = {
	"/settings": SettingsPage,
	"/reports": ReportsPage,
};

// Widgets keyed by ID
export const widgets = {
	status: StatusWidget,
	overview: OverviewWidget,
};

Composants d’administration

EmDash fournit des composants prêts à l’emploi pour les modèles courants :

import {
  Card,
  Button,
  Input,
  Select,
  Toggle,
  Table,
  Pagination,
  Alert,
  Loading
} from "@emdash-cms/admin";

function SettingsPage() {
  return (
    <Card title="Settings">
      <Input label="API Key" type="password" />
      <Toggle label="Enabled" defaultChecked />
      <Button variant="primary">Save</Button>
    </Card>
  );
}

Interface de paramètres générée automatiquement

Si votre plugin ne nécessite qu’un formulaire de paramètres, utilisez admin.settingsSchema sans composants personnalisés :

admin: {
  settingsSchema: {
    apiKey: { type: "secret", label: "API Key" },
    enabled: { type: "boolean", label: "Enabled", default: true }
  }
}

EmDash génère automatiquement une page de paramètres. N’ajoutez des pages personnalisées que pour des fonctionnalités au-delà des réglages de base.

Les pages du plugin apparaissent dans la barre latérale sous le nom du plugin. L’ordre suit le tableau admin.pages.

admin: {
	pages: [
		{ path: "/settings", label: "Settings", icon: "settings" }, // First
		{ path: "/history", label: "History", icon: "history" }, // Second
		{ path: "/reports", label: "Reports", icon: "chart" }, // Third
	];
}

Configuration de build

Les composants d’administration nécessitent un point d’entrée de build distinct. Configurez votre bundler :

tsdown

export default {
  entry: {
    index: "src/index.ts",
    admin: "src/admin.tsx"
  },
  format: "esm",
  dts: true,
  external: ["react", "react-dom", "emdash", "@emdash-cms/admin"]
};

tsup

export default {
  entry: ["src/index.ts", "src/admin.tsx"],
  format: "esm",
  dts: true,
  external: ["react", "react-dom", "emdash", "@emdash-cms/admin"]
};

Gardez React et EmDash admin comme dépendances externes pour éviter les doublons dans le bundle.

Activer / désactiver un plugin

Lorsqu’un plugin est désactivé dans l’admin :

  • Les liens de la barre latérale sont masqués
  • Les widgets du tableau de bord ne sont pas rendus
  • Les pages d’administration renvoient 404
  • Les hooks backend s’exécutent toujours (pour la sécurité des données)

Les plugins peuvent vérifier leur état d’activation :

const enabled = await ctx.kv.get<boolean>("_emdash:enabled");

Exemple : interface d’administration complète

import { definePlugin } from "emdash";

export default definePlugin({
	id: "analytics",
	version: "1.0.0",

	capabilities: ["network:fetch"],
	allowedHosts: ["api.analytics.example.com"],

	storage: {
		events: { indexes: ["type", "createdAt"] },
	},

	admin: {
		entry: "@my-org/plugin-analytics/admin",
		settingsSchema: {
			trackingId: { type: "string", label: "Tracking ID" },
			enabled: { type: "boolean", label: "Enabled", default: true },
		},
		pages: [
			{ path: "/dashboard", label: "Dashboard", icon: "chart" },
			{ path: "/settings", label: "Settings", icon: "settings" },
		],
		widgets: [{ id: "events-today", title: "Events Today", size: "third" }],
	},

	routes: {
		stats: {
			handler: async (ctx) => {
				const today = new Date().toISOString().split("T")[0];
				const count = await ctx.storage.events!.count({
					createdAt: { gte: today },
				});
				return { today: count };
			},
		},
	},
});
import { EventsWidget } from "./components/EventsWidget";
import { DashboardPage } from "./components/DashboardPage";
import { SettingsPage } from "./components/SettingsPage";

export const widgets = {
	"events-today": EventsWidget,
};

export const pages = {
	"/dashboard": DashboardPage,
	"/settings": SettingsPage,
};