whisper-money/resources/js/components/cashflow/savings-rate-card.tsx

100 lines
3.5 KiB
TypeScript

import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { CashflowSummary } from '@/hooks/use-cashflow-data';
import { cn } from '@/lib/utils';
import { __ } from '@/utils/i18n';
import { TrendingDown, TrendingUp } from 'lucide-react';
interface SavingsRateCardProps {
current: CashflowSummary;
previous: CashflowSummary;
loading?: boolean;
}
export function SavingsRateCard({
current,
previous,
loading,
}: SavingsRateCardProps) {
if (loading) {
return (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium">
{__('Savings Rate')}
</CardTitle>
</CardHeader>
<CardContent>
<div className="h-12 w-24 animate-pulse rounded bg-gray-200 dark:bg-gray-700" />
</CardContent>
</Card>
);
}
const diff = current.savings_rate - previous.savings_rate;
const isPositive = diff >= 0;
const hasPreviousData = previous.income > 0;
// Determine color based on savings rate
const rateColor =
current.savings_rate >= 20
? 'text-green-600 dark:text-green-400'
: current.savings_rate >= 10
? 'text-yellow-600 dark:text-yellow-400'
: current.savings_rate >= 0
? 'text-orange-600 dark:text-orange-400'
: 'text-red-600 dark:text-red-400';
return (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium">
{__('Savings Rate')}
</CardTitle>
<CardDescription>
{__('Percentage of income saved')}
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex items-baseline gap-2">
<span
className={cn(
'text-3xl font-bold tabular-nums',
rateColor,
)}
>
{current.savings_rate.toFixed(1)}%
</span>
{hasPreviousData && (
<div className={cn('flex items-center gap-1 text-sm')}>
{isPositive ? (
<TrendingUp className="size-4 text-green-600 dark:text-green-400" />
) : (
<TrendingDown className="size-4 text-red-600 dark:text-red-400" />
)}
<span>
{isPositive ? '+' : ''}
{diff.toFixed(1)}%
</span>
</div>
)}
</div>
<p className="mt-2 text-xs text-muted-foreground">
{current.savings_rate >= 20
? __("Great job! You're saving well.")
: current.savings_rate >= 10
? __('Good progress on your savings.')
: current.savings_rate >= 0
? __('Consider saving more if possible.')
: __('Spending exceeds income this period.')}
</p>
</CardContent>
</Card>
);
}