mirror of
https://github.com/elisiariocouto/leggen.git
synced 2025-12-14 12:02:19 +00:00
Fix MonthlyTrends dynamic title, remove Period Summary, convert BalanceChart to stacked area chart
Co-authored-by: elisiariocouto <818914+elisiariocouto@users.noreply.github.com>
This commit is contained in:
committed by
Elisiário Couto
parent
c8f0a103c6
commit
d51aa9429e
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
LineChart,
|
AreaChart,
|
||||||
Line,
|
Area,
|
||||||
XAxis,
|
XAxis,
|
||||||
YAxis,
|
YAxis,
|
||||||
CartesianGrid,
|
CartesianGrid,
|
||||||
@@ -111,7 +111,7 @@ export default function BalanceChart({ data, accounts, className }: BalanceChart
|
|||||||
</h3>
|
</h3>
|
||||||
<div className="h-80">
|
<div className="h-80">
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<LineChart data={finalData}>
|
<AreaChart data={finalData}>
|
||||||
<CartesianGrid strokeDasharray="3 3" />
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
<XAxis
|
<XAxis
|
||||||
dataKey="date"
|
dataKey="date"
|
||||||
@@ -131,25 +131,25 @@ export default function BalanceChart({ data, accounts, className }: BalanceChart
|
|||||||
tickFormatter={(value) => `€${value.toLocaleString()}`}
|
tickFormatter={(value) => `€${value.toLocaleString()}`}
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
formatter={(value: number) => [
|
formatter={(value: number, name: string) => [
|
||||||
`€${value.toLocaleString()}`,
|
`€${value.toLocaleString()}`,
|
||||||
"Balance",
|
getAccountDisplayName(name),
|
||||||
]}
|
]}
|
||||||
labelFormatter={(label) => `Date: ${label}`}
|
labelFormatter={(label) => `Date: ${label}`}
|
||||||
/>
|
/>
|
||||||
<Legend />
|
<Legend />
|
||||||
{Object.keys(accountBalances).map((accountId, index) => (
|
{Object.keys(accountBalances).map((accountId, index) => (
|
||||||
<Line
|
<Area
|
||||||
key={accountId}
|
key={accountId}
|
||||||
type="monotone"
|
type="monotone"
|
||||||
dataKey={accountId}
|
dataKey={accountId}
|
||||||
|
stackId="1"
|
||||||
|
fill={colors[index % colors.length]}
|
||||||
stroke={colors[index % colors.length]}
|
stroke={colors[index % colors.length]}
|
||||||
strokeWidth={2}
|
|
||||||
dot={{ r: 4 }}
|
|
||||||
name={getAccountDisplayName(accountId)}
|
name={getAccountDisplayName(accountId)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</LineChart>
|
</AreaChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export default function MonthlyTrends({ className, days = 365 }: MonthlyTrendsPr
|
|||||||
|
|
||||||
if (!monthlyMap[monthKey]) {
|
if (!monthlyMap[monthKey]) {
|
||||||
monthlyMap[monthKey] = {
|
monthlyMap[monthKey] = {
|
||||||
month: date.toLocaleDateString(undefined, {
|
month: date.toLocaleDateString('en-GB', {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'short'
|
month: 'short'
|
||||||
}),
|
}),
|
||||||
@@ -77,10 +77,20 @@ export default function MonthlyTrends({ className, days = 365 }: MonthlyTrendsPr
|
|||||||
...Object.entries(monthlyMap)
|
...Object.entries(monthlyMap)
|
||||||
.sort(([a], [b]) => a.localeCompare(b))
|
.sort(([a], [b]) => a.localeCompare(b))
|
||||||
.map(([, data]) => data)
|
.map(([, data]) => data)
|
||||||
.slice(-12) // Last 12 months
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate number of months to display based on days filter
|
||||||
|
const getMonthsToDisplay = (days: number): number => {
|
||||||
|
if (days <= 30) return 1;
|
||||||
|
if (days <= 180) return 6;
|
||||||
|
if (days <= 365) return 12;
|
||||||
|
return Math.ceil(days / 30);
|
||||||
|
};
|
||||||
|
|
||||||
|
const monthsToDisplay = getMonthsToDisplay(days);
|
||||||
|
const displayData = monthlyData.slice(-monthsToDisplay);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
@@ -94,7 +104,7 @@ export default function MonthlyTrends({ className, days = 365 }: MonthlyTrendsPr
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (monthlyData.length === 0) {
|
if (displayData.length === 0) {
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
||||||
@@ -123,14 +133,22 @@ export default function MonthlyTrends({ className, days = 365 }: MonthlyTrendsPr
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Generate dynamic title based on time period
|
||||||
|
const getTitle = (days: number): string => {
|
||||||
|
if (days <= 30) return "Monthly Spending Trends (Last 30 Days)";
|
||||||
|
if (days <= 180) return "Monthly Spending Trends (Last 6 Months)";
|
||||||
|
if (days <= 365) return "Monthly Spending Trends (Last 12 Months)";
|
||||||
|
return `Monthly Spending Trends (Last ${Math.ceil(days / 30)} Months)`;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
<h3 className="text-lg font-medium text-gray-900 mb-4">
|
||||||
Monthly Spending Trends (Last 12 Months)
|
{getTitle(days)}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="h-80">
|
<div className="h-80">
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
<BarChart data={monthlyData} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
|
<BarChart data={displayData} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
|
||||||
<CartesianGrid strokeDasharray="3 3" />
|
<CartesianGrid strokeDasharray="3 3" />
|
||||||
<XAxis
|
<XAxis
|
||||||
dataKey="month"
|
dataKey="month"
|
||||||
|
|||||||
@@ -144,41 +144,6 @@ function AnalyticsDashboard() {
|
|||||||
<div className="bg-white rounded-lg shadow p-6 border border-gray-200">
|
<div className="bg-white rounded-lg shadow p-6 border border-gray-200">
|
||||||
<MonthlyTrends days={selectedPeriod.days} />
|
<MonthlyTrends days={selectedPeriod.days} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Summary Section */}
|
|
||||||
{stats && (
|
|
||||||
<div className="bg-blue-50 rounded-lg p-6 border border-blue-200">
|
|
||||||
<h3 className="text-lg font-medium text-blue-900 mb-4">
|
|
||||||
Period Summary ({stats.period_days} days)
|
|
||||||
</h3>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 text-sm">
|
|
||||||
<div>
|
|
||||||
<p className="text-blue-700 font-medium">Booked Transactions</p>
|
|
||||||
<p className="text-blue-900">{stats.booked_transactions}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-blue-700 font-medium">Pending Transactions</p>
|
|
||||||
<p className="text-blue-900">{stats.pending_transactions}</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-blue-700 font-medium">Transaction Ratio</p>
|
|
||||||
<p className="text-blue-900">
|
|
||||||
{stats.total_transactions > 0
|
|
||||||
? `${Math.round(
|
|
||||||
(stats.booked_transactions / stats.total_transactions) * 100
|
|
||||||
)}% booked`
|
|
||||||
: "No transactions"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="text-blue-700 font-medium">Spend Rate</p>
|
|
||||||
<p className="text-blue-900">
|
|
||||||
€{((stats.total_expenses || 0) / stats.period_days).toFixed(2)}/day
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user