diff --git a/src/components/dashboard/StatCard.tsx b/src/components/dashboard/StatCard.tsx
index bdc7b33..b420c91 100644
--- a/src/components/dashboard/StatCard.tsx
+++ b/src/components/dashboard/StatCard.tsx
@@ -7,6 +7,8 @@ interface StatCardProps {
title: string
value: string
valueClassName?: string
+ subtitle?: string
+ subtitleClassName?: string
variance?: {
amount: string
direction: "up" | "down" | "neutral"
@@ -24,6 +26,8 @@ export function StatCard({
title,
value,
valueClassName,
+ subtitle,
+ subtitleClassName,
variance,
}: StatCardProps) {
return (
@@ -42,6 +46,11 @@ export function StatCard({
>
{value}
+ {subtitle && (
+
+ {subtitle}
+
+ )}
{variance && (
{(() => {
diff --git a/src/components/dashboard/SummaryStrip.tsx b/src/components/dashboard/SummaryStrip.tsx
index c22d9b9..64ede76 100644
--- a/src/components/dashboard/SummaryStrip.tsx
+++ b/src/components/dashboard/SummaryStrip.tsx
@@ -3,7 +3,12 @@ import { StatCard } from "./StatCard"
interface SummaryStripProps {
income: { value: string; budgeted: string }
expenses: { value: string; budgeted: string }
- balance: { value: string; isPositive: boolean }
+ balance: {
+ value: string
+ isPositive: boolean
+ carryoverSubtitle?: string
+ carryoverIsNegative?: boolean
+ }
t: (key: string) => string
}
@@ -39,6 +44,8 @@ export function SummaryStrip({
title={t("dashboard.availableBalance")}
value={balance.value}
valueClassName={balance.isPositive ? "text-on-budget" : "text-over-budget"}
+ subtitle={balance.carryoverSubtitle}
+ subtitleClassName={balance.carryoverIsNegative ? "text-over-budget" : undefined}
/>
)
diff --git a/src/i18n/de.json b/src/i18n/de.json
index ff25e6e..fb28b5b 100644
--- a/src/i18n/de.json
+++ b/src/i18n/de.json
@@ -84,7 +84,12 @@
"actual": "Tatsaechlich",
"noBudgetForMonth": "Kein Budget fuer diesen Monat",
"createBudget": "Budget erstellen",
- "generateFromTemplate": "Aus Vorlage generieren"
+ "generateFromTemplate": "Aus Vorlage generieren",
+ "sections": {
+ "itemName": "Posten",
+ "groupTotal": "{{label}} Gesamt"
+ },
+ "carryoverIncludes": "Inkl. {{amount}} Übertrag"
},
"quickAdd": {
"title": "Schnelleingabe-Bibliothek",
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 256e386..5209970 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -84,7 +84,12 @@
"actual": "Actual",
"noBudgetForMonth": "No budget for this month",
"createBudget": "Create Budget",
- "generateFromTemplate": "Generate from Template"
+ "generateFromTemplate": "Generate from Template",
+ "sections": {
+ "itemName": "Item",
+ "groupTotal": "{{label}} Total"
+ },
+ "carryoverIncludes": "Includes {{amount}} carryover"
},
"quickAdd": {
"title": "Quick Add Library",
diff --git a/src/index.css b/src/index.css
index 45c9917..c5d5974 100644
--- a/src/index.css
+++ b/src/index.css
@@ -71,9 +71,23 @@
--radius: 0.625rem;
+ /* Collapsible animation */
+ --animate-collapsible-open: collapsible-open 200ms ease-out;
+ --animate-collapsible-close: collapsible-close 200ms ease-out;
+
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
}
+@keyframes collapsible-open {
+ from { height: 0; overflow: hidden; }
+ to { height: var(--radix-collapsible-content-height); overflow: hidden; }
+}
+
+@keyframes collapsible-close {
+ from { height: var(--radix-collapsible-content-height); overflow: hidden; }
+ to { height: 0; overflow: hidden; }
+}
+
@layer base {
* {
@apply border-border;
diff --git a/src/pages/DashboardPage.tsx b/src/pages/DashboardPage.tsx
index 04c10ff..c6487dc 100644
--- a/src/pages/DashboardPage.tsx
+++ b/src/pages/DashboardPage.tsx
@@ -124,6 +124,12 @@ function DashboardContent({ budgetId }: { budgetId: string }) {
const currency = budget.currency
const availableBalance = totalIncome - totalExpenses + budget.carryover_amount
+ const carryover = budget.carryover_amount
+ const carryoverSubtitle = carryover !== 0
+ ? t("dashboard.carryoverIncludes", { amount: formatCurrency(Math.abs(carryover), currency) })
+ : undefined
+ const carryoverIsNegative = carryover < 0
+
return (
{/* Summary cards */}
@@ -139,6 +145,8 @@ function DashboardContent({ budgetId }: { budgetId: string }) {
balance={{
value: formatCurrency(availableBalance, currency),
isPositive: availableBalance >= 0,
+ carryoverSubtitle,
+ carryoverIsNegative,
}}
t={t}
/>