149 lines
5.7 KiB
TypeScript
149 lines
5.7 KiB
TypeScript
"use client";
|
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { CategoryIcon } from "@/components/ui/category-icon";
|
|
import type { BankingData } from "@/lib/types";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface RecentTransactionsProps {
|
|
data: BankingData;
|
|
}
|
|
|
|
export function RecentTransactions({ data }: RecentTransactionsProps) {
|
|
const recentTransactions = [...data.transactions]
|
|
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
|
|
.slice(0, 10);
|
|
|
|
const formatCurrency = (amount: number) => {
|
|
return new Intl.NumberFormat("fr-FR", {
|
|
style: "currency",
|
|
currency: "EUR",
|
|
}).format(amount);
|
|
};
|
|
|
|
const formatDate = (dateStr: string) => {
|
|
return new Date(dateStr).toLocaleDateString("fr-FR", {
|
|
day: "2-digit",
|
|
month: "short",
|
|
});
|
|
};
|
|
|
|
const getCategory = (categoryId: string | null) => {
|
|
if (!categoryId) return null;
|
|
return data.categories.find((c) => c.id === categoryId);
|
|
};
|
|
|
|
const getAccount = (accountId: string) => {
|
|
return data.accounts.find((a) => a.id === accountId);
|
|
};
|
|
|
|
if (recentTransactions.length === 0) {
|
|
return (
|
|
<Card className="card-hover">
|
|
<CardHeader className="pb-4">
|
|
<CardTitle className="text-base md:text-lg font-bold">
|
|
Transactions récentes
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex flex-col items-center justify-center py-12 text-center">
|
|
<p className="text-muted-foreground font-medium">
|
|
Aucune transaction
|
|
</p>
|
|
<p className="text-sm text-muted-foreground/70 mt-2">
|
|
Importez un fichier OFX pour commencer
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Card className="card-hover">
|
|
<CardHeader className="pb-5">
|
|
<CardTitle className="text-lg md:text-xl font-black tracking-tight">
|
|
Transactions récentes
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="px-5 md:px-6">
|
|
<div className="space-y-3">
|
|
{recentTransactions.map((transaction) => {
|
|
const category = getCategory(transaction.categoryId);
|
|
const account = getAccount(transaction.accountId);
|
|
|
|
return (
|
|
<div
|
|
key={transaction.id}
|
|
className="group rounded-2xl bg-gradient-to-r from-muted/50 via-muted/30 to-muted/20 hover:from-muted/70 hover:via-muted/50 hover:to-muted/40 border-2 border-border/40 hover:border-primary/30 transition-all duration-300 overflow-hidden hover:shadow-lg hover:shadow-primary/10 hover:scale-[1.02] backdrop-blur-sm"
|
|
>
|
|
<div className="flex items-start gap-4 md:gap-5 p-4 md:p-5">
|
|
<div className="flex-1 min-w-0 overflow-hidden">
|
|
<div className="flex items-start justify-between gap-2">
|
|
<p className="font-bold text-sm md:text-base truncate flex-1 leading-tight">
|
|
{transaction.description}
|
|
</p>
|
|
<div
|
|
className={cn(
|
|
"font-black tabular-nums text-sm md:text-base shrink-0 md:hidden",
|
|
transaction.amount >= 0
|
|
? "text-emerald-600 dark:text-emerald-400"
|
|
: "text-red-600 dark:text-red-400",
|
|
)}
|
|
>
|
|
{transaction.amount >= 0 ? "+" : ""}
|
|
{formatCurrency(transaction.amount)}
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-3 md:gap-4 mt-3 flex-wrap">
|
|
<span className="text-sm text-muted-foreground/70 font-semibold whitespace-nowrap">
|
|
{formatDate(transaction.date)}
|
|
</span>
|
|
{account && (
|
|
<span className="text-sm text-muted-foreground/60 truncate font-medium">
|
|
• {account.name}
|
|
</span>
|
|
)}
|
|
{category && (
|
|
<Badge
|
|
variant="secondary"
|
|
className="text-xs gap-2 shrink-0 rounded-xl px-3 py-1 font-bold border-2 backdrop-blur-sm"
|
|
style={{
|
|
backgroundColor: `${category.color}20`,
|
|
color: category.color,
|
|
borderColor: `${category.color}40`,
|
|
}}
|
|
>
|
|
<CategoryIcon
|
|
icon={category.icon}
|
|
color={category.color}
|
|
size={14}
|
|
/>
|
|
<span className="truncate">{category.name}</span>
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className={cn(
|
|
"font-black tabular-nums text-base md:text-lg shrink-0 hidden md:block leading-tight",
|
|
transaction.amount >= 0
|
|
? "text-emerald-600 dark:text-emerald-400"
|
|
: "text-red-600 dark:text-red-400",
|
|
)}
|
|
>
|
|
{transaction.amount >= 0 ? "+" : ""}
|
|
{formatCurrency(transaction.amount)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|