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} />