Files
skybridge/kms-frontend/src/components/Dashboard.tsx
2025-08-22 19:19:16 -04:00

229 lines
7.0 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Card, Row, Col, Statistic, Typography, Space, Alert, Spin } from 'antd';
import {
AppstoreOutlined,
KeyOutlined,
UserOutlined,
CheckCircleOutlined,
ExclamationCircleOutlined,
} from '@ant-design/icons';
import { apiService } from '../services/apiService';
const { Title } = Typography;
interface DashboardStats {
totalApplications: number;
totalTokens: number;
healthStatus: 'healthy' | 'unhealthy';
readinessStatus: 'ready' | 'not-ready';
}
const Dashboard: React.FC = () => {
const [stats, setStats] = useState<DashboardStats>({
totalApplications: 0,
totalTokens: 0,
healthStatus: 'unhealthy',
readinessStatus: 'not-ready',
});
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string>('');
useEffect(() => {
loadDashboardData();
}, []);
const loadDashboardData = async () => {
try {
setLoading(true);
setError('');
// Load health status
const [healthResponse, readinessResponse] = await Promise.allSettled([
apiService.healthCheck(),
apiService.readinessCheck(),
]);
const healthStatus = healthResponse.status === 'fulfilled' ? 'healthy' : 'unhealthy';
const readinessStatus = readinessResponse.status === 'fulfilled' ? 'ready' : 'not-ready';
// Load applications count
let totalApplications = 0;
let totalTokens = 0;
try {
const appsResponse = await apiService.getApplications(100, 0);
totalApplications = appsResponse.count;
// Count tokens across all applications
for (const app of appsResponse.data) {
try {
const tokensResponse = await apiService.getTokensForApplication(app.app_id, 100, 0);
totalTokens += tokensResponse.count;
} catch (tokenError) {
console.warn(`Failed to load tokens for app ${app.app_id}:`, tokenError);
}
}
} catch (appsError) {
console.warn('Failed to load applications:', appsError);
}
setStats({
totalApplications,
totalTokens,
healthStatus,
readinessStatus,
});
} catch (err) {
console.error('Dashboard error:', err);
setError('Failed to load dashboard data. Please check your connection.');
} finally {
setLoading(false);
}
};
if (loading) {
return (
<div style={{ textAlign: 'center', padding: '50px' }}>
<Spin size="large" />
<div style={{ marginTop: '16px' }}>Loading dashboard...</div>
</div>
);
}
return (
<div>
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<div>
<Title level={2}>Dashboard</Title>
<p>Welcome to the Key Management System dashboard. Monitor your applications, tokens, and system health.</p>
</div>
{error && (
<Alert
message="Error"
description={error}
type="error"
showIcon
closable
onClose={() => setError('')}
/>
)}
{/* System Status */}
<Card title="System Status" style={{ marginBottom: '24px' }}>
<Row gutter={16}>
<Col span={12}>
<Card>
<Statistic
title="Health Status"
value={stats.healthStatus === 'healthy' ? 'Healthy' : 'Unhealthy'}
prefix={
stats.healthStatus === 'healthy' ? (
<CheckCircleOutlined style={{ color: '#52c41a' }} />
) : (
<ExclamationCircleOutlined style={{ color: '#ff4d4f' }} />
)
}
valueStyle={{
color: stats.healthStatus === 'healthy' ? '#52c41a' : '#ff4d4f',
}}
/>
</Card>
</Col>
<Col span={12}>
<Card>
<Statistic
title="Readiness Status"
value={stats.readinessStatus === 'ready' ? 'Ready' : 'Not Ready'}
prefix={
stats.readinessStatus === 'ready' ? (
<CheckCircleOutlined style={{ color: '#52c41a' }} />
) : (
<ExclamationCircleOutlined style={{ color: '#ff4d4f' }} />
)
}
valueStyle={{
color: stats.readinessStatus === 'ready' ? '#52c41a' : '#ff4d4f',
}}
/>
</Card>
</Col>
</Row>
</Card>
{/* Statistics */}
<Row gutter={16}>
<Col xs={24} sm={12} lg={8}>
<Card>
<Statistic
title="Total Applications"
value={stats.totalApplications}
prefix={<AppstoreOutlined />}
valueStyle={{ color: '#1890ff' }}
/>
</Card>
</Col>
<Col xs={24} sm={12} lg={8}>
<Card>
<Statistic
title="Total Tokens"
value={stats.totalTokens}
prefix={<KeyOutlined />}
valueStyle={{ color: '#52c41a' }}
/>
</Card>
</Col>
<Col xs={24} sm={12} lg={8}>
<Card>
<Statistic
title="Active Users"
value={1}
prefix={<UserOutlined />}
valueStyle={{ color: '#722ed1' }}
/>
</Card>
</Col>
</Row>
{/* Quick Actions */}
<Card title="Quick Actions">
<Row gutter={16}>
<Col xs={24} sm={8}>
<Card
hoverable
onClick={() => window.location.pathname = '/applications'}
style={{ textAlign: 'center', cursor: 'pointer' }}
>
<AppstoreOutlined style={{ fontSize: '32px', color: '#1890ff', marginBottom: '8px' }} />
<div>Manage Applications</div>
</Card>
</Col>
<Col xs={24} sm={8}>
<Card
hoverable
onClick={() => window.location.pathname = '/tokens'}
style={{ textAlign: 'center', cursor: 'pointer' }}
>
<KeyOutlined style={{ fontSize: '32px', color: '#52c41a', marginBottom: '8px' }} />
<div>Manage Tokens</div>
</Card>
</Col>
<Col xs={24} sm={8}>
<Card
hoverable
onClick={() => window.location.pathname = '/audit'}
style={{ textAlign: 'center', cursor: 'pointer' }}
>
<ExclamationCircleOutlined style={{ fontSize: '32px', color: '#fa8c16', marginBottom: '8px' }} />
<div>View Audit Log</div>
</Card>
</Col>
</Row>
</Card>
</Space>
</div>
);
};
export default Dashboard;