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:
copilot-swe-agent[bot]
2025-09-13 23:40:25 +00:00
committed by Elisiário Couto
parent c8f0a103c6
commit d51aa9429e
3 changed files with 32 additions and 49 deletions

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>
); );
} }