erp-cicd/frontend/src/components/dashboard/DashboardStats.tsx
Ali af6fd7bcad
All checks were successful
Build & Deploy Frontend / build-push-deploy (push) Successful in 1m45s
added some
2025-08-30 11:57:49 +05:30

189 lines
7.5 KiB
TypeScript

import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import {
DollarSign,
TrendingUp,
Database,
AlertTriangle,
RefreshCw,
Activity
} from 'lucide-react';
import { useServiceStats } from '@/hooks/useServices';
import { formatCurrency } from '@/lib/currency';
export function DashboardStats() {
const { data: stats, isLoading } = useServiceStats();
if (isLoading) {
return (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
{[...Array(8)].map((_, i) => (
<Card key={i} className="animate-pulse">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<div className="h-4 bg-muted rounded w-24"></div>
<div className="h-4 w-4 bg-muted rounded"></div>
</CardHeader>
<CardContent>
<div className="h-8 bg-muted rounded w-20 mb-1"></div>
<div className="h-3 bg-muted rounded w-32"></div>
</CardContent>
</Card>
))}
</div>
);
}
return (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Monthly Spend</CardTitle>
<DollarSign className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{stats?.totalMonthlySpend?.byCurrency?.map((curr, index) => (
<div key={curr.currency}>
{formatCurrency(curr.amount, curr.currency as 'INR' | 'USD' | 'EUR')}
{index < (stats.totalMonthlySpend?.byCurrency?.length || 0) - 1 && <span className="text-lg"> + </span>}
</div>
)) || formatCurrency(0, 'INR')}
</div>
<p className="text-xs text-muted-foreground">
Current month active subscriptions
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Projected Yearly</CardTitle>
<TrendingUp className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{stats?.projectedYearlySpend?.byCurrency?.map((curr, index) => (
<div key={curr.currency}>
{formatCurrency(curr.amount, curr.currency as 'INR' | 'USD' | 'EUR')}
{index < (stats.projectedYearlySpend?.byCurrency?.length || 0) - 1 && <span className="text-lg"> + </span>}
</div>
)) || formatCurrency(0, 'INR')}
</div>
<p className="text-xs text-muted-foreground">
Based on current subscriptions
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Spent</CardTitle>
<DollarSign className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{stats?.totalSpent?.byCurrency?.map((curr, index) => (
<div key={curr.currency}>
{formatCurrency(curr.amount, curr.currency as 'INR' | 'USD' | 'EUR')}
{index < (stats.totalSpent?.byCurrency?.length || 0) - 1 && <span className="text-lg"> + </span>}
</div>
)) || formatCurrency(0, 'INR')}
</div>
<p className="text-xs text-muted-foreground">
All time actual payments
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">This Year Spent</CardTitle>
<TrendingUp className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{stats?.yearlySpent?.byCurrency?.map((curr, index) => (
<div key={curr.currency}>
{formatCurrency(curr.amount, curr.currency as 'INR' | 'USD' | 'EUR')}
{index < (stats.yearlySpent?.byCurrency?.length || 0) - 1 && <span className="text-lg"> + </span>}
</div>
)) || formatCurrency(0, 'INR')}
</div>
<p className="text-xs text-muted-foreground">
{new Date().getFullYear()} actual payments
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Active Services</CardTitle>
<Database className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-emerald-600">
{stats?.activeServices || 0}
</div>
<p className="text-xs text-muted-foreground">
of {stats?.totalServices || 0} total services
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Expired Services</CardTitle>
<AlertTriangle className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-destructive">
{stats?.expiredServices || 0}
</div>
<p className="text-xs text-muted-foreground">
Need immediate attention
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Auto Renewal</CardTitle>
<RefreshCw className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-primary">
{stats?.autoRenewServices || 0}
</div>
<p className="text-xs text-muted-foreground">
Services with auto-renewal enabled
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Health Score</CardTitle>
<Activity className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="flex items-center space-x-2">
<div className="text-2xl font-bold">
{stats?.totalServices ? Math.round((stats.activeServices / stats.totalServices) * 100) : 0}%
</div>
<Badge variant={
stats?.totalServices && (stats.activeServices / stats.totalServices) > 0.8 ? "default" :
stats?.totalServices && (stats.activeServices / stats.totalServices) > 0.6 ? "secondary" : "destructive"
}>
{stats?.totalServices && (stats.activeServices / stats.totalServices) > 0.8 ? "Excellent" :
stats?.totalServices && (stats.activeServices / stats.totalServices) > 0.6 ? "Good" : "Needs Attention"}
</Badge>
</div>
<p className="text-xs text-muted-foreground">
Active vs total services ratio
</p>
</CardContent>
</Card>
</div>
);
}