ui polish
This commit is contained in:
parent
a1c3c2f43c
commit
d0826dc15a
@ -6,8 +6,7 @@
|
|||||||
@theme inline {
|
@theme inline {
|
||||||
--color-background: var(--background);
|
--color-background: var(--background);
|
||||||
--color-foreground: var(--foreground);
|
--color-foreground: var(--foreground);
|
||||||
--font-sans: var(--font-geist-sans);
|
--font-sans: var(--font-inter);
|
||||||
--font-mono: var(--font-geist-mono);
|
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
|||||||
@ -11,14 +11,15 @@ interface BarChartProps {
|
|||||||
datasets: {
|
datasets: {
|
||||||
label: string
|
label: string
|
||||||
data: number[]
|
data: number[]
|
||||||
backgroundColor?: string
|
backgroundColor?: string | string[]
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
options?: ChartOptions<"bar">
|
options?: ChartOptions<"bar">
|
||||||
height?: number
|
height?: number
|
||||||
|
showGrid?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BarChart({ data, options, height = 300 }: BarChartProps) {
|
export function BarChart({ data, options, height = 300, showGrid = true }: BarChartProps) {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const colors = getChartColors(theme)
|
const colors = getChartColors(theme)
|
||||||
|
|
||||||
@ -27,6 +28,8 @@ export function BarChart({ data, options, height = 300 }: BarChartProps) {
|
|||||||
datasets: data.datasets.map((dataset) => ({
|
datasets: data.datasets.map((dataset) => ({
|
||||||
...dataset,
|
...dataset,
|
||||||
backgroundColor: dataset.backgroundColor || colors.primary,
|
backgroundColor: dataset.backgroundColor || colors.primary,
|
||||||
|
borderRadius: 6,
|
||||||
|
borderSkipped: false,
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,16 +38,19 @@ export function BarChart({ data, options, height = 300 }: BarChartProps) {
|
|||||||
...options,
|
...options,
|
||||||
scales: {
|
scales: {
|
||||||
y: {
|
y: {
|
||||||
|
display: showGrid,
|
||||||
grid: {
|
grid: {
|
||||||
|
display: showGrid,
|
||||||
color: colors.grid,
|
color: colors.grid,
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
|
display: showGrid,
|
||||||
color: colors.text,
|
color: colors.text,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
x: {
|
x: {
|
||||||
grid: {
|
grid: {
|
||||||
color: colors.grid,
|
display: false,
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
color: colors.text,
|
color: colors.text,
|
||||||
@ -55,10 +61,7 @@ export function BarChart({ data, options, height = 300 }: BarChartProps) {
|
|||||||
...defaultChartOptions.plugins,
|
...defaultChartOptions.plugins,
|
||||||
...options?.plugins,
|
...options?.plugins,
|
||||||
legend: {
|
legend: {
|
||||||
...defaultChartOptions.plugins?.legend,
|
display: false,
|
||||||
labels: {
|
|
||||||
color: colors.text,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,9 +17,11 @@ interface LineChartProps {
|
|||||||
}
|
}
|
||||||
options?: ChartOptions<"line">
|
options?: ChartOptions<"line">
|
||||||
height?: number
|
height?: number
|
||||||
|
showGrid?: boolean
|
||||||
|
showPoints?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LineChart({ data, options, height = 300 }: LineChartProps) {
|
export function LineChart({ data, options, height = 300, showGrid = true, showPoints = true }: LineChartProps) {
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const colors = getChartColors(theme)
|
const colors = getChartColors(theme)
|
||||||
|
|
||||||
@ -31,6 +33,9 @@ export function LineChart({ data, options, height = 300 }: LineChartProps) {
|
|||||||
backgroundColor: dataset.backgroundColor || (index === 0 ? `${colors.primary}20` : `${colors.secondary}20`),
|
backgroundColor: dataset.backgroundColor || (index === 0 ? `${colors.primary}20` : `${colors.secondary}20`),
|
||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
fill: true,
|
fill: true,
|
||||||
|
borderWidth: 2,
|
||||||
|
pointRadius: showPoints ? 3 : 0,
|
||||||
|
pointHoverRadius: showPoints ? 5 : 0,
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,16 +44,19 @@ export function LineChart({ data, options, height = 300 }: LineChartProps) {
|
|||||||
...options,
|
...options,
|
||||||
scales: {
|
scales: {
|
||||||
y: {
|
y: {
|
||||||
|
display: showGrid,
|
||||||
grid: {
|
grid: {
|
||||||
|
display: showGrid,
|
||||||
color: colors.grid,
|
color: colors.grid,
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
|
display: showGrid,
|
||||||
color: colors.text,
|
color: colors.text,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
x: {
|
x: {
|
||||||
grid: {
|
grid: {
|
||||||
color: colors.grid,
|
display: false,
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
color: colors.text,
|
color: colors.text,
|
||||||
@ -59,10 +67,7 @@ export function LineChart({ data, options, height = 300 }: LineChartProps) {
|
|||||||
...defaultChartOptions.plugins,
|
...defaultChartOptions.plugins,
|
||||||
...options?.plugins,
|
...options?.plugins,
|
||||||
legend: {
|
legend: {
|
||||||
...defaultChartOptions.plugins?.legend,
|
display: false,
|
||||||
labels: {
|
|
||||||
color: colors.text,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,28 @@
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||||
import { TrendingUp, TrendingDown } from "lucide-react"
|
import { TrendingUp, TrendingDown, Users, ShoppingCart, DollarSign, CreditCard, LucideIcon } from "lucide-react"
|
||||||
import type { MetricCard as MetricCardType } from "@/types/dashboard"
|
import type { MetricCard as MetricCardType } from "@/types/dashboard"
|
||||||
|
|
||||||
export function MetricCard({ title, value, change, changeLabel }: MetricCardType) {
|
const iconMap: Record<string, LucideIcon> = {
|
||||||
|
Users,
|
||||||
|
ShoppingCart,
|
||||||
|
DollarSign,
|
||||||
|
CreditCard,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MetricCard({ title, value, change, changeLabel, icon }: MetricCardType) {
|
||||||
const isPositive = change >= 0
|
const isPositive = change >= 0
|
||||||
|
const Icon = iconMap[icon]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-0">
|
||||||
<CardTitle className="text-sm font-medium">{title}</CardTitle>
|
<CardTitle className="text-sm font-medium flex items-center gap-2">
|
||||||
|
{Icon && <Icon className="h-4 w-4 text-muted-foreground" />}
|
||||||
|
{title}
|
||||||
|
</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="text-2xl font-bold">{value}</div>
|
<div className="text-3xl font-bold pb-1">{value}</div>
|
||||||
<div className="flex items-center text-xs text-muted-foreground mt-1">
|
<div className="flex items-center text-xs text-muted-foreground mt-1">
|
||||||
{isPositive ? (
|
{isPositive ? (
|
||||||
<TrendingUp className="mr-1 h-4 w-4 text-green-500" />
|
<TrendingUp className="mr-1 h-4 w-4 text-green-500" />
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { Card, CardContent, 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 type { ChartDataPoint } from "@/types/dashboard"
|
import type { ChartDataPoint } from "@/types/dashboard"
|
||||||
|
|
||||||
@ -15,21 +15,26 @@ export function RevenueChart({ data }: RevenueChartProps) {
|
|||||||
{
|
{
|
||||||
label: "Revenue",
|
label: "Revenue",
|
||||||
data: data.map((d) => d.value),
|
data: data.map((d) => d.value),
|
||||||
|
borderColor: "rgb(244, 114, 182)",
|
||||||
|
backgroundColor: "rgba(244, 114, 182, 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)",
|
||||||
|
backgroundColor: "rgba(94, 234, 212, 0.2)",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card className="h-[300px] flex flex-col">
|
||||||
<CardHeader>
|
<CardHeader className="flex-shrink-0">
|
||||||
<CardTitle>Revenue Overview</CardTitle>
|
<CardTitle>Revenue Overview</CardTitle>
|
||||||
|
<CardDescription>Showing total revenue for the last 6 months</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent className="flex-1 min-h-0">
|
||||||
<LineChart data={chartData} height={350} />
|
<LineChart data={chartData} height={180} showGrid={false} showPoints={false} />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { Card, CardContent, 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 type { ChartDataPoint } from "@/types/dashboard"
|
import type { ChartDataPoint } from "@/types/dashboard"
|
||||||
|
|
||||||
@ -9,23 +9,38 @@ interface SalesChartProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function SalesChart({ data }: SalesChartProps) {
|
export function SalesChart({ data }: SalesChartProps) {
|
||||||
|
// Calculate total and change percentage
|
||||||
|
const total = data.reduce((sum, d) => sum + d.value, 0)
|
||||||
|
const lastMonthValue = data[data.length - 2]?.value || 0
|
||||||
|
const currentMonthValue = data[data.length - 1]?.value || 0
|
||||||
|
const changePercent = lastMonthValue > 0
|
||||||
|
? (((currentMonthValue - lastMonthValue) / lastMonthValue) * 100).toFixed(1)
|
||||||
|
: "0.0"
|
||||||
|
|
||||||
|
// Alternate colors for bars
|
||||||
|
const backgroundColors = data.map((_, index) =>
|
||||||
|
index % 2 === 0 ? "rgb(251, 113, 133)" : "rgb(94, 234, 212)"
|
||||||
|
)
|
||||||
|
|
||||||
const chartData = {
|
const chartData = {
|
||||||
labels: data.map((d) => d.label),
|
labels: data.map((d) => d.label),
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: "Sales",
|
label: "Sales",
|
||||||
data: data.map((d) => d.value),
|
data: data.map((d) => d.value),
|
||||||
|
backgroundColor: backgroundColors,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card className="h-[300px] flex flex-col">
|
||||||
<CardHeader>
|
<CardHeader className="flex-shrink-0">
|
||||||
<CardTitle>Sales Activity</CardTitle>
|
<CardTitle>Subscriptions</CardTitle>
|
||||||
|
<CardDescription>+{changePercent}% from last month</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent className="flex-1 min-h-0">
|
||||||
<BarChart data={chartData} height={350} />
|
<BarChart data={chartData} height={180} showGrid={false} />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -9,6 +9,7 @@ export function getDashboardData(): DashboardData {
|
|||||||
value: "4,682",
|
value: "4,682",
|
||||||
change: 15.54,
|
change: 15.54,
|
||||||
changeLabel: "from last month",
|
changeLabel: "from last month",
|
||||||
|
icon: "Users",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "2",
|
id: "2",
|
||||||
@ -16,6 +17,7 @@ export function getDashboardData(): DashboardData {
|
|||||||
value: "1,226",
|
value: "1,226",
|
||||||
change: 40.2,
|
change: 40.2,
|
||||||
changeLabel: "from last month",
|
changeLabel: "from last month",
|
||||||
|
icon: "ShoppingCart",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "3",
|
id: "3",
|
||||||
@ -23,6 +25,7 @@ export function getDashboardData(): DashboardData {
|
|||||||
value: "$1,080",
|
value: "$1,080",
|
||||||
change: 10.8,
|
change: 10.8,
|
||||||
changeLabel: "from last month",
|
changeLabel: "from last month",
|
||||||
|
icon: "DollarSign",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "4",
|
id: "4",
|
||||||
@ -30,6 +33,7 @@ export function getDashboardData(): DashboardData {
|
|||||||
value: "$15,231.89",
|
value: "$15,231.89",
|
||||||
change: 20.1,
|
change: 20.1,
|
||||||
changeLabel: "from last month",
|
changeLabel: "from last month",
|
||||||
|
icon: "CreditCard",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
revenueData: [
|
revenueData: [
|
||||||
|
|||||||
@ -4,6 +4,7 @@ export interface MetricCard {
|
|||||||
value: string | number
|
value: string | number
|
||||||
change: number
|
change: number
|
||||||
changeLabel: string
|
changeLabel: string
|
||||||
|
icon: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Payment {
|
export interface Payment {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user