feat(03-01): add CSS animation tokens, i18n keys, and carryover display
- Add collapsible-open/close keyframes and CSS animation tokens to index.css - Add dashboard.sections and dashboard.carryoverIncludes keys to en.json and de.json - Add optional subtitle/subtitleClassName props to StatCard - Extend SummaryStrip balance prop with carryoverSubtitle/carryoverIsNegative - Compute and pass carryover subtitle from DashboardContent to SummaryStrip
This commit is contained in:
@@ -7,6 +7,8 @@ interface StatCardProps {
|
|||||||
title: string
|
title: string
|
||||||
value: string
|
value: string
|
||||||
valueClassName?: string
|
valueClassName?: string
|
||||||
|
subtitle?: string
|
||||||
|
subtitleClassName?: string
|
||||||
variance?: {
|
variance?: {
|
||||||
amount: string
|
amount: string
|
||||||
direction: "up" | "down" | "neutral"
|
direction: "up" | "down" | "neutral"
|
||||||
@@ -24,6 +26,8 @@ export function StatCard({
|
|||||||
title,
|
title,
|
||||||
value,
|
value,
|
||||||
valueClassName,
|
valueClassName,
|
||||||
|
subtitle,
|
||||||
|
subtitleClassName,
|
||||||
variance,
|
variance,
|
||||||
}: StatCardProps) {
|
}: StatCardProps) {
|
||||||
return (
|
return (
|
||||||
@@ -42,6 +46,11 @@ export function StatCard({
|
|||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
</p>
|
</p>
|
||||||
|
{subtitle && (
|
||||||
|
<p className={cn("mt-0.5 text-xs text-muted-foreground", subtitleClassName)}>
|
||||||
|
{subtitle}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
{variance && (
|
{variance && (
|
||||||
<div className="mt-1 flex items-center gap-1 text-xs text-muted-foreground">
|
<div className="mt-1 flex items-center gap-1 text-xs text-muted-foreground">
|
||||||
{(() => {
|
{(() => {
|
||||||
|
|||||||
@@ -3,7 +3,12 @@ import { StatCard } from "./StatCard"
|
|||||||
interface SummaryStripProps {
|
interface SummaryStripProps {
|
||||||
income: { value: string; budgeted: string }
|
income: { value: string; budgeted: string }
|
||||||
expenses: { 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
|
t: (key: string) => string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,6 +44,8 @@ export function SummaryStrip({
|
|||||||
title={t("dashboard.availableBalance")}
|
title={t("dashboard.availableBalance")}
|
||||||
value={balance.value}
|
value={balance.value}
|
||||||
valueClassName={balance.isPositive ? "text-on-budget" : "text-over-budget"}
|
valueClassName={balance.isPositive ? "text-on-budget" : "text-over-budget"}
|
||||||
|
subtitle={balance.carryoverSubtitle}
|
||||||
|
subtitleClassName={balance.carryoverIsNegative ? "text-over-budget" : undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -84,7 +84,12 @@
|
|||||||
"actual": "Tatsaechlich",
|
"actual": "Tatsaechlich",
|
||||||
"noBudgetForMonth": "Kein Budget fuer diesen Monat",
|
"noBudgetForMonth": "Kein Budget fuer diesen Monat",
|
||||||
"createBudget": "Budget erstellen",
|
"createBudget": "Budget erstellen",
|
||||||
"generateFromTemplate": "Aus Vorlage generieren"
|
"generateFromTemplate": "Aus Vorlage generieren",
|
||||||
|
"sections": {
|
||||||
|
"itemName": "Posten",
|
||||||
|
"groupTotal": "{{label}} Gesamt"
|
||||||
|
},
|
||||||
|
"carryoverIncludes": "Inkl. {{amount}} Übertrag"
|
||||||
},
|
},
|
||||||
"quickAdd": {
|
"quickAdd": {
|
||||||
"title": "Schnelleingabe-Bibliothek",
|
"title": "Schnelleingabe-Bibliothek",
|
||||||
|
|||||||
@@ -84,7 +84,12 @@
|
|||||||
"actual": "Actual",
|
"actual": "Actual",
|
||||||
"noBudgetForMonth": "No budget for this month",
|
"noBudgetForMonth": "No budget for this month",
|
||||||
"createBudget": "Create Budget",
|
"createBudget": "Create Budget",
|
||||||
"generateFromTemplate": "Generate from Template"
|
"generateFromTemplate": "Generate from Template",
|
||||||
|
"sections": {
|
||||||
|
"itemName": "Item",
|
||||||
|
"groupTotal": "{{label}} Total"
|
||||||
|
},
|
||||||
|
"carryoverIncludes": "Includes {{amount}} carryover"
|
||||||
},
|
},
|
||||||
"quickAdd": {
|
"quickAdd": {
|
||||||
"title": "Quick Add Library",
|
"title": "Quick Add Library",
|
||||||
|
|||||||
@@ -71,9 +71,23 @@
|
|||||||
|
|
||||||
--radius: 0.625rem;
|
--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;
|
--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 {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border;
|
@apply border-border;
|
||||||
|
|||||||
@@ -124,6 +124,12 @@ function DashboardContent({ budgetId }: { budgetId: string }) {
|
|||||||
const currency = budget.currency
|
const currency = budget.currency
|
||||||
const availableBalance = totalIncome - totalExpenses + budget.carryover_amount
|
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 (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Summary cards */}
|
{/* Summary cards */}
|
||||||
@@ -139,6 +145,8 @@ function DashboardContent({ budgetId }: { budgetId: string }) {
|
|||||||
balance={{
|
balance={{
|
||||||
value: formatCurrency(availableBalance, currency),
|
value: formatCurrency(availableBalance, currency),
|
||||||
isPositive: availableBalance >= 0,
|
isPositive: availableBalance >= 0,
|
||||||
|
carryoverSubtitle,
|
||||||
|
carryoverIsNegative,
|
||||||
}}
|
}}
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user