@dbs-portal/module-tenant-management
Version:
Tenant management and multi-tenancy support module
144 lines • 10.9 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
* TenantAnalytics Component - Comprehensive tenant analytics dashboard
*/
import React, { useState } from 'react';
import { Card, Row, Col, Statistic, Select, DatePicker, Space, Typography, Alert, Spin, Progress, Table, Tag } from 'antd';
import { UserOutlined, DatabaseOutlined, ApiOutlined, ClockCircleOutlined, RiseOutlined, DollarOutlined, BarChartOutlined } from '@ant-design/icons';
import { format, subDays, subWeeks, subMonths, subYears } from 'date-fns';
import { useTenantAnalytics } from '../hooks';
const { Title, Text } = Typography;
const { Option } = Select;
const { RangePicker } = DatePicker;
export const TenantAnalytics = ({ tenantId, period = 'month', className }) => {
const [selectedPeriod, setSelectedPeriod] = useState(period);
const [customDateRange, setCustomDateRange] = useState(null);
// Fetch analytics data
const { data: analytics, isLoading, error } = useTenantAnalytics(tenantId, selectedPeriod);
if (isLoading) {
return (_jsx(Card, { className: className, children: _jsx(Spin, { size: "large", style: { display: 'block', textAlign: 'center', padding: '50px' } }) }));
}
if (error || !analytics) {
return (_jsx(Card, { className: className, children: _jsx(Alert, { message: "Failed to load analytics", description: error?.message || 'Analytics data not available', type: "error", showIcon: true }) }));
}
const getPeriodLabel = (period) => {
switch (period) {
case 'day': return 'Daily';
case 'week': return 'Weekly';
case 'month': return 'Monthly';
case 'year': return 'Yearly';
default: return 'Monthly';
}
};
const formatMetricValue = (value, type = 'number') => {
switch (type) {
case 'bytes':
if (value >= 1024 * 1024 * 1024)
return `${(value / (1024 * 1024 * 1024)).toFixed(2)} GB`;
if (value >= 1024 * 1024)
return `${(value / (1024 * 1024)).toFixed(2)} MB`;
if (value >= 1024)
return `${(value / 1024).toFixed(2)} KB`;
return `${value} B`;
case 'percentage':
return `${value.toFixed(2)}%`;
case 'currency':
return `$${value.toLocaleString()}`;
default:
return value.toLocaleString();
}
};
const getGrowthColor = (current, previous) => {
if (current > previous)
return '#52c41a';
if (current < previous)
return '#ff4d4f';
return '#1890ff';
};
// Calculate growth rates (simplified - would need historical data)
const calculateGrowthRate = (data) => {
if (data.length < 2)
return 0;
const latest = data[data.length - 1];
const previous = data[data.length - 2];
const latestValue = latest?.count || latest?.usage || latest?.amount || 0;
const previousValue = previous?.count || previous?.usage || previous?.amount || 0;
if (previousValue === 0)
return latestValue > 0 ? 100 : 0;
return ((latestValue - previousValue) / previousValue) * 100;
};
const userGrowthRate = calculateGrowthRate(analytics.metrics.userGrowth);
const storageGrowthRate = calculateGrowthRate(analytics.metrics.storageUsage);
const apiGrowthRate = calculateGrowthRate(analytics.metrics.apiCalls);
// Prepare table data for detailed metrics
const detailedMetrics = [
{
key: 'users',
metric: 'Total Users',
current: analytics.summary.totalUsers,
growth: userGrowthRate,
icon: _jsx(UserOutlined, {})
},
{
key: 'active_users',
metric: 'Active Users',
current: analytics.summary.activeUsers,
growth: userGrowthRate * 0.8, // Simplified calculation
icon: _jsx(UserOutlined, {})
},
{
key: 'storage',
metric: 'Storage Used',
current: analytics.summary.storageUsed,
growth: storageGrowthRate,
icon: _jsx(DatabaseOutlined, {})
},
{
key: 'api_calls',
metric: 'API Calls',
current: analytics.summary.totalApiCalls,
growth: apiGrowthRate,
icon: _jsx(ApiOutlined, {})
}
];
const columns = [
{
title: 'Metric',
dataIndex: 'metric',
key: 'metric',
render: (text, record) => (_jsxs(Space, { children: [record.icon, _jsx(Text, { strong: true, children: text })] }))
},
{
title: 'Current Value',
dataIndex: 'current',
key: 'current',
render: (value, record) => {
const type = record.key === 'storage' ? 'bytes' : 'number';
return formatMetricValue(value, type);
}
},
{
title: 'Growth Rate',
dataIndex: 'growth',
key: 'growth',
render: (growth) => (_jsxs(Space, { children: [_jsx(RiseOutlined, { style: { color: getGrowthColor(growth, 0) } }), _jsxs(Text, { style: { color: getGrowthColor(growth, 0) }, children: [growth > 0 ? '+' : '', growth.toFixed(1), "%"] })] }))
}
];
return (_jsxs("div", { className: className, children: [_jsx(Card, { children: _jsxs(Row, { justify: "space-between", align: "middle", children: [_jsxs(Col, { children: [_jsxs(Title, { level: 4, style: { margin: 0 }, children: [_jsx(BarChartOutlined, {}), " Tenant Analytics"] }), _jsxs(Text, { type: "secondary", children: [getPeriodLabel(selectedPeriod), " Overview"] })] }), _jsx(Col, { children: _jsxs(Space, { children: [_jsxs(Select, { value: selectedPeriod, onChange: setSelectedPeriod, style: { width: 120 }, children: [_jsx(Option, { value: "day", children: "Daily" }), _jsx(Option, { value: "week", children: "Weekly" }), _jsx(Option, { value: "month", children: "Monthly" }), _jsx(Option, { value: "year", children: "Yearly" })] }), _jsx(RangePicker, { onChange: (dates) => {
if (dates && dates[0] && dates[1]) {
setCustomDateRange([dates[0].toDate(), dates[1].toDate()]);
}
else {
setCustomDateRange(null);
}
}, placeholder: ['Start Date', 'End Date'] })] }) })] }) }), _jsxs(Row, { gutter: 16, style: { marginTop: 16 }, children: [_jsx(Col, { span: 6, children: _jsxs(Card, { children: [_jsx(Statistic, { title: "Total Users", value: analytics.summary.totalUsers, prefix: _jsx(UserOutlined, {}), valueStyle: { color: '#1890ff' } }), _jsx(Progress, { percent: Math.min((analytics.summary.activeUsers / analytics.summary.totalUsers) * 100, 100), size: "small", format: () => `${analytics.summary.activeUsers} active` })] }) }), _jsx(Col, { span: 6, children: _jsxs(Card, { children: [_jsx(Statistic, { title: "Storage Used", value: analytics.summary.storageUsed, suffix: "MB", prefix: _jsx(DatabaseOutlined, {}), valueStyle: { color: '#52c41a' } }), _jsx(Text, { type: "secondary", style: { fontSize: '12px' }, children: formatMetricValue(analytics.summary.storageUsed * 1024 * 1024, 'bytes') })] }) }), _jsx(Col, { span: 6, children: _jsxs(Card, { children: [_jsx(Statistic, { title: "API Calls", value: analytics.summary.totalApiCalls, prefix: _jsx(ApiOutlined, {}), valueStyle: { color: '#722ed1' } }), _jsxs(Text, { type: "secondary", style: { fontSize: '12px' }, children: ["Avg: ", analytics.summary.averageResponseTime, "ms response"] })] }) }), _jsx(Col, { span: 6, children: _jsxs(Card, { children: [_jsx(Statistic, { title: "Uptime", value: analytics.summary.uptime, suffix: "%", precision: 2, prefix: _jsx(ClockCircleOutlined, {}), valueStyle: {
color: analytics.summary.uptime >= 99 ? '#52c41a' :
analytics.summary.uptime >= 95 ? '#faad14' : '#ff4d4f'
} }), _jsx(Progress, { percent: analytics.summary.uptime, size: "small", strokeColor: analytics.summary.uptime >= 99 ? '#52c41a' :
analytics.summary.uptime >= 95 ? '#faad14' : '#ff4d4f', showInfo: false })] }) })] }), analytics.metrics.revenue && analytics.metrics.revenue.length > 0 && (_jsxs(Card, { style: { marginTop: 16 }, children: [_jsxs(Title, { level: 5, children: [_jsx(DollarOutlined, {}), " Revenue Metrics"] }), _jsxs(Row, { gutter: 16, children: [_jsx(Col, { span: 8, children: _jsx(Statistic, { title: "Total Revenue", value: analytics.metrics.revenue.reduce((sum, item) => sum + item.amount, 0), prefix: _jsx(DollarOutlined, {}), precision: 2, valueStyle: { color: '#52c41a' } }) }), _jsx(Col, { span: 8, children: _jsx(Statistic, { title: "Average per User", value: analytics.summary.totalUsers > 0
? analytics.metrics.revenue.reduce((sum, item) => sum + item.amount, 0) / analytics.summary.totalUsers
: 0, prefix: _jsx(DollarOutlined, {}), precision: 2, valueStyle: { color: '#1890ff' } }) }), _jsx(Col, { span: 8, children: _jsx(Statistic, { title: "Growth Rate", value: calculateGrowthRate(analytics.metrics.revenue), suffix: "%", precision: 1, prefix: _jsx(RiseOutlined, {}), valueStyle: {
color: getGrowthColor(calculateGrowthRate(analytics.metrics.revenue), 0)
} }) })] })] })), _jsxs(Card, { style: { marginTop: 16 }, children: [_jsx(Title, { level: 5, children: "Detailed Metrics" }), _jsx(Table, { columns: columns, dataSource: detailedMetrics, pagination: false, size: "small" })] }), _jsxs(Card, { style: { marginTop: 16 }, children: [_jsx(Title, { level: 5, children: "Period Summary" }), _jsxs(Row, { gutter: 16, children: [_jsxs(Col, { span: 12, children: [_jsx(Text, { strong: true, children: "Analysis Period: " }), _jsx(Tag, { color: "blue", children: getPeriodLabel(selectedPeriod) })] }), _jsxs(Col, { span: 12, children: [_jsx(Text, { strong: true, children: "Data Points: " }), _jsxs(Text, { children: [analytics.metrics.userGrowth.length, " records"] })] })] }), _jsxs(Row, { gutter: 16, style: { marginTop: 8 }, children: [_jsxs(Col, { span: 12, children: [_jsx(Text, { strong: true, children: "Last Updated: " }), _jsx(Text, { children: format(new Date(), 'PPP p') })] }), _jsxs(Col, { span: 12, children: [_jsx(Text, { strong: true, children: "Tenant ID: " }), _jsx(Text, { code: true, children: tenantId })] })] })] })] }));
};
//# sourceMappingURL=TenantAnalytics.js.map