AgentSkillsCN

Dashboard Scaffold

仪表板框架

SKILL.md

Dashboard Scaffold Skill

Generate Admin Dashboard: Sidebar, stats, tables, and charts.


🎯 Purpose

When user says: "Create an admin dashboard" or "Add dashboard"

Generate a complete dashboard with:

  • Sidebar navigation
  • Stats cards
  • Data tables
  • Charts

📁 Generated Structure

code
src/
├── app/
│   └── dashboard/
│       ├── layout.tsx
│       ├── page.tsx
│       ├── analytics/
│       │   └── page.tsx
│       ├── users/
│       │   └── page.tsx
│       └── settings/
│           └── page.tsx
└── components/
    └── dashboard/
        ├── sidebar.tsx
        ├── header.tsx
        ├── stat-card.tsx
        ├── chart.tsx
        └── data-table.tsx

🏠 Dashboard Layout

tsx
// app/dashboard/layout.tsx
import { Sidebar } from "@/components/dashboard/sidebar"
import { Header } from "@/components/dashboard/header"
import { auth } from "@/auth"
import { redirect } from "next/navigation"

export default async function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  const session = await auth()
  if (!session?.user) redirect("/login")

  return (
    <div className="flex h-screen bg-gray-100">
      <Sidebar />
      <div className="flex-1 flex flex-col overflow-hidden">
        <Header user={session.user} />
        <main className="flex-1 overflow-y-auto p-6">
          {children}
        </main>
      </div>
    </div>
  )
}

📊 Dashboard Page

tsx
// app/dashboard/page.tsx
import { StatCard } from "@/components/dashboard/stat-card"
import { RevenueChart } from "@/components/dashboard/charts/revenue-chart"
import { RecentOrders } from "@/components/dashboard/recent-orders"

export default async function DashboardPage() {
  const stats = await getStats()

  return (
    <div className="space-y-6">
      <h1 className="text-2xl font-bold">Dashboard</h1>

      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
        <StatCard
          title="Total Revenue"
          value={`$${stats.revenue.toLocaleString()}`}
          change="+12.5%"
          changeType="positive"
          icon="💰"
        />
        <StatCard
          title="Total Orders"
          value={stats.orders.toLocaleString()}
          change="+8.2%"
          changeType="positive"
          icon="📦"
        />
        <StatCard
          title="Active Users"
          value={stats.users.toLocaleString()}
          change="+3.1%"
          changeType="positive"
          icon="👥"
        />
        <StatCard
          title="Conversion Rate"
          value={`${stats.conversionRate}%`}
          change="-0.5%"
          changeType="negative"
          icon="📈"
        />
      </div>

      <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
        <div className="lg:col-span-2">
          <RevenueChart />
        </div>
        <div>
          <RecentOrders />
        </div>
      </div>
    </div>
  )
}

🧭 Sidebar

tsx
// components/dashboard/sidebar.tsx
"use client"
import Link from "next/link"
import { usePathname } from "next/navigation"
import { cn } from "@/lib/utils"

const navigation = [
  { name: "Dashboard", href: "/dashboard", icon: "🏠" },
  { name: "Analytics", href: "/dashboard/analytics", icon: "📊" },
  { name: "Users", href: "/dashboard/users", icon: "👥" },
  { name: "Orders", href: "/dashboard/orders", icon: "📦" },
  { name: "Products", href: "/dashboard/products", icon: "🏷️" },
  { name: "Settings", href: "/dashboard/settings", icon: "⚙️" },
]

export function Sidebar() {
  const pathname = usePathname()

  return (
    <aside className="w-64 bg-gray-900 text-white flex flex-col">
      <div className="p-4 border-b border-gray-800">
        <h1 className="text-xl font-bold">Admin Panel</h1>
      </div>

      <nav className="flex-1 p-4 space-y-1">
        {navigation.map((item) => (
          <Link
            key={item.name}
            href={item.href}
            className={cn(
              "flex items-center gap-3 px-3 py-2 rounded-lg transition-colors",
              pathname === item.href
                ? "bg-gray-800 text-white"
                : "text-gray-400 hover:bg-gray-800 hover:text-white"
            )}
          >
            <span>{item.icon}</span>
            {item.name}
          </Link>
        ))}
      </nav>

      <div className="p-4 border-t border-gray-800">
        <Link href="/dashboard/settings" className="text-sm text-gray-400 hover:text-white">
          Help & Support
        </Link>
      </div>
    </aside>
  )
}

📈 Stat Card

tsx
// components/dashboard/stat-card.tsx
interface StatCardProps {
  title: string
  value: string
  change: string
  changeType: "positive" | "negative" | "neutral"
  icon: string
}

export function StatCard({ title, value, change, changeType, icon }: StatCardProps) {
  return (
    <div className="bg-white rounded-lg shadow p-6">
      <div className="flex items-center justify-between">
        <p className="text-sm font-medium text-gray-600">{title}</p>
        <span className="text-2xl">{icon}</span>
      </div>
      <p className="mt-2 text-3xl font-bold">{value}</p>
      <p
        className={cn(
          "mt-2 text-sm",
          changeType === "positive" && "text-green-600",
          changeType === "negative" && "text-red-600",
          changeType === "neutral" && "text-gray-600"
        )}
      >
        {change} from last period
      </p>
    </div>
  )
}

📋 Data Table

tsx
// components/dashboard/data-table.tsx
interface Column<T> {
  key: keyof T
  header: string
  render?: (value: T[keyof T], item: T) => React.ReactNode
}

interface DataTableProps<T> {
  data: T[]
  columns: Column<T>[]
}

export function DataTable<T extends { id: string }>({ data, columns }: DataTableProps<T>) {
  return (
    <div className="bg-white rounded-lg shadow overflow-hidden">
      <table className="w-full">
        <thead className="bg-gray-50">
          <tr>
            {columns.map((col) => (
              <th key={String(col.key)} className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase">
                {col.header}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="divide-y divide-gray-200">
          {data.map((row) => (
            <tr key={row.id} className="hover:bg-gray-50">
              {columns.map((col) => (
                <td key={String(col.key)} className="px-6 py-4 whitespace-nowrap">
                  {col.render ? col.render(row[col.key], row) : String(row[col.key])}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  )
}

Admin dashboard in minutes!