Skip to main content
add-dashboard Create a new customizable dashboard with its own chart registry, provider, and page. Use when adding dashboards like DRep or SPO dashboard.
الانتقال إلى التثبيت سوق المهارات اكتشف واستكشف مهارات الذكاء الاصطناعي التي بناها المجتمع.
المهن ذات الصلة SOC
استنادا إلى تصنيف SOC المهني
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
نسخ Promptعرض تفاصيل Prompt يتجاوز الأمر المباشر Prompt المخصّص للمراجعة. افحص المصدر قبل تشغيله.
npx skills add https://github.com/nomos-guild/cgov --skill add-dashboardيبقى الأمر في سطر واحد. مرّر أفقيًا لمراجعته كاملًا قبل النسخ.
تفضّل نسخة محلية؟ نزّل الملفات المتاحة حاليًا لدى SkillsMP.
تحميل Zip جاري التحميل... name add-dashboard updated "2026-03-03T00:00:00.000Z" description Create a new customizable dashboard with its own chart registry, provider, and page. Use when adding dashboards like DRep or SPO dashboard. argument-hint ["DashboardName"] allowed-tools Read, Edit, Write, Glob, Grep, Bash
Add New Dashboard
Create a complete new dashboard instance with its own charts, state management, and page.
Arguments
$0 - Dashboard name in PascalCase (e.g., DRep, SPO, Voter)
Architecture Overview
Each dashboard is a self-contained module with these features:
Charts : Lazy-loaded chart components in a registry
Side Panel : Tabbed panel for Charts, Elements, Layout, Share
Text Elements : User-addable text labels
Page Margins : Draggable margin handles for width control
Multi-Select : Box selection and Ctrl+click for multiple elements
localStorage : All settings persist per-dashboard
src/
├── components/dashboards/
│ ├── shared/ # Reused across all dashboards
│ │ ├── DashboardProvider.tsx # State + localStorage (COPY & MODIFY)
│ │ ├── DashboardGrid.tsx # Canvas with selection (COPY & MODIFY)
│ │ ├── DashboardChartCard.tsx # Chart wrapper (REUSE AS-IS)
│ │ ├── DashboardTextElement.tsx # Text labels (REUSE AS-IS)
│ │ ├── DashboardSidePanel.tsx # Customization panel (COPY & MODIFY)
│ │ ├── DashboardMarginHandles.tsx # Margin controls (REUSE AS-IS)
│ │ └── chartTheme.ts # Theme colors (REUSE AS-IS)
│ │
│ ├── governance/ # Reference implementation
│ │ └── charts/
│ │ ├── index.tsx # CHART_REGISTRY
│ │ └── *.tsx # Chart components
│ │
│ ├── development_activity/ # Dev activity dashboard (real example)
│ │ └── charts/
│ │ ├── index.ts # CHART_REGISTRY
│ │ └── *.tsx # 13+ chart components
│ │
│ └── ${lowercase}/ # YOUR NEW DASHBOARD
│ ├── charts/
│ │ ├── index.tsx # CHART_REGISTRY for this dashboard
│ │ └── *.tsx # Chart components
│ ├── ${$0}DashboardProvider.tsx
│ ├── ${$0}DashboardGrid.tsx
│ ├── ${$0}DashboardSidePanel.tsx
│ └── index.ts # Barrel export
│
├── pages/
│ ├── adadev.tsx # Governance dashboard page
│ └── ${lowercase}-dashboard.tsx # YOUR NEW DASHBOARD PAGE
│
└── types/
└── ${lowercase}-dashboard.ts # Dashboard-specific types
Step 1: Create Dashboard Directory Structure mkdir -p src/components/dashboards/${lowercase} /charts
Step 2: Create Chart Registry Create src/components/dashboards/${lowercase}/charts/index.tsx:
import dynamic from "next/dynamic" ;
import { BarChart3 } from "lucide-react" ;
import { ChartSkeleton } from "@/components/dashboards/shared/ChartSkeleton" ;
import type { ChartDefinition } from "@/types/dashboard" ;
import { DEFAULT_ ${UPPERCASE }_CHART_LAYOUTS } from "@/types/${lowercase}-dashboard" ;
const Example ${$0}Chart = dynamic (
() => import ("./Example${$0}Chart" ).then ((mod ) => mod.Example$ {$0}Chart ),
{ loading : () => <ChartSkeleton /> , ssr : false }
);
export const ${UPPERCASE }_CHART_REGISTRY : ChartDefinition [] = [
{
id : "example-chart" ,
title : "Example Chart" ,
description : "Description of what this chart shows" ,
component : Example ${$0}Chart ,
defaultVisible : true ,
defaultLayout : DEFAULT_ ${UPPERCASE }_CHART_LAYOUTS["example-chart" ],
icon : BarChart3 ,
},
];
export function get${$0}ChartById (id : string ): ChartDefinition | undefined {
return ${UPPERCASE }_CHART_REGISTRY.find ((chart ) => chart.id === id);
}
export { ChartSkeleton } from "@/components/dashboards/shared/ChartSkeleton" ;
export { Example ${$0}Chart };
Step 3: Create Dashboard Types Create src/types/${lowercase}-dashboard.ts:
import type { ChartLayout , TextElement , PageMargins } from "./dashboard" ;
import { PAGE_MARGIN_CONSTRAINTS , DEFAULT_PAGE_MARGINS } from "./dashboard" ;
export type ${$0}ChartId = "example-chart" ;
export const ALL_ ${UPPERCASE }_CHART_IDS : ${$0}ChartId [] = ["example-chart" ];
export const DEFAULT_ ${UPPERCASE }_CHART_LAYOUTS : Record <${$0}ChartId , ChartLayout > = {
"example-chart" : { x : 0 , y : 0 , width : 380 , height : 320 },
};
export interface ${$0}DashboardConfig {
visibleCharts : ${$0}ChartId [];
chartOrder : ${$0}ChartId [];
layouts : Record <${$0}ChartId , ChartLayout >;
textElements : TextElement [];
pageMargins : PageMargins ;
version : number ;
}
export const DEFAULT_ ${UPPERCASE }_DASHBOARD_CONFIG : ${$0}DashboardConfig = {
visibleCharts : ALL_ ${UPPERCASE }_CHART_IDS,
chartOrder : ALL_ ${UPPERCASE }_CHART_IDS,
layouts : DEFAULT_ ${UPPERCASE }_CHART_LAYOUTS,
textElements : [],
pageMargins : DEFAULT_PAGE_MARGINS ,
version : 1 ,
};
export { PAGE_MARGIN_CONSTRAINTS };
Step 4: Create Dashboard Provider Create src/components/dashboards/${lowercase}/${$0}DashboardProvider.tsx:
Copy from shared/DashboardProvider.tsx and modify:
Change STORAGE_KEY to "${lowercase}-dashboard-config"
Update imports to use ${$0}ChartId, ${$0}DashboardConfig, etc.
Update default config imports
Rename context and hook
const STORAGE_KEY = "${lowercase}-dashboard-config" ;
import type {
${$0}ChartId ,
${$0}DashboardConfig ,
} from "@/types/${lowercase}-dashboard" ;
import {
DEFAULT_ ${UPPERCASE }_DASHBOARD_CONFIG,
DEFAULT_ ${UPPERCASE }_CHART_LAYOUTS,
ALL_ ${UPPERCASE }_CHART_IDS,
PAGE_MARGIN_CONSTRAINTS ,
} from "@/types/${lowercase}-dashboard" ;
interface ${$0}DashboardContextValue {
config : ${$0}DashboardConfig ;
mounted : boolean ;
isChartVisible : (chartId : ${$0}ChartId ) => boolean ;
toggleChartVisibility : (chartId : ${$0}ChartId ) => void ;
setVisibleCharts : (chartIds : ${$0}ChartId [] ) => void ;
getLayout : (chartId : ${$0}ChartId ) => ChartLayout ;
updateLayout : (chartId : ${$0}ChartId , layout : Partial <ChartLayout > ) => void ;
reorderCharts : (fromIndex : number , toIndex : number ) => void ;
resetToDefaults : () => void ;
addTextElement : () => void ;
updateTextElement : (id : string , updates : Partial <TextElement > ) => void ;
removeTextElement : (id : string ) => void ;
updatePageMargins : (margins : Partial <PageMargins > ) => void ;
exportConfig : () => string ;
importConfig : (code : string ) => { success : boolean ; error ?: string };
}
export function ${$0}DashboardProvider ({ children }) { ... }
export function use${$0}Dashboard () { ... }
Step 5: Create Dashboard Grid Create src/components/dashboards/${lowercase}/${$0}DashboardGrid.tsx:
Copy from shared/DashboardGrid.tsx and modify:
Import from your chart registry: ${UPPERCASE}_CHART_REGISTRY, get${$0}ChartById
Use your dashboard hook: use${$0}Dashboard
Update type references to ${$0}ChartId
Key Features to Preserve Data Attributes (required for selection exclusion):
<DashboardChartCard data-chart-card ... />
<DashboardTextElement data-text-element ... />
Document-Level Selection Handler :
useEffect (() => {
const handleMouseDown = (e : MouseEvent ) => {
const target = e.target as HTMLElement ;
if (target.closest ("[data-chart-card], [data-text-element], [data-margin-handle], button, [role='button'], input, textarea, a" )) {
return ;
}
};
document .addEventListener ("mousedown" , handleMouseDown);
return () => document .removeEventListener ("mousedown" , handleMouseDown);
}, []);
Card Position Constraints (when margins change):
useEffect (() => {
if (!mounted || containerWidth < 400 ) return ;
for (const chartId of config.visibleCharts ) {
const layout = getLayout (chartId);
const maxX = Math .max (0 , containerWidth - layout.width );
if (layout.x > maxX) {
updateLayout (chartId, { x : snapToGrid (maxX) });
}
}
}, [containerWidth, mounted, ...]);
Step 6: Create Side Panel Create src/components/dashboards/${lowercase}/${$0}DashboardSidePanel.tsx:
Copy from shared/DashboardSidePanel.tsx and modify:
Import your chart registry: get${$0}ChartById
Use your dashboard hook: use${$0}Dashboard
Side Panel Tabs Tab Purpose Charts Toggle visibility, reorder via drag Elements Add/manage text labels Layout Page margin sliders Share Export/import config as base64
Step 7: Create Dashboard Page Create src/pages/${lowercase}-dashboard.tsx:
import { useRef } from "react" ;
import Head from "next/head" ;
import { Card } from "@/components/ui/card" ;
import { GameLoader } from "@/components/ui/game-loader" ;
import { useTheme } from "@/lib/theme" ;
import {
${$0}DashboardProvider ,
${$0}DashboardGrid ,
${$0}DashboardSidePanel ,
use${$0}Dashboard ,
} from "@/components/dashboards/${lowercase}" ;
import { DashboardMarginHandles } from "@/components/dashboards/shared" ;
function ${$0}DashboardContent () {
const { activeTheme } = useTheme ();
const isGame = activeTheme.id === "game" ;
const { config, mounted } = use${$0}Dashboard ();
const gridAreaRef = useRef<HTMLDivElement >(null );
const isLoading = false ;
const error = null ;
const hasData = true ;
const minPadding = 24 ;
const leftPadding = Math .max (minPadding, config.pageMargins .left );
const rightPadding = Math .max (minPadding, config.pageMargins .right );
return (
<>
<Head >
<title > ${$0} Dashboard - CGOV</title >
<meta name ="description" content ="${$0} Dashboard" />
</Head >
<div className ="min-h-screen bg-background" >
{/* Margin handles - only show when mounted */}
{mounted && <DashboardMarginHandles containerRef ={gridAreaRef} /> }
{/* Full-width container with dynamic padding */}
<div
className ="py-4 sm:py-6 md:py-8 transition-[padding] duration-75"
style ={{
paddingLeft: leftPadding ,
paddingRight: rightPadding ,
}}
>
{/* Header */}
<div className ="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-4 sm:mb-6 md:mb-8" >
<div className ="text-left" >
<h1 className ="landing-title text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold mb-2 sm:mb-3 md:mb-4 text-black dark:text-foreground" >
${$0} Dashboard
</h1 >
<p className ="landing-subtitle text-muted-foreground text-sm sm:text-base md:text-lg" >
Your customizable ${$0} overview
<${$0}DashboardSidePanel />
{/* Loading state */}
{isLoading && !hasData && (
isGame ? (
) : (
Loading...
)
)}
{/* Error state - inline warning, doesn't block content */}
{error && (
{error}
)}
{/* Dashboard Grid - show even if some data fails */}
{(hasData || (!isLoading && error)) && (
<${$0}DashboardGrid isLoading={isLoading} />
)}
);
}
export default function ${$0}Dashboard () {
return (
<${$0}DashboardProvider >
<${$0}DashboardContent />
</${$0}DashboardProvider >
);
}
Step 8: Create First Chart Create src/components/dashboards/${lowercase}/charts/Example${$0}Chart.tsx:
Use the /add-chart skill patterns but place in your dashboard's charts folder.
Step 9: Create Barrel Export Create src/components/dashboards/${lowercase}/index.ts:
export { ${$0}DashboardProvider , use${$0}Dashboard } from "./${$0}DashboardProvider" ;
export { ${$0}DashboardGrid } from "./${$0}DashboardGrid" ;
export { ${$0}DashboardSidePanel } from "./${$0}DashboardSidePanel" ;
export { ${UPPERCASE }_CHART_REGISTRY, get${$0}ChartById } from "./charts" ;
Step 10: Add Navigation Link (Optional) Update header navigation to include link to new dashboard.
Data Attributes Reference These data attributes are used for selection box exclusion:
Attribute Element Purpose data-chart-cardChart wrapper Exclude from selection start data-text-elementText label Exclude from selection start data-margin-handleMargin lines Exclude from selection start
When dragging starts on elements with these attributes, the selection box won't activate.
Files Created Summary File Purpose components/dashboards/${lowercase}/charts/index.tsxChart registry components/dashboards/${lowercase}/charts/Example${$0}Chart.tsxFirst chart components/dashboards/${lowercase}/${$0}DashboardProvider.tsxState management components/dashboards/${lowercase}/${$0}DashboardGrid.tsxGrid canvas with selection components/dashboards/${lowercase}/${$0}DashboardSidePanel.tsxCustomization panel components/dashboards/${lowercase}/index.tsBarrel export types/${lowercase}-dashboard.tsDashboard-specific types pages/${lowercase}-dashboard.tsxDashboard page
Gotchas: Graceful Degradation Never gate all page content behind a single data endpoint's success. When a page uses multiple independent API endpoints, each section should handle errors independently:
Stats/summary cards: Show "--" when their endpoint fails
Lists/tables from separate endpoints: Render even if stats fail
Charts: Show empty state, not a full-page error
Use inline warnings instead of full-page blocking error cards
{error && <FullPageError /> }
{data && !error && <AllContent /> }
{!isLoading && (
<>
{error && <InlineWarning /> }
{data && <DataDependentSection /> }
<IndependentSection /> {/* always renders */}
</>
)}
Verification Checklist </p >
</div >
</div >
<div className ="flex items-center justify-center py-24" >
<GameLoader />
</div >
<Card className ="p-12 mb-6" >
<div className ="flex flex-col items-center justify-center" >
<div className ="animate-spin rounded-full h-12 w-12 border-b-2 border-primary mb-4" />
<p className ="text-muted-foreground" >
</p >
</div >
</Card >
<Card className ="p-3 sm:p-4 mb-4 sm:mb-6 border-destructive/50 bg-destructive/5" >
<div className ="flex items-center justify-between gap-3" >
<p className ="text-destructive text-sm" >
</p >
</div >
</Card >
<div ref ={gridAreaRef} >
</div >
</div >
</div >
</>