- Copy admin-dashboard-web → dashboards/admin-web - Copy tracker-dashboard-web → dashboards/tracker-web - Update pnpm-workspace.yaml to include dashboards/* - Replace file: refs with workspace:* for @bytelyst/* packages - Replace all hardcoded LysnrAI/lysnn.com branding with generic platform refs - Make telemetry use NEXT_PUBLIC_PRODUCT_ID / PRODUCT_ID env vars - Update mock credentials, seed data, invitation codes, placeholders - Update READMEs, e2e tests, unit tests for product-agnostic content - Both dashboards pass tsc --noEmit clean
569 lines
13 KiB
TypeScript
569 lines
13 KiB
TypeScript
// Mock data for the admin dashboard
|
|
// Replace with real API calls when backend is ready
|
|
|
|
export interface User {
|
|
id: string;
|
|
name: string;
|
|
email: string;
|
|
plan: 'free' | 'pro' | 'enterprise';
|
|
status: 'active' | 'inactive' | 'suspended';
|
|
createdAt: string;
|
|
lastActive: string;
|
|
totalTokensUsed: number;
|
|
totalRequests: number;
|
|
monthlySpend: number;
|
|
}
|
|
|
|
export interface Subscription {
|
|
id: string;
|
|
name: string;
|
|
price: number;
|
|
interval: 'monthly' | 'yearly';
|
|
features: string[];
|
|
limits: {
|
|
tokensPerMonth: number;
|
|
requestsPerDay: number;
|
|
modelsAllowed: string[];
|
|
};
|
|
activeUsers: number;
|
|
}
|
|
|
|
export interface ApiToken {
|
|
id: string;
|
|
userId: string;
|
|
userName: string;
|
|
name: string;
|
|
prefix: string;
|
|
status: 'active' | 'revoked' | 'expired';
|
|
createdAt: string;
|
|
expiresAt: string;
|
|
lastUsed: string | null;
|
|
scopes: string[];
|
|
}
|
|
|
|
export interface UsageRecord {
|
|
date: string;
|
|
tokens: number;
|
|
requests: number;
|
|
cost: number;
|
|
model: string;
|
|
}
|
|
|
|
export interface ModelUsage {
|
|
model: string;
|
|
tokens: number;
|
|
requests: number;
|
|
cost: number;
|
|
percentage: number;
|
|
}
|
|
|
|
export interface DailyMetric {
|
|
date: string;
|
|
activeUsers: number;
|
|
totalRequests: number;
|
|
totalTokens: number;
|
|
revenue: number;
|
|
}
|
|
|
|
// --- Mock Users ---
|
|
export const mockUsers: User[] = [
|
|
{
|
|
id: 'usr_001',
|
|
name: 'Alice Johnson',
|
|
email: 'alice@company.com',
|
|
plan: 'pro',
|
|
status: 'active',
|
|
createdAt: '2025-09-15',
|
|
lastActive: '2026-02-07',
|
|
totalTokensUsed: 2_450_000,
|
|
totalRequests: 12_340,
|
|
monthlySpend: 29.99,
|
|
},
|
|
{
|
|
id: 'usr_002',
|
|
name: 'Bob Smith',
|
|
email: 'bob@startup.io',
|
|
plan: 'enterprise',
|
|
status: 'active',
|
|
createdAt: '2025-08-01',
|
|
lastActive: '2026-02-08',
|
|
totalTokensUsed: 15_800_000,
|
|
totalRequests: 89_200,
|
|
monthlySpend: 149.99,
|
|
},
|
|
{
|
|
id: 'usr_003',
|
|
name: 'Carol Davis',
|
|
email: 'carol@freelance.dev',
|
|
plan: 'free',
|
|
status: 'active',
|
|
createdAt: '2025-12-20',
|
|
lastActive: '2026-02-06',
|
|
totalTokensUsed: 180_000,
|
|
totalRequests: 920,
|
|
monthlySpend: 0,
|
|
},
|
|
{
|
|
id: 'usr_004',
|
|
name: 'David Lee',
|
|
email: 'david@bigcorp.com',
|
|
plan: 'pro',
|
|
status: 'active',
|
|
createdAt: '2025-10-05',
|
|
lastActive: '2026-02-08',
|
|
totalTokensUsed: 5_200_000,
|
|
totalRequests: 28_100,
|
|
monthlySpend: 29.99,
|
|
},
|
|
{
|
|
id: 'usr_005',
|
|
name: 'Eva Martinez',
|
|
email: 'eva@design.co',
|
|
plan: 'pro',
|
|
status: 'inactive',
|
|
createdAt: '2025-11-10',
|
|
lastActive: '2026-01-15',
|
|
totalTokensUsed: 890_000,
|
|
totalRequests: 4_500,
|
|
monthlySpend: 0,
|
|
},
|
|
{
|
|
id: 'usr_006',
|
|
name: 'Frank Wilson',
|
|
email: 'frank@tech.org',
|
|
plan: 'free',
|
|
status: 'active',
|
|
createdAt: '2026-01-02',
|
|
lastActive: '2026-02-07',
|
|
totalTokensUsed: 95_000,
|
|
totalRequests: 480,
|
|
monthlySpend: 0,
|
|
},
|
|
{
|
|
id: 'usr_007',
|
|
name: 'Grace Kim',
|
|
email: 'grace@enterprise.net',
|
|
plan: 'enterprise',
|
|
status: 'active',
|
|
createdAt: '2025-07-20',
|
|
lastActive: '2026-02-08',
|
|
totalTokensUsed: 32_000_000,
|
|
totalRequests: 156_000,
|
|
monthlySpend: 149.99,
|
|
},
|
|
{
|
|
id: 'usr_008',
|
|
name: 'Henry Brown',
|
|
email: 'henry@agency.com',
|
|
plan: 'pro',
|
|
status: 'suspended',
|
|
createdAt: '2025-09-30',
|
|
lastActive: '2026-01-28',
|
|
totalTokensUsed: 3_100_000,
|
|
totalRequests: 15_600,
|
|
monthlySpend: 0,
|
|
},
|
|
{
|
|
id: 'usr_009',
|
|
name: 'Iris Chen',
|
|
email: 'iris@university.edu',
|
|
plan: 'free',
|
|
status: 'active',
|
|
createdAt: '2026-01-15',
|
|
lastActive: '2026-02-08',
|
|
totalTokensUsed: 42_000,
|
|
totalRequests: 210,
|
|
monthlySpend: 0,
|
|
},
|
|
{
|
|
id: 'usr_010',
|
|
name: 'Jake Thompson',
|
|
email: 'jake@consulting.biz',
|
|
plan: 'pro',
|
|
status: 'active',
|
|
createdAt: '2025-11-25',
|
|
lastActive: '2026-02-07',
|
|
totalTokensUsed: 1_800_000,
|
|
totalRequests: 9_200,
|
|
monthlySpend: 29.99,
|
|
},
|
|
];
|
|
|
|
// --- Mock Subscriptions ---
|
|
export const mockSubscriptions: Subscription[] = [
|
|
{
|
|
id: 'plan_free',
|
|
name: 'Free',
|
|
price: 0,
|
|
interval: 'monthly',
|
|
features: ['50,000 tokens/month', '100 requests/day', 'GPT-4o-mini only', 'Community support'],
|
|
limits: {
|
|
tokensPerMonth: 50_000,
|
|
requestsPerDay: 100,
|
|
modelsAllowed: ['gpt-4o-mini'],
|
|
},
|
|
activeUsers: 156,
|
|
},
|
|
{
|
|
id: 'plan_pro',
|
|
name: 'Pro',
|
|
price: 29.99,
|
|
interval: 'monthly',
|
|
features: [
|
|
'2,000,000 tokens/month',
|
|
'1,000 requests/day',
|
|
'All models (GPT-4o, Claude, etc.)',
|
|
'Priority support',
|
|
'Custom vocabulary',
|
|
'LLM text cleanup',
|
|
],
|
|
limits: {
|
|
tokensPerMonth: 2_000_000,
|
|
requestsPerDay: 1_000,
|
|
modelsAllowed: ['gpt-4o-mini', 'gpt-4o', 'claude-3.5-sonnet'],
|
|
},
|
|
activeUsers: 89,
|
|
},
|
|
{
|
|
id: 'plan_enterprise',
|
|
name: 'Enterprise',
|
|
price: 149.99,
|
|
interval: 'monthly',
|
|
features: [
|
|
'Unlimited tokens',
|
|
'Unlimited requests',
|
|
'All models + custom deployments',
|
|
'Dedicated support',
|
|
'SSO / SAML',
|
|
'Audit logs',
|
|
'Custom integrations',
|
|
'SLA guarantee',
|
|
],
|
|
limits: {
|
|
tokensPerMonth: -1,
|
|
requestsPerDay: -1,
|
|
modelsAllowed: ['gpt-4o-mini', 'gpt-4o', 'claude-3.5-sonnet', 'custom'],
|
|
},
|
|
activeUsers: 12,
|
|
},
|
|
];
|
|
|
|
// --- Mock API Tokens ---
|
|
export const mockApiTokens: ApiToken[] = [
|
|
{
|
|
id: 'tok_001',
|
|
userId: 'usr_001',
|
|
userName: 'Alice Johnson',
|
|
name: 'Production App',
|
|
prefix: 'wai_prod_',
|
|
status: 'active',
|
|
createdAt: '2025-10-01',
|
|
expiresAt: '2026-10-01',
|
|
lastUsed: '2026-02-08',
|
|
scopes: ['dictate', 'transcribe', 'cleanup'],
|
|
},
|
|
{
|
|
id: 'tok_002',
|
|
userId: 'usr_002',
|
|
userName: 'Bob Smith',
|
|
name: 'CI/CD Pipeline',
|
|
prefix: 'wai_ci_',
|
|
status: 'active',
|
|
createdAt: '2025-11-15',
|
|
expiresAt: '2026-05-15',
|
|
lastUsed: '2026-02-07',
|
|
scopes: ['dictate', 'transcribe'],
|
|
},
|
|
{
|
|
id: 'tok_003',
|
|
userId: 'usr_004',
|
|
userName: 'David Lee',
|
|
name: 'Dev Environment',
|
|
prefix: 'wai_dev_',
|
|
status: 'active',
|
|
createdAt: '2026-01-10',
|
|
expiresAt: '2026-07-10',
|
|
lastUsed: '2026-02-08',
|
|
scopes: ['dictate', 'transcribe', 'cleanup'],
|
|
},
|
|
{
|
|
id: 'tok_004',
|
|
userId: 'usr_007',
|
|
userName: 'Grace Kim',
|
|
name: 'Team Shared',
|
|
prefix: 'wai_team_',
|
|
status: 'active',
|
|
createdAt: '2025-08-20',
|
|
expiresAt: '2026-08-20',
|
|
lastUsed: '2026-02-08',
|
|
scopes: ['dictate', 'transcribe', 'cleanup', 'admin'],
|
|
},
|
|
{
|
|
id: 'tok_005',
|
|
userId: 'usr_008',
|
|
userName: 'Henry Brown',
|
|
name: 'Old Token',
|
|
prefix: 'wai_old_',
|
|
status: 'revoked',
|
|
createdAt: '2025-06-01',
|
|
expiresAt: '2025-12-01',
|
|
lastUsed: '2025-11-30',
|
|
scopes: ['dictate'],
|
|
},
|
|
{
|
|
id: 'tok_006',
|
|
userId: 'usr_005',
|
|
userName: 'Eva Martinez',
|
|
name: 'Personal',
|
|
prefix: 'wai_per_',
|
|
status: 'expired',
|
|
createdAt: '2025-05-01',
|
|
expiresAt: '2025-11-01',
|
|
lastUsed: '2025-10-28',
|
|
scopes: ['dictate', 'transcribe'],
|
|
},
|
|
];
|
|
|
|
// --- Mock Usage Data (last 30 days) ---
|
|
function generateDailyMetrics(): DailyMetric[] {
|
|
const metrics: DailyMetric[] = [];
|
|
const now = new Date('2026-02-08');
|
|
for (let i = 29; i >= 0; i--) {
|
|
const date = new Date(now);
|
|
date.setDate(date.getDate() - i);
|
|
const dayOfWeek = date.getDay();
|
|
const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
|
|
const base = isWeekend ? 0.6 : 1;
|
|
metrics.push({
|
|
date: date.toISOString().split('T')[0],
|
|
activeUsers: Math.round((120 + Math.random() * 80) * base),
|
|
totalRequests: Math.round((8000 + Math.random() * 4000) * base),
|
|
totalTokens: Math.round((1_500_000 + Math.random() * 800_000) * base),
|
|
revenue: Math.round((1200 + Math.random() * 600) * base * 100) / 100,
|
|
});
|
|
}
|
|
return metrics;
|
|
}
|
|
|
|
export const mockDailyMetrics: DailyMetric[] = generateDailyMetrics();
|
|
|
|
// --- Mock Model Usage ---
|
|
export const mockModelUsage: ModelUsage[] = [
|
|
{
|
|
model: 'gpt-4o-mini',
|
|
tokens: 28_500_000,
|
|
requests: 142_000,
|
|
cost: 428.0,
|
|
percentage: 45,
|
|
},
|
|
{
|
|
model: 'gpt-4o',
|
|
tokens: 18_200_000,
|
|
requests: 68_000,
|
|
cost: 912.0,
|
|
percentage: 29,
|
|
},
|
|
{
|
|
model: 'claude-3.5-sonnet',
|
|
tokens: 12_800_000,
|
|
requests: 52_000,
|
|
cost: 640.0,
|
|
percentage: 20,
|
|
},
|
|
{
|
|
model: 'whisper-v3',
|
|
tokens: 3_600_000,
|
|
requests: 38_000,
|
|
cost: 108.0,
|
|
percentage: 6,
|
|
},
|
|
];
|
|
|
|
// --- Summary Stats ---
|
|
export const mockSummaryStats = {
|
|
totalUsers: 257,
|
|
activeUsers: 198,
|
|
totalRevenue: 8_420.5,
|
|
monthlyRecurring: 4_890.0,
|
|
totalTokensThisMonth: 63_100_000,
|
|
totalRequestsThisMonth: 300_000,
|
|
avgTokensPerUser: 318_686,
|
|
churnRate: 2.3,
|
|
newUsersThisMonth: 34,
|
|
conversionRate: 12.5,
|
|
};
|
|
|
|
// --- Audit Log ---
|
|
export interface AuditEntry {
|
|
id: string;
|
|
timestamp: string;
|
|
actor: string;
|
|
action: string;
|
|
category: 'auth' | 'user' | 'config' | 'token' | 'billing';
|
|
target?: string;
|
|
details?: string;
|
|
ip: string;
|
|
}
|
|
|
|
export const mockAuditLog: AuditEntry[] = [
|
|
{
|
|
id: 'a1',
|
|
timestamp: '2025-02-08T09:12:00Z',
|
|
actor: 'admin@example.com',
|
|
action: 'Login',
|
|
category: 'auth',
|
|
ip: '10.0.1.42',
|
|
details: 'Successful login',
|
|
},
|
|
{
|
|
id: 'a2',
|
|
timestamp: '2025-02-08T08:55:00Z',
|
|
actor: 'admin@example.com',
|
|
action: 'Suspend User',
|
|
category: 'user',
|
|
target: 'bob.johnson@email.com',
|
|
ip: '10.0.1.42',
|
|
details: 'Exceeded abuse threshold',
|
|
},
|
|
{
|
|
id: 'a3',
|
|
timestamp: '2025-02-08T07:30:00Z',
|
|
actor: 'admin@example.com',
|
|
action: 'Revoke Token',
|
|
category: 'token',
|
|
target: 'tok_analytics_v2',
|
|
ip: '10.0.1.42',
|
|
},
|
|
{
|
|
id: 'a4',
|
|
timestamp: '2025-02-07T22:10:00Z',
|
|
actor: 'admin@example.com',
|
|
action: 'Update Plan Pricing',
|
|
category: 'billing',
|
|
target: 'Pro Plan',
|
|
ip: '10.0.1.42',
|
|
details: '$29/mo → $39/mo',
|
|
},
|
|
{
|
|
id: 'a5',
|
|
timestamp: '2025-02-07T18:45:00Z',
|
|
actor: 'admin@example.com',
|
|
action: 'Enable Maintenance Mode',
|
|
category: 'config',
|
|
ip: '10.0.1.42',
|
|
},
|
|
{
|
|
id: 'a6',
|
|
timestamp: '2025-02-07T18:00:00Z',
|
|
actor: 'admin@example.com',
|
|
action: 'Update Rate Limit',
|
|
category: 'config',
|
|
ip: '10.0.1.42',
|
|
details: 'Per-user limit: 60 → 100 req/min',
|
|
},
|
|
{
|
|
id: 'a7',
|
|
timestamp: '2025-02-07T15:20:00Z',
|
|
actor: 'viewer@example.com',
|
|
action: 'Login',
|
|
category: 'auth',
|
|
ip: '192.168.1.15',
|
|
details: 'Successful login',
|
|
},
|
|
{
|
|
id: 'a8',
|
|
timestamp: '2025-02-07T14:00:00Z',
|
|
actor: 'admin@example.com',
|
|
action: 'Create Token',
|
|
category: 'token',
|
|
target: 'tok_mobile_app',
|
|
ip: '10.0.1.42',
|
|
details: 'Scopes: stt, cleanup',
|
|
},
|
|
{
|
|
id: 'a9',
|
|
timestamp: '2025-02-07T11:30:00Z',
|
|
actor: 'admin@example.com',
|
|
action: 'Reactivate User',
|
|
category: 'user',
|
|
target: 'carol.williams@email.com',
|
|
ip: '10.0.1.42',
|
|
},
|
|
{
|
|
id: 'a10',
|
|
timestamp: '2025-02-07T09:00:00Z',
|
|
actor: 'admin@example.com',
|
|
action: 'Login',
|
|
category: 'auth',
|
|
ip: '10.0.1.42',
|
|
details: 'Successful login',
|
|
},
|
|
{
|
|
id: 'a11',
|
|
timestamp: '2025-02-06T16:45:00Z',
|
|
actor: 'admin@example.com',
|
|
action: 'Disable Maintenance Mode',
|
|
category: 'config',
|
|
ip: '10.0.1.42',
|
|
},
|
|
{
|
|
id: 'a12',
|
|
timestamp: '2025-02-06T14:20:00Z',
|
|
actor: 'admin@example.com',
|
|
action: 'Create New Plan',
|
|
category: 'billing',
|
|
target: 'Startup Plan',
|
|
ip: '10.0.1.42',
|
|
details: '$19/mo, 5M tokens',
|
|
},
|
|
{
|
|
id: 'a13',
|
|
timestamp: '2025-02-06T10:05:00Z',
|
|
actor: 'unknown@attacker.com',
|
|
action: 'Failed Login',
|
|
category: 'auth',
|
|
ip: '203.0.113.99',
|
|
details: 'Invalid credentials (attempt 5)',
|
|
},
|
|
{
|
|
id: 'a14',
|
|
timestamp: '2025-02-06T10:04:00Z',
|
|
actor: 'unknown@attacker.com',
|
|
action: 'Failed Login',
|
|
category: 'auth',
|
|
ip: '203.0.113.99',
|
|
details: 'Invalid credentials (attempt 4)',
|
|
},
|
|
{
|
|
id: 'a15',
|
|
timestamp: '2025-02-05T20:30:00Z',
|
|
actor: 'admin@example.com',
|
|
action: 'Update Azure Config',
|
|
category: 'config',
|
|
ip: '10.0.1.42',
|
|
details: 'Changed speech region to swedencentral',
|
|
},
|
|
];
|
|
|
|
// --- Helper: format numbers ---
|
|
export function formatNumber(n: number): string {
|
|
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
|
|
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;
|
|
return n.toString();
|
|
}
|
|
|
|
export function formatCurrency(n: number): string {
|
|
return new Intl.NumberFormat('en-US', {
|
|
style: 'currency',
|
|
currency: 'USD',
|
|
}).format(n);
|
|
}
|
|
|
|
export function formatDate(dateStr: string): string {
|
|
return new Date(dateStr).toLocaleDateString('en-US', {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
year: 'numeric',
|
|
});
|
|
}
|