229 lines
7.0 KiB
TypeScript
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;
|