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": {
"allow": [
"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 { SalesChart } from "@/components/dashboard/sales-chart"
import { PaymentsTable } from "@/components/dashboard/payments-table"
import { UnclassifiedPaymentsTable } from "@/components/dashboard/unclassified-payments-table"
import { TeamTable } from "@/components/dashboard/team-table"
import { getDashboardData } from "@/lib/mock-data"
export default function Home() {
const data = getDashboardData()
// <TeamTable team={data.team} />
return (
<>
<AppSidebar />
@ -40,7 +42,7 @@ export default function Home() {
<PaymentsTable payments={data.payments} />
</div>
<div className="col-span-3">
<TeamTable team={data.team} />
<UnclassifiedPaymentsTable transactions={data.transactions} />
</div>
</div>
</DashboardTabs>

View File

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

View File

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

View File

@ -1,7 +1,9 @@
"use client"
import { useTheme } from "next-themes"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { BarChart } from "@/components/charts/bar-chart"
import { getChartColors } from "@/lib/chart-config"
import type { ChartDataPoint } from "@/types/dashboard"
interface SalesChartProps {
@ -9,6 +11,9 @@ interface SalesChartProps {
}
export function SalesChart({ data }: SalesChartProps) {
const { theme } = useTheme()
const colors = getChartColors(theme)
// Calculate total and change percentage
const total = data.reduce((sum, d) => sum + d.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)
: "0.0"
// Alternate colors for bars
// Alternate colors for bars using centralized palette
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 = {
labels: data.map((d) => d.label),
datasets: [
{
label: "Sales",
label: "Expenditure",
data: data.map((d) => d.value),
backgroundColor: backgroundColors,
},
@ -34,13 +39,13 @@ export function SalesChart({ data }: SalesChartProps) {
}
return (
<Card className="h-[300px] flex flex-col">
<Card className="h-[350px] flex flex-col">
<CardHeader className="flex-shrink-0">
<CardTitle>Subscriptions</CardTitle>
<CardTitle>Expenditure</CardTitle>
<CardDescription>+{changePercent}% from last month</CardDescription>
</CardHeader>
<CardContent className="flex-1 min-h-0">
<BarChart data={chartData} height={180} showGrid={false} />
<BarChart data={chartData} height={230} showGrid={false} />
</CardContent>
</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"
return {
primary: isDark ? "rgb(96, 165, 250)" : "rgb(59, 130, 246)",
secondary: isDark ? "rgb(168, 85, 247)" : "rgb(147, 51, 234)",
success: isDark ? "rgb(74, 222, 128)" : "rgb(34, 197, 94)",
// Primary colors for chart elements
primary: isDark ? "rgb(96, 165, 250)" : "rgb(59, 130, 246)", // blue
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)",
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: [
{
id: "1",
title: "New Subscriptions",
value: "4,682",
change: 15.54,
title: "Income",
value: "45,682",
change: -1.54,
changeLabel: "from last month",
icon: "Users",
icon: "DollarSign",
},
{
id: "2",
title: "New Orders",
value: "1,226",
change: 40.2,
title: "Expenditure",
value: "34,226",
change: 4.2,
changeLabel: "from last month",
icon: "ShoppingCart",
icon: "CreditCard",
},
{
id: "3",
title: "Avg Order Revenue",
value: "$1,080",
change: 10.8,
title: "Saving",
value: "10,000",
change: 0,
changeLabel: "from last month",
icon: "DollarSign",
},
{
id: "4",
title: "Total Revenue",
value: "$15,231.89",
change: 20.1,
title: "Pension",
value: "1,500,231",
change: 2.1,
changeLabel: "from last month",
icon: "CreditCard",
icon: "DollarSign",
},
],
revenueData: [
@ -45,93 +45,150 @@ export function getDashboardData(): DashboardData {
{ label: "Jun", value: 5800, secondaryValue: 3100 },
],
salesData: [
{ label: "Jan", value: 1200 },
{ label: "Feb", value: 1900 },
{ label: "Mar", value: 1600 },
{ label: "Apr", value: 2100 },
{ label: "May", value: 1800 },
{ label: "Jun", value: 2400 },
{ label: "Jul", value: 2200 },
{ label: "Aug", value: 2600 },
{ label: "Sep", value: 2300 },
{ label: "Oct", value: 2800 },
{ label: "Nov", value: 2500 },
{ label: "Dec", value: 3100 },
{ label: "Housing", value: 22000 },
{ label: "Groceries", value: 13200 },
{ label: "Transport", value: 3600 },
{ label: "Entertainment", value: 4100 },
{ label: "Shopping", value: 5200 },
{ label: "Insurance", value: 4900 },
{ label: "Other", value: 5800 },
],
payments: [
{
id: "1",
status: "success",
email: "olivia.martin@email.com",
category: "groceries",
description: "Costco - Groceries & Household",
amount: 316.0,
},
{
id: "2",
status: "processing",
email: "jackson.lee@email.com",
category: "transport",
description: "BP Gas Station",
amount: 242.0,
},
{
id: "3",
status: "success",
email: "isabella.nguyen@email.com",
category: "housing",
description: "Mortgage Payment - Wells Fargo",
amount: 837.0,
},
{
id: "4",
status: "failed",
email: "william.kim@email.com",
category: "insurance",
description: "Insurance Premium - State Farm",
amount: 874.0,
},
{
id: "5",
status: "success",
email: "sofia.davis@email.com",
category: "utilities",
description: "Internet Service - Comcast",
amount: 721.0,
},
{
id: "6",
status: "processing",
email: "alexander.chen@email.com",
category: "entertainment",
description: "Amazon Prime - Annual Subscription",
amount: 456.0,
},
{
id: "7",
status: "success",
email: "emily.wilson@email.com",
category: "shopping",
description: "Best Buy - Laptop Purchase",
amount: 592.0,
},
{
id: "8",
status: "success",
email: "michael.brown@email.com",
category: "groceries",
description: "Trader Joe's - Groceries",
amount: 389.0,
},
{
id: "9",
status: "failed",
email: "sarah.johnson@email.com",
category: "transport",
description: "Car Repair - AutoZone",
amount: 1250.0,
},
{
id: "10",
status: "success",
email: "daniel.garcia@email.com",
category: "entertainment",
description: "Spotify Premium - Family Plan",
amount: 675.0,
},
{
id: "11",
status: "processing",
email: "jessica.martinez@email.com",
category: "transport",
description: "Delta Airlines - Flight Tickets",
amount: 943.0,
},
{
id: "12",
status: "success",
email: "james.rodriguez@email.com",
category: "shopping",
description: "Home Depot - Home Improvement",
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: [
{
id: "1",

View File

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