Enhance cospend monthly expenses chart with improved UX

- Add monthly total labels above each bar showing cumulative expense amounts
- Improve chart styling: white labels, larger fonts, clean flat tooltip design
- Hide Y-axis ticks and grid lines for cleaner appearance
- Capitalize category names in legend and tooltips
- Show only hovered category in tooltip instead of all categories
- Trim empty months from start of data for users with limited history
- Create responsive layout: balance and chart side-by-side on wide screens
- Increase max width to 1400px for dashboard while keeping recent activity at 800px
- Filter out settlements from monthly expenses view

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-12 22:21:22 +02:00
parent b03ba61599
commit effed784b7
3 changed files with 190 additions and 74 deletions

View File

@@ -106,9 +106,23 @@ export const GET: RequestHandler = async ({ url, locals }) => {
});
// Convert to arrays for Chart.js
const months = Array.from(monthsMap.keys()).sort();
const allMonths = Array.from(monthsMap.keys()).sort();
const categoryList = Array.from(categories).sort();
// Find the first month with any data and trim empty months from the start
let firstMonthWithData = 0;
for (let i = 0; i < allMonths.length; i++) {
const monthData = monthsMap.get(allMonths[i]);
const hasData = Object.values(monthData).some(value => value > 0);
if (hasData) {
firstMonthWithData = i;
break;
}
}
// Trim the months array to start from the first month with data
const months = allMonths.slice(firstMonthWithData);
const datasets = categoryList.map((category: string) => ({
label: category,
data: months.map(month => monthsMap.get(month)[category] || 0)

View File

@@ -153,33 +153,38 @@
<main class="cospend-main">
<h1>Cospend</h1>
<EnhancedBalance bind:this={enhancedBalanceComponent} initialBalance={data.balance} initialDebtData={data.debtData} />
<!-- Responsive layout for balance and chart -->
<div class="dashboard-layout">
<div class="balance-section">
<EnhancedBalance bind:this={enhancedBalanceComponent} initialBalance={data.balance} initialDebtData={data.debtData} />
<div class="actions">
{#if balance.netBalance !== 0}
<a href="/cospend/settle" class="btn btn-settlement">Settle Debts</a>
{/if}
</div>
<DebtBreakdown bind:this={debtBreakdownComponent} />
<!-- Monthly Expenses Chart -->
<div class="chart-section">
{#if expensesLoading}
<div class="loading">Loading monthly expenses chart...</div>
{:else if monthlyExpensesData.datasets && monthlyExpensesData.datasets.length > 0}
<BarChart
data={monthlyExpensesData}
title="Monthly Expenses by Category"
height="400px"
/>
{:else}
<div class="loading">
Debug: expensesLoading={expensesLoading},
datasets={monthlyExpensesData.datasets?.length || 0},
data={JSON.stringify(monthlyExpensesData)}
<div class="actions">
{#if balance.netBalance !== 0}
<a href="/cospend/settle" class="btn btn-settlement">Settle Debts</a>
{/if}
</div>
{/if}
<DebtBreakdown bind:this={debtBreakdownComponent} />
</div>
<!-- Monthly Expenses Chart -->
<div class="chart-section">
{#if expensesLoading}
<div class="loading">Loading monthly expenses chart...</div>
{:else if monthlyExpensesData.datasets && monthlyExpensesData.datasets.length > 0}
<BarChart
data={monthlyExpensesData}
title="Monthly Expenses by Category"
height="400px"
/>
{:else}
<div class="loading">
Debug: expensesLoading={expensesLoading},
datasets={monthlyExpensesData.datasets?.length || 0},
data={JSON.stringify(monthlyExpensesData)}
</div>
{/if}
</div>
</div>
{#if loading}
@@ -274,7 +279,6 @@
<style>
.cospend-main {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
@@ -355,6 +359,9 @@
border-radius: 0.75rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border: 1px solid var(--nord4);
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.recent-activity h2 {
@@ -705,8 +712,33 @@
}
}
.chart-section {
.dashboard-layout {
display: flex;
flex-direction: column;
gap: 2rem;
margin-bottom: 2rem;
max-width: 1400px;
margin-left: auto;
margin-right: auto;
}
@media (min-width: 1200px) {
.dashboard-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
align-items: start;
}
.balance-section {
display: flex;
flex-direction: column;
gap: 2rem;
}
}
.chart-section {
min-height: 400px;
}
.chart-section .loading {