Interfaccia di amministrazione

In questa pagina

I plugin possono estendere il pannello di amministrazione con pagine personalizzate e widget della dashboard. Sono componenti React resi accanto alle funzionalità admin principali.

Punto di ingresso admin

I plugin con UI admin esportano componenti da un punto di ingresso 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,
};

Configura il punto di ingresso in package.json:

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

Riferiscilo nella definizione del 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" }],
	},
});

Pagine di amministrazione

Le pagine admin sono componenti React che ricevono il contesto del plugin tramite hook.

Definizione della pagina

Definisci le pagine in 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",
		},
	];
}

Le pagine sono montate su /_emdash/admin/plugins/<plugin-id>/<path>.

Componente pagina

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 del plugin

Usa usePluginAPI() per chiamare le route del 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");
}

L’hook aggiunge automaticamente il prefisso dell’ID plugin agli URL delle route.

Widget della dashboard

I widget compaiono nella dashboard admin e forniscono informazioni a colpo d’occhio.

Definizione del widget

Definisci i widget in admin.widgets:

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

Componente 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>
  );
}

Dimensioni dei widget

DimensioneDescrizione
fullLarghezza piena della dashboard
halfMetà larghezza
thirdUn terzo della larghezza

I widget si adattano automaticamente alla larghezza dello schermo.

Struttura di export

Il punto di ingresso admin esporta due oggetti:

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,
};

Componenti di amministrazione

EmDash fornisce componenti predefiniti per pattern comuni:

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>
  );
}

UI impostazioni generata automaticamente

Se il plugin richiede solo un modulo impostazioni, usa admin.settingsSchema senza componenti personalizzati:

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

EmDash genera automaticamente la pagina impostazioni. Aggiungi pagine personalizzate solo per funzionalità oltre le impostazioni di base.

Le pagine del plugin compaiono nella barra laterale admin sotto il nome del plugin. L’ordine segue l’array 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
	];
}

Configurazione di build

I componenti admin richiedono un punto di ingresso di build separato. Configura il 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"]
};

Mantieni React e EmDash admin come dipendenze esterne per evitare bundle duplicati.

Abilitazione / disabilitazione plugin

Quando un plugin è disabilitato nell’admin:

  • I link della barra laterale sono nascosti
  • I widget della dashboard non vengono renderizzati
  • Le pagine admin restituiscono 404
  • Gli hook backend continuano a eseguirsi (per sicurezza dei dati)

I plugin possono verificare se sono abilitati:

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

Esempio: admin UI completa

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,
};