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