Compare commits
3 Commits
c4707e5511
...
2452e30a0f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2452e30a0f | ||
|
|
b3ae6059ca | ||
|
|
6f78dca1f0 |
@@ -351,7 +351,7 @@ export default function CategoriesPage() {
|
||||
onToggleAll={allExpanded ? collapseAll : expandAll}
|
||||
/>
|
||||
|
||||
<div className="space-y-1">
|
||||
<div className="space-y-3 md:space-y-4">
|
||||
{filteredParentCategories.map((parent: Category) => {
|
||||
const allChildren = childrenByParent[parent.id] || [];
|
||||
const children = searchQuery.trim()
|
||||
@@ -387,8 +387,8 @@ export default function CategoriesPage() {
|
||||
})}
|
||||
|
||||
{orphanCategories.length > 0 && (
|
||||
<div className="border rounded-lg bg-card">
|
||||
<div className="px-3 py-2 border-b">
|
||||
<Card className="card-hover">
|
||||
<div className="px-3 py-2 border-b border-border">
|
||||
<span className="text-sm font-medium text-muted-foreground">
|
||||
Catégories non classées ({orphanCategories.length})
|
||||
</span>
|
||||
@@ -405,7 +405,7 @@ export default function CategoriesPage() {
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
--foreground: oklch(0.1 0.015 280);
|
||||
|
||||
/* Cards avec glassmorphism très prononcé */
|
||||
--card: oklch(1 0 0 / 0.6);
|
||||
--card: oklch(1 0 0 / 0.5);
|
||||
--card-foreground: oklch(0.1 0.015 280);
|
||||
--card-hover: oklch(1 0 0 / 0.75);
|
||||
--card-hover: oklch(1 0 0 / 0.65);
|
||||
|
||||
/* Popover avec backdrop blur très fort */
|
||||
--popover: oklch(1 0 0 / 0.95);
|
||||
@@ -82,9 +82,9 @@
|
||||
--foreground: oklch(0.97 0.005 280);
|
||||
|
||||
/* Cards avec effet glassmorphism sombre très prononcé */
|
||||
--card: oklch(0.15 0.015 280 / 0.6);
|
||||
--card: oklch(0.15 0.015 280 / 0.5);
|
||||
--card-foreground: oklch(0.97 0.005 280);
|
||||
--card-hover: oklch(0.18 0.015 280 / 0.75);
|
||||
--card-hover: oklch(0.18 0.015 280 / 0.65);
|
||||
|
||||
/* Popover avec backdrop blur très fort */
|
||||
--popover: oklch(0.15 0.015 280 / 0.95);
|
||||
@@ -459,18 +459,19 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Fintech card styles avec design moderne et élégant */
|
||||
/* Fintech card styles avec effet glassmorphism prononcé */
|
||||
.fintech-card {
|
||||
background: var(--card);
|
||||
backdrop-filter: blur(40px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(40px) saturate(180%);
|
||||
border: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
|
||||
background: color-mix(in srgb, var(--card) 70%, transparent);
|
||||
backdrop-filter: blur(60px) saturate(200%) brightness(1.05);
|
||||
-webkit-backdrop-filter: blur(60px) saturate(200%) brightness(1.05);
|
||||
border: none;
|
||||
border-radius: calc(var(--radius) + 0.25rem);
|
||||
box-shadow:
|
||||
0 1px 3px 0 color-mix(in srgb, var(--foreground) 4%, transparent),
|
||||
0 4px 12px -2px color-mix(in srgb, var(--primary) 6%, transparent),
|
||||
0 8px 24px -4px color-mix(in srgb, var(--foreground) 2%, transparent),
|
||||
inset 0 1px 0 0 color-mix(in srgb, white 30%, transparent);
|
||||
0 2px 8px 0 color-mix(in srgb, var(--foreground) 6%, transparent),
|
||||
0 8px 24px -4px color-mix(in srgb, var(--primary) 8%, transparent),
|
||||
0 16px 48px -8px color-mix(in srgb, var(--foreground) 3%, transparent),
|
||||
inset 0 1px 0 0 color-mix(in srgb, white 40%, transparent),
|
||||
inset 0 -1px 0 0 color-mix(in srgb, var(--foreground) 2%, transparent);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -494,13 +495,47 @@
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
/* Texture plus prononcée pour les cards de statistiques */
|
||||
.stat-card-textured::before {
|
||||
background-image:
|
||||
radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--foreground) 3%, transparent) 1px, transparent 0),
|
||||
radial-gradient(circle at 11px 11px, color-mix(in srgb, var(--foreground) 1.5%, transparent) 1px, transparent 0);
|
||||
background-size: 16px 16px, 24px 24px;
|
||||
opacity: 0.2;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dark .stat-card-textured::before {
|
||||
background-image:
|
||||
radial-gradient(circle at 1px 1px, color-mix(in srgb, white 4%, transparent) 1px, transparent 0),
|
||||
radial-gradient(circle at 11px 11px, color-mix(in srgb, white 2%, transparent) 1px, transparent 0);
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
/* Effet glass renforcé pour les cards de contenu */
|
||||
.fintech-card.card-hover {
|
||||
background: color-mix(in srgb, var(--card) 60%, transparent);
|
||||
backdrop-filter: blur(80px) saturate(220%) brightness(1.08);
|
||||
-webkit-backdrop-filter: blur(80px) saturate(220%) brightness(1.08);
|
||||
}
|
||||
|
||||
.dark .fintech-card.card-hover {
|
||||
background: color-mix(in srgb, var(--card) 55%, transparent);
|
||||
backdrop-filter: blur(80px) saturate(220%) brightness(0.92);
|
||||
-webkit-backdrop-filter: blur(80px) saturate(220%) brightness(0.92);
|
||||
}
|
||||
|
||||
.dark .fintech-card {
|
||||
border-color: color-mix(in srgb, var(--border) 40%, transparent);
|
||||
background: color-mix(in srgb, var(--card) 65%, transparent);
|
||||
backdrop-filter: blur(60px) saturate(200%) brightness(0.95);
|
||||
-webkit-backdrop-filter: blur(60px) saturate(200%) brightness(0.95);
|
||||
border: none;
|
||||
box-shadow:
|
||||
0 1px 3px 0 color-mix(in srgb, var(--foreground) 8%, transparent),
|
||||
0 4px 12px -2px color-mix(in srgb, var(--primary) 8%, transparent),
|
||||
0 8px 24px -4px color-mix(in srgb, black 40%, transparent),
|
||||
inset 0 1px 0 0 color-mix(in srgb, white 8%, transparent);
|
||||
0 2px 8px 0 color-mix(in srgb, var(--foreground) 12%, transparent),
|
||||
0 8px 24px -4px color-mix(in srgb, var(--primary) 12%, transparent),
|
||||
0 16px 48px -8px color-mix(in srgb, black 50%, transparent),
|
||||
inset 0 1px 0 0 color-mix(in srgb, white 12%, transparent),
|
||||
inset 0 -1px 0 0 color-mix(in srgb, var(--foreground) 4%, transparent);
|
||||
}
|
||||
|
||||
.fintech-card::after {
|
||||
@@ -509,19 +544,29 @@
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
height: 2px;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
color-mix(in srgb, white 50%, transparent) 50%,
|
||||
color-mix(in srgb, white 60%, transparent) 20%,
|
||||
color-mix(in srgb, white 80%, transparent) 50%,
|
||||
color-mix(in srgb, white 60%, transparent) 80%,
|
||||
transparent 100%
|
||||
);
|
||||
opacity: 0.6;
|
||||
opacity: 0.7;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.dark .fintech-card::after {
|
||||
opacity: 0.2;
|
||||
opacity: 0.25;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
color-mix(in srgb, white 30%, transparent) 20%,
|
||||
color-mix(in srgb, white 40%, transparent) 50%,
|
||||
color-mix(in srgb, white 30%, transparent) 80%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
/* Gradient backgrounds for stat cards - design moderne et subtil */
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function LoginPage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-[var(--background)] p-4 page-background">
|
||||
<Card className="w-full max-w-md page-content">
|
||||
<Card className="w-full max-w-md page-content card-hover">
|
||||
<CardHeader className="space-y-1">
|
||||
<div className="flex items-center justify-center mb-4">
|
||||
<Lock className="w-12 h-12 text-[var(--primary)]" />
|
||||
|
||||
22
app/page.tsx
22
app/page.tsx
@@ -60,18 +60,16 @@ export default function DashboardPage() {
|
||||
}
|
||||
/>
|
||||
|
||||
<Card className="mb-6 card-hover">
|
||||
<CardContent className="px-5 py-5 sm:px-6 sm:py-6">
|
||||
<AccountFilterCombobox
|
||||
accounts={data.accounts}
|
||||
folders={data.folders}
|
||||
value={selectedAccounts}
|
||||
onChange={setSelectedAccounts}
|
||||
className="w-full md:w-[320px]"
|
||||
filteredTransactions={data.transactions}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="mb-6">
|
||||
<AccountFilterCombobox
|
||||
accounts={data.accounts}
|
||||
folders={data.folders}
|
||||
value={selectedAccounts}
|
||||
onChange={setSelectedAccounts}
|
||||
className="w-full md:w-[320px]"
|
||||
filteredTransactions={data.transactions}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<OverviewCards data={filteredData} />
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ export default function TransactionsPage() {
|
||||
/>
|
||||
|
||||
{(!isLoadingChart || !isLoadingTransactions) && (
|
||||
<Card className="mb-6">
|
||||
<Card className="mb-6 card-hover">
|
||||
<Collapsible open={isStatsExpanded} onOpenChange={setIsStatsExpanded}>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 py-3 px-6">
|
||||
<CardTitle className="text-base font-semibold">
|
||||
@@ -272,7 +272,7 @@ export default function TransactionsPage() {
|
||||
{/* Summary cards */}
|
||||
{!isLoadingTransactions && (
|
||||
<div className="grid gap-4 grid-cols-1 sm:grid-cols-2 mb-6">
|
||||
<Card>
|
||||
<Card className="card-hover">
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
@@ -287,7 +287,7 @@ export default function TransactionsPage() {
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<Card className="card-hover">
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { CategoryIcon } from "@/components/ui/category-icon";
|
||||
import {
|
||||
Plus,
|
||||
@@ -54,7 +55,7 @@ export function ParentCategoryRow({
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<div className="border rounded-lg bg-card">
|
||||
<Card className="card-hover">
|
||||
<Collapsible open={isExpanded} onOpenChange={onToggleExpanded}>
|
||||
<div className="flex items-center justify-between px-2 md:px-3 py-1.5 md:py-2">
|
||||
<CollapsibleTrigger asChild>
|
||||
@@ -155,6 +156,6 @@ export function ParentCategoryRow({
|
||||
)}
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -61,13 +61,13 @@ export function RecentTransactions({ data }: RecentTransactionsProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="card-hover">
|
||||
<CardHeader className="pb-5">
|
||||
<Card className="card-hover relative overflow-hidden">
|
||||
<CardHeader className="pb-5 relative z-10">
|
||||
<CardTitle className="text-lg md:text-xl font-black tracking-tight">
|
||||
Transactions récentes
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="px-5 md:px-6">
|
||||
<CardContent className="px-5 md:px-6 relative z-10">
|
||||
<div className="space-y-3">
|
||||
{recentTransactions.map((transaction) => {
|
||||
const category = getCategory(transaction.categoryId);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ChevronDown, ChevronRight, Plus, Tag } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { CategoryCombobox } from "@/components/ui/category-combobox";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { useIsMobile } from "@/hooks/use-mobile";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { Transaction, Category } from "@/lib/types";
|
||||
@@ -54,7 +55,7 @@ export function RuleGroupCard({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border border-border rounded-lg bg-card overflow-hidden">
|
||||
<Card className="card-hover overflow-hidden">
|
||||
{/* Header */}
|
||||
<div
|
||||
className="flex flex-col md:flex-row md:items-center gap-2 md:gap-3 p-3 md:p-4 cursor-pointer hover:bg-accent/5"
|
||||
@@ -247,6 +248,6 @@ export function RuleGroupCard({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export function BalanceLineChart({
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card className="card-hover">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle>Évolution du solde</CardTitle>
|
||||
<div className="flex gap-1">
|
||||
|
||||
@@ -73,7 +73,7 @@ export function CategoryBarChart({
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card className="card-hover">
|
||||
<CardHeader>
|
||||
<CardTitle>{title}</CardTitle>
|
||||
</CardHeader>
|
||||
@@ -154,3 +154,5 @@ export function CategoryBarChart({
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
// Find the Card component in this file
|
||||
|
||||
@@ -51,7 +51,7 @@ export function CategoryPieChart({
|
||||
const currentData = isExpanded ? baseData : baseData.slice(0, maxItems);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card className="card-hover">
|
||||
<CardHeader className="flex flex-col md:flex-row md:items-center md:justify-between space-y-2 md:space-y-0 pb-2">
|
||||
<CardTitle className="text-sm md:text-base">{title}</CardTitle>
|
||||
<div className="flex flex-col md:flex-row gap-2 w-full md:w-auto">
|
||||
|
||||
@@ -96,7 +96,7 @@ export function CategoryTrendChart({
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card className="card-hover">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle>Évolution des dépenses par catégorie</CardTitle>
|
||||
<div className="flex gap-2">
|
||||
|
||||
@@ -28,7 +28,7 @@ export function IncomeExpenseTrendChart({
|
||||
formatCurrency,
|
||||
}: IncomeExpenseTrendChartProps) {
|
||||
return (
|
||||
<Card>
|
||||
<Card className="card-hover">
|
||||
<CardHeader>
|
||||
<CardTitle>Tendances revenus et dépenses</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
@@ -44,20 +44,35 @@ export function MonthlyChart({
|
||||
}: MonthlyChartProps) {
|
||||
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
||||
|
||||
// Calculer l'intervalle dynamiquement selon le nombre de données
|
||||
const getXAxisInterval = () => {
|
||||
if (data.length <= 6) return 0; // Afficher tous les labels pour 6 mois ou moins
|
||||
if (data.length <= 12) return 1; // Afficher un label sur deux pour 7-12 mois
|
||||
return Math.floor(data.length / 12); // Pour plus de 12 mois, afficher environ 12 labels
|
||||
};
|
||||
|
||||
// Formater les labels de manière plus compacte
|
||||
const formatMonthLabel = (month: string) => {
|
||||
// Format: "janv. 24" -> "janv 24" (enlever le point)
|
||||
return month.replace('.', '');
|
||||
};
|
||||
|
||||
const chartContent = (
|
||||
<>
|
||||
{data.length > 0 ? (
|
||||
<div className="h-[400px] sm:h-[300px]">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart data={data} margin={{ left: 0, right: 10, top: 10, bottom: 5 }}>
|
||||
<LineChart data={data} margin={{ left: 0, right: 10, top: 10, bottom: data.length > 6 ? 80 : 60 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
|
||||
<XAxis
|
||||
dataKey="month"
|
||||
className="text-xs"
|
||||
angle={-45}
|
||||
textAnchor="end"
|
||||
height={60}
|
||||
interval={0}
|
||||
angle={data.length > 6 ? -45 : 0}
|
||||
textAnchor={data.length > 6 ? "end" : "middle"}
|
||||
height={data.length > 6 ? 80 : 60}
|
||||
interval={getXAxisInterval()}
|
||||
tickFormatter={formatMonthLabel}
|
||||
tick={{ fontSize: 11 }}
|
||||
/>
|
||||
<YAxis
|
||||
className="text-xs"
|
||||
@@ -112,7 +127,7 @@ export function MonthlyChart({
|
||||
|
||||
if (!collapsible) {
|
||||
return (
|
||||
<Card>
|
||||
<Card className="card-hover">
|
||||
<CardHeader>
|
||||
<CardTitle>Revenus vs Dépenses par mois</CardTitle>
|
||||
</CardHeader>
|
||||
@@ -122,7 +137,7 @@ export function MonthlyChart({
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card className="card-hover">
|
||||
<Collapsible open={isExpanded} onOpenChange={setIsExpanded}>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 py-3 px-6">
|
||||
<CardTitle className="text-base font-semibold">
|
||||
|
||||
@@ -31,7 +31,7 @@ export function SavingsTrendChart({
|
||||
const isPositive = latestSavings >= 0;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card className="card-hover">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle>Évolution des économies</CardTitle>
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -21,7 +21,7 @@ export function StatsSummaryCards({
|
||||
|
||||
return (
|
||||
<div className="grid gap-3 md:gap-4 grid-cols-2 md:grid-cols-4">
|
||||
<Card className="relative overflow-hidden">
|
||||
<Card className="stat-card-textured relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<TrendingUp className="h-16 w-16 md:h-20 md:w-20 text-emerald-600 dark:text-emerald-400" strokeWidth={1} />
|
||||
@@ -38,7 +38,7 @@ export function StatsSummaryCards({
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="relative overflow-hidden">
|
||||
<Card className="stat-card-textured relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<TrendingDown className="h-16 w-16 md:h-20 md:w-20 text-red-600 dark:text-red-400" strokeWidth={1} />
|
||||
@@ -55,7 +55,7 @@ export function StatsSummaryCards({
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="relative overflow-hidden">
|
||||
<Card className="stat-card-textured relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<ArrowRight className="h-16 w-16 md:h-20 md:w-20 text-muted-foreground/40" strokeWidth={1} />
|
||||
@@ -72,7 +72,7 @@ export function StatsSummaryCards({
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="relative overflow-hidden">
|
||||
<Card className="stat-card-textured relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<div className={cn(
|
||||
|
||||
@@ -21,7 +21,7 @@ export function TopExpensesList({
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card className="card-hover">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-sm md:text-base">Top 5 dépenses</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
@@ -33,7 +33,7 @@ export function YearOverYearChart({
|
||||
previousYearLabel = "Année précédente",
|
||||
}: YearOverYearChartProps) {
|
||||
return (
|
||||
<Card>
|
||||
<Card className="card-hover">
|
||||
<CardHeader>
|
||||
<CardTitle>Comparaison année sur année</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
@@ -139,19 +139,29 @@ export function useTransactionsChartData({
|
||||
monthlyMap.set(monthKey, current);
|
||||
});
|
||||
|
||||
const monthlyChartData: MonthlyChartData[] = Array.from(
|
||||
monthlyMap.entries()
|
||||
)
|
||||
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||
.map(([month, values]) => ({
|
||||
month: new Date(month + "-01").toLocaleDateString("fr-FR", {
|
||||
// Format months with year: use short format for better readability
|
||||
const sortedMonths = Array.from(monthlyMap.entries()).sort((a, b) =>
|
||||
a[0].localeCompare(b[0])
|
||||
);
|
||||
|
||||
const monthlyChartData: MonthlyChartData[] = sortedMonths.map(
|
||||
([monthKey, values]) => {
|
||||
const date = new Date(monthKey + "-01");
|
||||
|
||||
// Format: "janv. 24" instead of "janv. 2024" for compactness
|
||||
const monthLabel = date.toLocaleDateString("fr-FR", {
|
||||
month: "short",
|
||||
year: "numeric",
|
||||
}),
|
||||
revenus: values.income,
|
||||
depenses: values.expenses,
|
||||
solde: values.income - values.expenses,
|
||||
}));
|
||||
});
|
||||
const yearShort = date.getFullYear().toString().slice(-2);
|
||||
|
||||
return {
|
||||
month: `${monthLabel} ${yearShort}`,
|
||||
revenus: values.income,
|
||||
depenses: values.expenses,
|
||||
solde: values.income - values.expenses,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
return monthlyChartData;
|
||||
}, [transactionsData]);
|
||||
|
||||
Reference in New Issue
Block a user