diff --git a/src/components/dashboard/DashboardSkeleton.tsx b/src/components/dashboard/DashboardSkeleton.tsx new file mode 100644 index 0000000..2936e68 --- /dev/null +++ b/src/components/dashboard/DashboardSkeleton.tsx @@ -0,0 +1,49 @@ +import { Skeleton } from "@/components/ui/skeleton" +import { Card, CardContent, CardHeader } from "@/components/ui/card" + +function SkeletonStatCard() { + return ( + + + + + + + + + + ) +} + +export function DashboardSkeleton() { + return ( +
+ {/* Summary cards skeleton */} +
+ + + +
+ + {/* Chart area skeleton */} +
+ + + + + + + + + + + + + + + + +
+
+ ) +} diff --git a/src/components/dashboard/StatCard.tsx b/src/components/dashboard/StatCard.tsx new file mode 100644 index 0000000..bdc7b33 --- /dev/null +++ b/src/components/dashboard/StatCard.tsx @@ -0,0 +1,58 @@ +import { TrendingUp, TrendingDown, Minus } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" + +interface StatCardProps { + title: string + value: string + valueClassName?: string + variance?: { + amount: string + direction: "up" | "down" | "neutral" + label: string + } +} + +const directionIcon = { + up: TrendingUp, + down: TrendingDown, + neutral: Minus, +} as const + +export function StatCard({ + title, + value, + valueClassName, + variance, +}: StatCardProps) { + return ( + + + + {title} + + + +

+ {value} +

+ {variance && ( +
+ {(() => { + const Icon = directionIcon[variance.direction] + return + })()} + {variance.amount} + {variance.label} +
+ )} +
+
+ ) +} diff --git a/src/components/dashboard/SummaryStrip.tsx b/src/components/dashboard/SummaryStrip.tsx new file mode 100644 index 0000000..c22d9b9 --- /dev/null +++ b/src/components/dashboard/SummaryStrip.tsx @@ -0,0 +1,45 @@ +import { StatCard } from "./StatCard" + +interface SummaryStripProps { + income: { value: string; budgeted: string } + expenses: { value: string; budgeted: string } + balance: { value: string; isPositive: boolean } + t: (key: string) => string +} + +export function SummaryStrip({ + income, + expenses, + balance, + t, +}: SummaryStripProps) { + return ( +
+ + + +
+ ) +} diff --git a/src/components/shared/PageShell.tsx b/src/components/shared/PageShell.tsx new file mode 100644 index 0000000..32907a1 --- /dev/null +++ b/src/components/shared/PageShell.tsx @@ -0,0 +1,28 @@ +interface PageShellProps { + title: string + description?: string + action?: React.ReactNode + children: React.ReactNode +} + +export function PageShell({ + title, + description, + action, + children, +}: PageShellProps) { + return ( +
+
+
+

{title}

+ {description && ( +

{description}

+ )} +
+ {action &&
{action}
} +
+ {children} +
+ ) +}