admin-dashboard
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAdmin & Developer Suite Development
管理与开发者套件开发
This skill helps you extend the admin dashboard and build internal tools following the established patterns.
本技能可帮助你遵循既定模式扩展管理控制台并构建内部工具。
Architecture Overview
架构概述
/admin - Admin Dashboard (user metrics, access control, audit)
/dev - Developer Portal (docs, code browser, feature map) [PLANNED]
/ops - Operations Console (infrastructure, logs, incidents) [PLANNED]See for the full design specification.
docs/ADMIN-DEVELOPER-SUITE.md/admin - 管理控制台(用户指标、访问控制、审计)
/dev - 开发者门户(文档、代码浏览器、功能地图)[规划中]
/ops - 运维控制台(基础设施、日志、事件)[规划中]完整设计规范请查看 。
docs/ADMIN-DEVELOPER-SUITE.mdCurrent Admin Dashboard Structure
当前管理控制台结构
Location:
src/app/admin/page.tsx位置:
src/app/admin/page.tsxExisting Tabs
现有标签页
| Tab | Purpose | Data Source |
|---|---|---|
| Overview | Quick stats (users, check-ins, messages) | |
| Funnel | User engagement waterfall | |
| Page Views | Analytics by page path | |
| Users | User roster with activity | |
| Access Requests | Pending/approved/denied requests | |
| Allowed Emails | Email whitelist management | |
| Email Templates | Preview system emails | Local data |
| 标签页 | 用途 | 数据源 |
|---|---|---|
| 概览 | 快速统计(用户、签到、消息) | |
| 转化漏斗 | 用户参与度瀑布图 | |
| 页面访问量 | 按页面路径统计的分析数据 | |
| 用户 | 含活动记录的用户列表 | |
| 访问请求 | 待处理/已批准/已拒绝的请求 | |
| 允许邮箱 | 邮箱白名单管理 | |
| 邮件模板 | 预览系统邮件 | 本地数据 |
Planned Tabs (from design)
规划中的标签页(来自设计文档)
| Tab | Purpose | Status |
|---|---|---|
| Production Health | API latency, Core Web Vitals | Pending |
| Error Tracking | HIPAA-safe error aggregation | Pending |
| External Services | Anthropic, DB, Push status | Pending |
| AI Analytics | Conversation metrics, tokens | Pending |
| Audit Logs | HIPAA compliance viewer | Pending |
| 标签页 | 用途 | 状态 |
|---|---|---|
| 生产环境健康度 | API延迟、核心Web指标 | 待开发 |
| 错误追踪 | 符合HIPAA标准的错误聚合 | 待开发 |
| 外部服务 | Anthropic、数据库、推送服务状态 | 待开发 |
| AI分析 | 对话指标、令牌使用量 | 待开发 |
| 审计日志 | HIPAA合规查看器 | 待开发 |
Adding a New Admin Tab
添加新的管理标签页
1. Create the Tab Content Component
1. 创建标签页内容组件
typescript
// 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>
);
}typescript
// 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. Add the Tab to the Tab List
2. 将标签页添加到标签列表
typescript
const tabs = [
{ id: 'overview', label: 'Overview' },
{ id: 'health', label: 'Production Health' }, // NEW
{ id: 'funnel', label: 'Funnel' },
// ...
];typescript
const tabs = [
{ id: 'overview', label: 'Overview' },
{ id: 'health', label: 'Production Health' }, // NEW
{ id: 'funnel', label: 'Funnel' },
// ...
];3. Add the Tab Content Renderer
3. 添加标签页内容渲染器
typescript
function renderTabContent(tabId: string) {
switch (tabId) {
case 'overview':
return <OverviewTab stats={stats} />;
case 'health':
return <ProductionHealthTab />; // NEW
// ...
}
}typescript
function renderTabContent(tabId: string) {
switch (tabId) {
case 'overview':
return <OverviewTab stats={stats} />;
case 'health':
return <ProductionHealthTab />; // NEW
// ...
}
}Creating Admin API Endpoints
创建管理API端点
Pattern: Admin Stats Endpoint
模式:管理统计数据端点
typescript
// 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);
}typescript
// 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);
}Key Patterns
核心模式
StatCard Component
StatCard组件
typescript
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>
);
}typescript
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>
);
}Data Fetching Pattern
数据获取模式
typescript
// 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 };
}typescript
// 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-Safe Error Display
HIPAA合规的错误展示
typescript
// 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>
);
}typescript
// 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>
);
}Database Tables for Admin Features
管理功能相关的数据库表
Existing tables:
- - Admin role assignments
adminUsers - - Email whitelist
allowedEmails - - Access request queue
accessRequests - - HIPAA audit trail
auditLog - - Navigation analytics
pageViews
Planned tables (from design):
- - API timing data
api_metrics - - Aggregated errors
app_errors - - External service status
service_health - - AI chat metadata
conversation_analytics - - Incident tracking
incidents
现有表:
- - 管理员角色分配
adminUsers - - 邮箱白名单
allowedEmails - - 访问请求队列
accessRequests - - HIPAA审计日志
auditLog - - 导航分析数据
pageViews
规划中的表(来自设计文档):
- - API时序数据
api_metrics - - 聚合后的错误数据
app_errors - - 外部服务状态
service_health - - AI聊天元数据
conversation_analytics - - 事件追踪
incidents
Access Control
访问控制
typescript
// 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 });
}typescript
// 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 });
}Testing Admin Features
管理功能测试
typescript
// 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);
});
});typescript
// 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);
});
});Design Resources
设计资源
- Full design spec:
docs/ADMIN-DEVELOPER-SUITE.md - Design system: Use existing components from
src/components/ui/ - Colors: Follow therapeutic palette (navy, teal, coral, cream)
- 完整设计规范:
docs/ADMIN-DEVELOPER-SUITE.md - 设计系统:使用中的现有组件
src/components/ui/ - 配色:遵循治疗系调色板(藏青、蓝绿、珊瑚、米白)