This commit is contained in:
Pierre Wessman 2025-12-14 17:47:17 +01:00
parent 4bdddaa7e7
commit 08a3132bec
9 changed files with 259 additions and 83 deletions

View File

@ -2,7 +2,9 @@
"permissions": { "permissions": {
"allow": [ "allow": [
"Bash(cat:*)", "Bash(cat:*)",
"Bash(tree:*)" "Bash(tree:*)",
"Bash(pnpm run build:*)",
"Bash(pnpm run dev:*)"
] ]
} }
} }

View File

@ -6,12 +6,14 @@ import { MetricCard } from "@/components/dashboard/metric-card"
import { RevenueChart } from "@/components/dashboard/revenue-chart" import { RevenueChart } from "@/components/dashboard/revenue-chart"
import { SalesChart } from "@/components/dashboard/sales-chart" import { SalesChart } from "@/components/dashboard/sales-chart"
import { PaymentsTable } from "@/components/dashboard/payments-table" import { PaymentsTable } from "@/components/dashboard/payments-table"
import { UnclassifiedPaymentsTable } from "@/components/dashboard/unclassified-payments-table"
import { TeamTable } from "@/components/dashboard/team-table" import { TeamTable } from "@/components/dashboard/team-table"
import { getDashboardData } from "@/lib/mock-data" import { getDashboardData } from "@/lib/mock-data"
export default function Home() { export default function Home() {
const data = getDashboardData() const data = getDashboardData()
// <TeamTable team={data.team} />
return ( return (
<> <>
<AppSidebar /> <AppSidebar />
@ -40,7 +42,7 @@ export default function Home() {
<PaymentsTable payments={data.payments} /> <PaymentsTable payments={data.payments} />
</div> </div>
<div className="col-span-3"> <div className="col-span-3">
<TeamTable team={data.team} /> <UnclassifiedPaymentsTable transactions={data.transactions} />
</div> </div>
</div> </div>
</DashboardTabs> </DashboardTabs>

View File

@ -30,14 +30,22 @@ interface PaymentsTableProps {
} }
export function PaymentsTable({ payments }: PaymentsTableProps) { export function PaymentsTable({ payments }: PaymentsTableProps) {
const getStatusColor = (status: Payment["status"]) => { const getCategoryColor = (category: Payment["category"]) => {
switch (status) { switch (category) {
case "success": case "groceries":
return "bg-green-500/10 text-green-500 hover:bg-green-500/20" return "bg-green-500/15 text-green-500 hover:bg-green-500/20"
case "processing": case "utilities":
return "bg-yellow-500/10 text-yellow-500 hover:bg-yellow-500/20" return "bg-blue-500/15 text-blue-500 hover:bg-blue-500/20"
case "failed": case "transport":
return "bg-red-500/10 text-red-500 hover:bg-red-500/20" return "bg-purple-500/15 text-purple-500 hover:bg-purple-500/20"
case "entertainment":
return "bg-pink-500/15 text-pink-500 hover:bg-pink-500/20"
case "shopping":
return "bg-orange-500/15 text-orange-500 hover:bg-orange-500/20"
case "insurance":
return "bg-red-500/15 text-red-500 hover:bg-red-500/20"
case "housing":
return "bg-yellow-500/15 text-yellow-500 hover:bg-yellow-500/20"
default: default:
return "" return ""
} }
@ -54,8 +62,8 @@ export function PaymentsTable({ payments }: PaymentsTableProps) {
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead>Status</TableHead> <TableHead>Category</TableHead>
<TableHead>Email</TableHead> <TableHead>Description</TableHead>
<TableHead className="text-right">Amount</TableHead> <TableHead className="text-right">Amount</TableHead>
<TableHead className="text-right">Actions</TableHead> <TableHead className="text-right">Actions</TableHead>
</TableRow> </TableRow>
@ -64,11 +72,11 @@ export function PaymentsTable({ payments }: PaymentsTableProps) {
{payments.map((payment) => ( {payments.map((payment) => (
<TableRow key={payment.id}> <TableRow key={payment.id}>
<TableCell> <TableCell>
<Badge className={getStatusColor(payment.status)} variant="secondary"> <Badge className={getCategoryColor(payment.category)} variant="secondary">
{payment.status} {payment.category}
</Badge> </Badge>
</TableCell> </TableCell>
<TableCell className="font-medium">{payment.email}</TableCell> <TableCell className="font-medium">{payment.description}</TableCell>
<TableCell className="text-right"> <TableCell className="text-right">
${payment.amount.toFixed(2)} ${payment.amount.toFixed(2)}
</TableCell> </TableCell>

View File

@ -1,7 +1,9 @@
"use client" "use client"
import { useTheme } from "next-themes"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { LineChart } from "@/components/charts/line-chart" import { LineChart } from "@/components/charts/line-chart"
import { getChartColors } from "@/lib/chart-config"
import type { ChartDataPoint } from "@/types/dashboard" import type { ChartDataPoint } from "@/types/dashboard"
interface RevenueChartProps { interface RevenueChartProps {
@ -9,32 +11,35 @@ interface RevenueChartProps {
} }
export function RevenueChart({ data }: RevenueChartProps) { export function RevenueChart({ data }: RevenueChartProps) {
const { theme } = useTheme()
const colors = getChartColors(theme)
const chartData = { const chartData = {
labels: data.map((d) => d.label), labels: data.map((d) => d.label),
datasets: [ datasets: [
{ {
label: "Revenue", label: "Revenue",
data: data.map((d) => d.value), data: data.map((d) => d.value),
borderColor: "rgb(244, 114, 182)", borderColor: colors.blue,
backgroundColor: "rgba(244, 114, 182, 0.2)", backgroundColor: `${colors.blue.replace('rgb', 'rgba').replace(')', ', 0.2)')}`,
}, },
{ {
label: "Subscriptions", label: "Subscriptions",
data: data.map((d) => d.secondaryValue || 0), data: data.map((d) => d.secondaryValue || 0),
borderColor: "rgb(94, 234, 212)", borderColor: colors.orange,
backgroundColor: "rgba(94, 234, 212, 0.2)", backgroundColor: `${colors.orange.replace('rgb', 'rgba').replace(')', ', 0.2)')}`,
}, },
], ],
} }
return ( return (
<Card className="h-[300px] flex flex-col"> <Card className="h-[350px] flex flex-col">
<CardHeader className="flex-shrink-0"> <CardHeader className="flex-shrink-0">
<CardTitle>Revenue Overview</CardTitle> <CardTitle>Revenue Overview</CardTitle>
<CardDescription>Showing total revenue for the last 6 months</CardDescription> <CardDescription>Showing total revenue for the last 6 months</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="flex-1 min-h-0"> <CardContent className="flex-1 min-h-0">
<LineChart data={chartData} height={180} showGrid={false} showPoints={false} /> <LineChart data={chartData} height={230} showGrid={false} showPoints={false} />
</CardContent> </CardContent>
</Card> </Card>
) )

View File

@ -1,7 +1,9 @@
"use client" "use client"
import { useTheme } from "next-themes"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { BarChart } from "@/components/charts/bar-chart" import { BarChart } from "@/components/charts/bar-chart"
import { getChartColors } from "@/lib/chart-config"
import type { ChartDataPoint } from "@/types/dashboard" import type { ChartDataPoint } from "@/types/dashboard"
interface SalesChartProps { interface SalesChartProps {
@ -9,6 +11,9 @@ interface SalesChartProps {
} }
export function SalesChart({ data }: SalesChartProps) { export function SalesChart({ data }: SalesChartProps) {
const { theme } = useTheme()
const colors = getChartColors(theme)
// Calculate total and change percentage // Calculate total and change percentage
const total = data.reduce((sum, d) => sum + d.value, 0) const total = data.reduce((sum, d) => sum + d.value, 0)
const lastMonthValue = data[data.length - 2]?.value || 0 const lastMonthValue = data[data.length - 2]?.value || 0
@ -17,16 +22,16 @@ export function SalesChart({ data }: SalesChartProps) {
? (((currentMonthValue - lastMonthValue) / lastMonthValue) * 100).toFixed(1) ? (((currentMonthValue - lastMonthValue) / lastMonthValue) * 100).toFixed(1)
: "0.0" : "0.0"
// Alternate colors for bars // Alternate colors for bars using centralized palette
const backgroundColors = data.map((_, index) => const backgroundColors = data.map((_, index) =>
index % 2 === 0 ? "rgb(251, 113, 133)" : "rgb(94, 234, 212)" index % 2 === 0 ? colors.darkBlue : colors.orange
) )
const chartData = { const chartData = {
labels: data.map((d) => d.label), labels: data.map((d) => d.label),
datasets: [ datasets: [
{ {
label: "Sales", label: "Expenditure",
data: data.map((d) => d.value), data: data.map((d) => d.value),
backgroundColor: backgroundColors, backgroundColor: backgroundColors,
}, },
@ -34,13 +39,13 @@ export function SalesChart({ data }: SalesChartProps) {
} }
return ( return (
<Card className="h-[300px] flex flex-col"> <Card className="h-[350px] flex flex-col">
<CardHeader className="flex-shrink-0"> <CardHeader className="flex-shrink-0">
<CardTitle>Subscriptions</CardTitle> <CardTitle>Expenditure</CardTitle>
<CardDescription>+{changePercent}% from last month</CardDescription> <CardDescription>+{changePercent}% from last month</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="flex-1 min-h-0"> <CardContent className="flex-1 min-h-0">
<BarChart data={chartData} height={180} showGrid={false} /> <BarChart data={chartData} height={230} showGrid={false} />
</CardContent> </CardContent>
</Card> </Card>
) )

View File

@ -0,0 +1,78 @@
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Button } from "@/components/ui/button"
import { MoreHorizontal } from "lucide-react"
import type { Transaction } from "@/types/dashboard"
interface TransactionsTableProps {
transactions: Transaction[]
}
export function UnclassifiedPaymentsTable({ transactions }: TransactionsTableProps) {
return (
<Card>
<CardHeader>
<CardTitle>Unclassified Transactions</CardTitle>
<CardDescription>You have {transactions.length} transactions that need to be labeled.</CardDescription>
</CardHeader>
<CardContent>
<div className="overflow-x-auto">
<Table>
<TableHeader>
<TableRow>
<TableHead>Description</TableHead>
<TableHead className="text-right">Amount</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{transactions.map((transaction) => (
<TableRow key={transaction.id}>
<TableCell className="font-medium">{transaction.description}</TableCell>
<TableCell className="text-right">
${transaction.amount.toFixed(2)}
</TableCell>
<TableCell className="text-right">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem>View details</DropdownMenuItem>
<DropdownMenuItem>Categorize</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</CardContent>
</Card>
)
}

View File

@ -27,9 +27,21 @@ export function getChartColors(theme: string | undefined) {
const isDark = theme === "dark" const isDark = theme === "dark"
return { return {
primary: isDark ? "rgb(96, 165, 250)" : "rgb(59, 130, 246)", // Primary colors for chart elements
secondary: isDark ? "rgb(168, 85, 247)" : "rgb(147, 51, 234)", primary: isDark ? "rgb(96, 165, 250)" : "rgb(59, 130, 246)", // blue
success: isDark ? "rgb(74, 222, 128)" : "rgb(34, 197, 94)", secondary: isDark ? "rgb(168, 85, 247)" : "rgb(147, 51, 234)", // purple
success: isDark ? "rgb(74, 222, 128)" : "rgb(34, 197, 94)", // green
// Additional chart colors
orange: isDark ? "rgb(237, 152, 95)" : "rgb(237, 152, 95)",
pink: isDark ? "rgb(236, 72, 153)" : "rgb(236, 72, 153)",
amber: isDark ? "rgb(251, 191, 36)" : "rgb(251, 191, 36)",
teal: isDark ? "rgb(94, 234, 212)" : "rgb(94, 234, 212)",
rose: isDark ? "rgb(244, 114, 182)" : "rgb(244, 114, 182)",
blue: isDark ? "rgb(96, 123, 143)": "rgb(96, 123, 143)",
darkBlue: isDark ? "rgb(67, 78, 120)" : "rgb(67, 78, 120)",
// UI colors
grid: isDark ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.1)", grid: isDark ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.1)",
text: isDark ? "rgba(255, 255, 255, 0.7)" : "rgba(0, 0, 0, 0.7)", text: isDark ? "rgba(255, 255, 255, 0.7)" : "rgba(0, 0, 0, 0.7)",
} }

View File

@ -5,35 +5,35 @@ export function getDashboardData(): DashboardData {
metrics: [ metrics: [
{ {
id: "1", id: "1",
title: "New Subscriptions", title: "Income",
value: "4,682", value: "45,682",
change: 15.54, change: -1.54,
changeLabel: "from last month", changeLabel: "from last month",
icon: "Users", icon: "DollarSign",
}, },
{ {
id: "2", id: "2",
title: "New Orders", title: "Expenditure",
value: "1,226", value: "34,226",
change: 40.2, change: 4.2,
changeLabel: "from last month", changeLabel: "from last month",
icon: "ShoppingCart", icon: "CreditCard",
}, },
{ {
id: "3", id: "3",
title: "Avg Order Revenue", title: "Saving",
value: "$1,080", value: "10,000",
change: 10.8, change: 0,
changeLabel: "from last month", changeLabel: "from last month",
icon: "DollarSign", icon: "DollarSign",
}, },
{ {
id: "4", id: "4",
title: "Total Revenue", title: "Pension",
value: "$15,231.89", value: "1,500,231",
change: 20.1, change: 2.1,
changeLabel: "from last month", changeLabel: "from last month",
icon: "CreditCard", icon: "DollarSign",
}, },
], ],
revenueData: [ revenueData: [
@ -45,93 +45,150 @@ export function getDashboardData(): DashboardData {
{ label: "Jun", value: 5800, secondaryValue: 3100 }, { label: "Jun", value: 5800, secondaryValue: 3100 },
], ],
salesData: [ salesData: [
{ label: "Jan", value: 1200 }, { label: "Housing", value: 22000 },
{ label: "Feb", value: 1900 }, { label: "Groceries", value: 13200 },
{ label: "Mar", value: 1600 }, { label: "Transport", value: 3600 },
{ label: "Apr", value: 2100 }, { label: "Entertainment", value: 4100 },
{ label: "May", value: 1800 }, { label: "Shopping", value: 5200 },
{ label: "Jun", value: 2400 }, { label: "Insurance", value: 4900 },
{ label: "Jul", value: 2200 }, { label: "Other", value: 5800 },
{ label: "Aug", value: 2600 },
{ label: "Sep", value: 2300 },
{ label: "Oct", value: 2800 },
{ label: "Nov", value: 2500 },
{ label: "Dec", value: 3100 },
], ],
payments: [ payments: [
{ {
id: "1", id: "1",
status: "success", category: "groceries",
email: "olivia.martin@email.com", description: "Costco - Groceries & Household",
amount: 316.0, amount: 316.0,
}, },
{ {
id: "2", id: "2",
status: "processing", category: "transport",
email: "jackson.lee@email.com", description: "BP Gas Station",
amount: 242.0, amount: 242.0,
}, },
{ {
id: "3", id: "3",
status: "success", category: "housing",
email: "isabella.nguyen@email.com", description: "Mortgage Payment - Wells Fargo",
amount: 837.0, amount: 837.0,
}, },
{ {
id: "4", id: "4",
status: "failed", category: "insurance",
email: "william.kim@email.com", description: "Insurance Premium - State Farm",
amount: 874.0, amount: 874.0,
}, },
{ {
id: "5", id: "5",
status: "success", category: "utilities",
email: "sofia.davis@email.com", description: "Internet Service - Comcast",
amount: 721.0, amount: 721.0,
}, },
{ {
id: "6", id: "6",
status: "processing", category: "entertainment",
email: "alexander.chen@email.com", description: "Amazon Prime - Annual Subscription",
amount: 456.0, amount: 456.0,
}, },
{ {
id: "7", id: "7",
status: "success", category: "shopping",
email: "emily.wilson@email.com", description: "Best Buy - Laptop Purchase",
amount: 592.0, amount: 592.0,
}, },
{ {
id: "8", id: "8",
status: "success", category: "groceries",
email: "michael.brown@email.com", description: "Trader Joe's - Groceries",
amount: 389.0, amount: 389.0,
}, },
{ {
id: "9", id: "9",
status: "failed", category: "transport",
email: "sarah.johnson@email.com", description: "Car Repair - AutoZone",
amount: 1250.0, amount: 1250.0,
}, },
{ {
id: "10", id: "10",
status: "success", category: "entertainment",
email: "daniel.garcia@email.com", description: "Spotify Premium - Family Plan",
amount: 675.0, amount: 675.0,
}, },
{ {
id: "11", id: "11",
status: "processing", category: "transport",
email: "jessica.martinez@email.com", description: "Delta Airlines - Flight Tickets",
amount: 943.0, amount: 943.0,
}, },
{ {
id: "12", id: "12",
status: "success", category: "shopping",
email: "james.rodriguez@email.com", description: "Home Depot - Home Improvement",
amount: 528.0, amount: 528.0,
}, },
], ],
transactions: [
{
id: "1",
description: "Whole Foods Market - Groceries",
amount: 127.43,
},
{
id: "2",
description: "Shell Gas Station",
amount: 52.18,
},
{
id: "3",
description: "Netflix Subscription",
amount: 15.99,
},
{
id: "4",
description: "Amazon.com - Electronics",
amount: 243.67,
},
{
id: "5",
description: "Starbucks Coffee",
amount: 6.75,
},
{
id: "6",
description: "Electric Company - Monthly Bill",
amount: 89.32,
},
{
id: "7",
description: "Target - Household Items",
amount: 94.21,
},
{
id: "8",
description: "Uber Ride",
amount: 18.50,
},
{
id: "9",
description: "Restaurant - Dinner",
amount: 67.89,
},
{
id: "10",
description: "Gym Membership",
amount: 45.00,
},
{
id: "11",
description: "Phone Bill - AT&T",
amount: 72.15,
},
{
id: "12",
description: "Apple Store - App Purchase",
amount: 4.99,
},
],
team: [ team: [
{ {
id: "1", id: "1",

View File

@ -9,8 +9,14 @@ export interface MetricCard {
export interface Payment { export interface Payment {
id: string id: string
status: "success" | "processing" | "failed" category: "groceries" | "utilities" | "transport" | "entertainment" | "shopping" | "insurance" | "housing"
email: string description: string
amount: number
}
export interface Transaction {
id: string
description: string
amount: number amount: number
} }
@ -33,5 +39,6 @@ export interface DashboardData {
revenueData: ChartDataPoint[] revenueData: ChartDataPoint[]
salesData: ChartDataPoint[] salesData: ChartDataPoint[]
payments: Payment[] payments: Payment[]
transactions: Transaction[]
team: TeamMember[] team: TeamMember[]
} }