Files
fintrack/components/dashboard/overview-cards.tsx

145 lines
5.6 KiB
TypeScript

"use client";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { TrendingUp, TrendingDown, Wallet, CreditCard } from "lucide-react";
import type { BankingData } from "@/lib/types";
import { getAccountBalance } from "@/lib/account-utils";
import { cn } from "@/lib/utils";
interface OverviewCardsProps {
data: BankingData;
}
export function OverviewCards({ data }: OverviewCardsProps) {
const totalBalance = data.accounts.reduce(
(sum, acc) => sum + getAccountBalance(acc),
0
);
const thisMonth = new Date();
thisMonth.setDate(1);
const thisMonthStr = thisMonth.toISOString().slice(0, 7);
const monthTransactions = data.transactions.filter((t) =>
t.date.startsWith(thisMonthStr)
);
const income = monthTransactions
.filter((t) => t.amount > 0)
.reduce((sum, t) => sum + t.amount, 0);
const expenses = monthTransactions
.filter((t) => t.amount < 0)
.reduce((sum, t) => sum + Math.abs(t.amount), 0);
const reconciled = data.transactions.filter((t) => t.isReconciled).length;
const total = data.transactions.length;
const reconciledPercent =
total > 0 ? Math.round((reconciled / total) * 100) : 0;
const formatCurrency = (amount: number) => {
return new Intl.NumberFormat("fr-FR", {
style: "currency",
currency: "EUR",
}).format(amount);
};
return (
<div className="grid gap-3 sm:gap-4 grid-cols-2 lg:grid-cols-4">
<Card
className={cn(
"border shadow-sm bg-gradient-to-br",
totalBalance >= 0
? "from-emerald-50/50 to-card dark:from-emerald-950/20 dark:to-card"
: "from-red-50/50 to-card dark:from-red-950/20 dark:to-card"
)}
>
<CardHeader className="flex flex-row items-start justify-between space-y-0 pb-2 px-4 pt-4 sm:px-6 sm:pt-6">
<CardTitle className="text-sm font-medium text-muted-foreground leading-tight">
Solde Total
</CardTitle>
<div className="rounded-full bg-muted/50 p-2 shrink-0">
<Wallet className="h-4 w-4 text-muted-foreground" />
</div>
</CardHeader>
<CardContent className="px-4 pb-4 sm:px-6 sm:pb-6 pt-0">
<div
className={cn(
"text-2xl sm:text-2xl md:text-3xl font-bold tracking-tight",
totalBalance >= 0 ? "text-emerald-600" : "text-red-600"
)}
>
{formatCurrency(totalBalance)}
</div>
<p className="text-xs text-muted-foreground mt-2">
{data.accounts.length} compte{data.accounts.length > 1 ? "s" : ""}
</p>
</CardContent>
</Card>
<Card className="border shadow-sm bg-gradient-to-br from-emerald-50/50 to-card dark:from-emerald-950/20 dark:to-card">
<CardHeader className="flex flex-row items-start justify-between space-y-0 pb-2 px-4 pt-4 sm:px-6 sm:pt-6">
<CardTitle className="text-sm font-medium text-muted-foreground leading-tight">
Revenus du mois
</CardTitle>
<div className="rounded-full bg-emerald-500/10 p-2 shrink-0">
<TrendingUp className="h-4 w-4 text-emerald-600" />
</div>
</CardHeader>
<CardContent className="px-4 pb-4 sm:px-6 sm:pb-6 pt-0">
<div className="text-2xl sm:text-2xl md:text-3xl font-bold tracking-tight text-emerald-600">
{formatCurrency(income)}
</div>
<p className="text-xs text-muted-foreground mt-2">
{monthTransactions.filter((t) => t.amount > 0).length} opération
{monthTransactions.filter((t) => t.amount > 0).length > 1
? "s"
: ""}
</p>
</CardContent>
</Card>
<Card className="border shadow-sm bg-gradient-to-br from-red-50/50 to-card dark:from-red-950/20 dark:to-card">
<CardHeader className="flex flex-row items-start justify-between space-y-0 pb-2 px-4 pt-4 sm:px-6 sm:pt-6">
<CardTitle className="text-sm font-medium text-muted-foreground leading-tight">
Dépenses du mois
</CardTitle>
<div className="rounded-full bg-red-500/10 p-2 shrink-0">
<TrendingDown className="h-4 w-4 text-red-600" />
</div>
</CardHeader>
<CardContent className="px-4 pb-4 sm:px-6 sm:pb-6 pt-0">
<div className="text-2xl sm:text-2xl md:text-3xl font-bold tracking-tight text-red-600">
{formatCurrency(expenses)}
</div>
<p className="text-xs text-muted-foreground mt-2">
{monthTransactions.filter((t) => t.amount < 0).length} opération
{monthTransactions.filter((t) => t.amount < 0).length > 1
? "s"
: ""}
</p>
</CardContent>
</Card>
<Card className="border shadow-sm bg-gradient-to-br from-blue-50/50 to-card dark:from-blue-950/20 dark:to-card">
<CardHeader className="flex flex-row items-start justify-between space-y-0 pb-2 px-4 pt-4 sm:px-6 sm:pt-6">
<CardTitle className="text-sm font-medium text-muted-foreground leading-tight">
Pointage
</CardTitle>
<div className="rounded-full bg-muted/50 p-2 shrink-0">
<CreditCard className="h-4 w-4 text-muted-foreground" />
</div>
</CardHeader>
<CardContent className="px-4 pb-4 sm:px-6 sm:pb-6 pt-0">
<div className="text-2xl sm:text-2xl md:text-3xl font-bold tracking-tight">
{reconciledPercent}%
</div>
<p className="text-xs text-muted-foreground mt-2">
{reconciled} / {total} opérations pointées
</p>
</CardContent>
</Card>
</div>
);
}