admin-dashboard について
既存の管理ダッシュボードを拡張し、プロジェクトの既定パターンに沿って内部ツールを開発するためのスキルです。タブ、APIエンドポイント、アクセス制御、データ取得、HIPAAに配慮したエラー表示を扱います。
主な機能
- 01管理ダッシュボードのタブ拡張
- 02管理用APIエンドポイントの作成
- 03共通のデータ取得パターンの利用
- 04管理者アクセス制御の適用
- 05集約エラーの安全な表示
- 06管理機能のテスト
活用シーン
- 01ダッシュボードに管理機能を追加
- 02アクセス制御付き管理エンドポイントの開発
- 03ユーザー・アクセス申請・分析データの表示
- 04管理者権限に関わる機能のテスト
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ curiositech/ some_claude_skills --skill admin-dashboardインストール後、Claude Code で/admin-dashboardを入力して読み込まれたか確認します。
同梱ファイル.claude-plugin/plugin.json
詳細
管理者・開発者向けスイートの開発
このスキルでは、既存のパターンに沿って管理者ダッシュボードを拡張し、内部ツールを構築する方法を説明します。
アーキテクチャの概要
/admin - Admin Dashboard (user metrics, access control, audit)
/dev - Developer Portal (docs, code browser, feature map) [PLANNED]
/ops - Operations Console (infrastructure, logs, incidents) [PLANNED]
設計仕様の全文については、docs/ADMIN-DEVELOPER-SUITE.md を参照してください。
現在の管理者ダッシュボードの構成
場所:src/app/admin/page.tsx
既存のタブ
| タブ | 目的 | データソース |
|---|---|---|
| 概要 | 簡易統計(ユーザー、チェックイン、メッセージ) | /api/admin/stats |
| ファネル | ユーザーエンゲージメントの推移 | /api/admin/stats |
| ページビュー | ページパス別の分析 | /api/admin/stats |
| ユーザー | アクティビティ付きユーザー一覧 | /api/admin/stats |
| アクセスリクエスト | 承認待ち/承認済み/拒否済みのリクエスト | /api/admin/access-requests |
| 許可済みメール | メールの許可リスト管理 | /api/admin/allowed-emails |
| メールテンプレート | システムメールのプレビュー | ローカルデータ |
予定されているタブ(設計より)
| タブ | 目的 | ステータス |
|---|---|---|
| 本番環境の健全性 | API のレイテンシ、Core Web Vitals | 保留中 |
| エラー追跡 | HIPAA に準拠したエラーの集約 | 保留中 |
| 外部サービス | Anthropic、DB、Push のステータス | 保留中 |
| AI 分析 | 会話のメトリクス、トークン | 保留中 |
| 監査ログ | HIPAA コンプライアンスビューアー | 保留中 |
新しい管理者タブを追加する
1. タブのコンテンツコンポーネントを作成する
// In src/app/admin/page.tsx, add a new tab component
function ProductionHealthTab() {
const [metrics, setMetrics] = useState<APIMetrics | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchMetrics() {
const res = await fetch('/api/admin/metrics');
const data = await res.json();
setMetrics(data);
setLoading(false);
}
fetchMetrics();
}, []);
if (loading) return <div>Loading...</div>;
return (
<div className="space-y-6">
<div className="grid grid-cols-4 gap-4">
<StatCard label="Uptime" value={metrics.uptime} />
<StatCard label="Avg Latency" value={`${metrics.avgLatency}ms`} />
<StatCard label="Errors (24h)" value={metrics.errorCount} />
<StatCard label="Active Users" value={metrics.activeUsers} />
</div>
{/* More content */}
</div>
);
}
2. タブリストにタブを追加する
const tabs = [
{ id: 'overview', label: 'Overview' },
{ id: 'health', label: 'Production Health' }, // NEW
{ id: 'funnel', label: 'Funnel' },
// ...
];
3. タブのコンテンツレンダラーを追加する
function renderTabContent(tabId: string) {
switch (tabId) {
case 'overview':
return <OverviewTab stats={stats} />;
case 'health':
return <ProductionHealthTab />; // NEW
// ...
}
}
管理者 API エンドポイントを作成する
パターン:管理者統計エンドポイント
// src/app/api/admin/metrics/route.ts
import { requireAdmin } from '@/db/secure-db';
import { createRateLimiter } from '@/lib/rate-limit';
import { logAdminAction } from '@/lib/hipaa/audit';
const rateLimiter = createRateLimiter({
windowMs: 60000,
maxRequests: 60,
keyPrefix: 'admin:metrics'
});
export async function GET(request: Request) {
// 1. Check admin access
const admin = await requireAdmin();
if (!admin) {
return Response.json({ error: 'Forbidden' }, { status: 403 });
}
// 2. Apply rate limiting
const rateLimitResult = await rateLimiter.check(admin.id);
if (!rateLimitResult.allowed) {
return Response.json(
{ error: 'Rate limit exceeded' },
{ status: 429, headers: rateLimitResult.headers }
);
}
// 3. Log admin action
await logAdminAction(
admin.id,
AuditAction.ADMIN_STATS_VIEW,
'metrics',
null
);
// 4. Fetch and return data
const metrics = await getAPIMetrics();
return Response.json(metrics);
}
主なパターン
StatCard コンポーネント
function StatCard({
label,
value,
trend,
status
}: {
label: string;
value: string | number;
trend?: 'up' | 'down' | 'neutral';
status?: 'good' | 'warning' | 'error';
}) {
return (
<div className="rounded-lg border bg-card p-4">
<div className="text-sm text-muted-foreground">{label}</div>
<div className="text-2xl font-bold">{value}</div>
{trend && <TrendIndicator direction={trend} />}
{status && <StatusBadge status={status} />}
</div>
);
}
データ取得パターン
// Use SWR or React Query for real-time updates
import useSWR from 'swr';
function useAdminMetrics() {
const { data, error, isLoading } = useSWR(
'/api/admin/metrics',
fetcher,
{ refreshInterval: 30000 } // Refresh every 30s
);
return { metrics: data, error, isLoading };
}
HIPAA に準拠したエラー表示
// Never show user-specific error details
function ErrorList({ errors }: { errors: AggregatedError[] }) {
return (
<div>
{errors.map(error => (
<div key={error.hash}>
<span className="font-mono">{error.type}</span>
<span>{error.path}</span>
<span>{error.count} occurrences</span>
<span>{error.affectedUsers} users</span>
{/* NO user IDs, NO error messages with PHI */}
</div>
))}
</div>
);
}
管理者機能用のデータベーステーブル
既存のテーブル:
adminUsers- 管理者ロールの割り当てallowedEmails- メールの許可リストaccessRequests- アクセスリクエストのキューauditLog- HIPAA 監査証跡pageViews- ナビゲーション分析
予定されているテーブル(設計より):
api_metrics- API の所要時間データapp_errors- 集約されたエラーservice_health- 外部サービスのステータスconversation_analytics- AI チャットのメタデータincidents- インシデントの追跡
アクセス制御
// Always use requireAdmin() for admin routes
import { requireAdmin } from '@/db/secure-db';
// For super-admin only features
const admin = await requireAdmin();
if (admin.role !== 'super_admin') {
return Response.json({ error: 'Super admin required' }, { status: 403 });
}
管理者機能のテスト
// Mock admin authentication for tests
vi.mock('@/db/secure-db', () => ({
requireAdmin: vi.fn().mockResolvedValue({
id: 'test-admin',
role: 'admin'
})
}));
describe('Admin Metrics Endpoint', () => {
it('returns metrics for authenticated admin', async () => {
const response = await GET(mockRequest);
expect(response.status).toBe(200);
});
it('returns 403 for non-admin', async () => {
vi.mocked(requireAdmin).mockResolvedValueOnce(null);
const response = await GET(mockRequest);
expect(response.status).toBe(403);
});
});
デザインリソース
- 設計仕様の全文:
docs/ADMIN-DEVELOPER-SUITE.md - デザインシステム:
src/components/ui/の既存コンポーネントを使用してください - 色:セラピー向けの配色(ネイビー、ティール、コーラル、クリーム)に従ってください
コメント
Markdown 対応