Cartes de tableau de bord

Les plugins peuvent contribuer des cartes self-contained au tableau de bord admin. Les quatre cartes statistiques intégrées (Posts / Pages / Catégories / Tags) sont au-dessus ; les cartes contribuées

Les plugins peuvent contribuer des cartes self-contained au tableau de bord admin. Les quatre cartes statistiques intégrées (Posts / Pages / Catégories / Tags) sont au-dessus ; les cartes contribuées par les plugins apparaissent dans une grille responsive 1 / 2 / 3 colonnes en dessous.

Le must-use flexweg-metrics utilise ceci pour surfacer l'utilisation du stockage Flexweg et les comptages de documents Firestore / SQLite. Les plugins customs peuvent ajouter les leurs — statut backup, indicateurs de sync, dernières lignes de log, n'importe quoi qui rentre dans une carte.

Enregistrer une carte

TS
import { MyCard } from "./MyCard";

export const manifest: PluginManifest = {
  // …
  register(api) {
    api.registerDashboardCard({
      id: "my-plugin/my-card",
      priority: 50,
      component: MyCard,
    });
  },
};

Manifest de carte

TS
interface DashboardCardManifest {
  id: string;                        // <namespace>/<name>
  priority?: number;                 // défaut 100 — lower runs first
  component: React.ComponentType;    // pas de props
}
  • id — préfixé par votre id de plugin, kebab-case. Identifie la carte.
  • priority — drive l'ordre d'affichage. Plus bas = plus à gauche / plus en haut.
  • component — un composant React qui ne prend AUCUNE prop. Il fetche ses propres données et gère son propre loading / error / empty state.

Le composant

Pas de prop. Fetche tout en interne.

TSX
import { useEffect, useState } from "react";
import { useCmsData } from "@flexweg/cms-runtime";

export function MyCard() {
  const { settings } = useCmsData();
  const [data, setData] = useState<MyData | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchMyData().then((d) => {
      setData(d);
      setLoading(false);
    });
  }, []);

  if (loading) return <div className="card p-4">Loading…</div>;
  if (!data) return <div className="card p-4">No data</div>;

  return (
    <div className="card p-4">
      <h3 className="font-semibold">My Stat</h3>
      <p className="text-2xl">{data.value}</p>
    </div>
  );
}

Conventions visuelles :

  • Utilisez card (class admin standard) pour le container
  • Utilisez p-4 pour le padding
  • Titre en font-semibold
  • Stat principale en text-2xl ou text-3xl

Fetching de données

Depuis useCmsData

Pour des comptages basés sur la live subscription :

TSX
const { posts, pages, terms } = useCmsData();
const draftCount = posts.filter(p => p.status === "draft").length;

C'est gratuit en mode global (la subscription est déjà là). En mode paginated, posts / pages ne sont pas dans le ctx — utilisez useAllPosts ou fetchAllPosts.

Depuis le backend (CRUD)

TSX
import { fetchAllPosts } from "@flexweg/cms-runtime";

useEffect(() => {
  fetchAllPosts({ type: "post" }).then(setData);
}, []);

fetchAllPosts est dispatcher-routed — fonctionne identiquement en Firebase et SQLite.

Depuis Flexweg API

TSX
import { getStorageLimits } from "@flexweg/cms-runtime";

useEffect(() => {
  getStorageLimits().then(setData);
}, []);

Depuis une API externe

fetch() standard. Pensez aux CORS si l'API externe ne vous autorise pas l'origine de votre admin.

Refresh

Pas de polling automatique côté framework. Vous décidez :

TSX
const [refreshKey, setRefreshKey] = useState(0);

useEffect(() => {
  fetchMyData().then(setData);
}, [refreshKey]);

return (
  <div className="card p-4">
    <button onClick={() => setRefreshKey(k => k + 1)}>Refresh</button>
    {/* ... */}
  </div>
);

Ou utilisez un setInterval si vous voulez polling.

Le flexweg-metrics ne poll pas — le tableau de bord se re-mount à chaque navigation away/back, ce qui re-déclenche les useEffect.

i18n

Utilisez useTranslation('<plugin-id>') pour les labels :

TSX
import { useTranslation } from "react-i18next";

export function MyCard() {
  const { t } = useTranslation("my-plugin");
  return (
    <div className="card p-4">
      <h3>{t("dashboardCard.title")}</h3>
    </div>
  );
}

Définissez la clé dans votre bundle i18n :

TS
// i18n.ts
export const en = {
  dashboardCard: { title: "My Card" },
};
export const fr = {
  dashboardCard: { title: "Ma carte" },
};

Limites

  • Pas d'accès à des registries au-delà de ce que le runtime expose
  • Pas d'override du layout du tableau de bord (la grille est fixe)
  • Pas de drag-and-drop pour réordonner les cartes (la priority décide)
  • Pas de bouton « Masquer cette carte » côté utilisateur (à venir éventuellement)