Rocky_Mountain_Vending/app/admin/page.tsx

445 lines
14 KiB
TypeScript

import Link from "next/link"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import {
ShoppingCart,
Package,
Users,
TrendingUp,
DollarSign,
Clock,
CheckCircle,
Truck,
AlertTriangle,
Settings,
BarChart3,
Phone,
} from "lucide-react"
import { fetchAllProducts } from "@/lib/stripe/products"
// Mock analytics data for demo
const mockAnalytics = {
totalOrders: 156,
totalRevenue: 48567.89,
pendingOrders: 12,
completedOrders: 144,
lowStockProducts: 3,
avgOrderValue: 311.46,
conversionRate: 2.8,
monthlyGrowth: 15.2,
}
async function getProductsCount() {
try {
const products = await fetchAllProducts()
return products.length
} catch {
return 0
}
}
async function getOrdersCount() {
try {
const response = await fetch("/api/orders")
if (response.ok) {
const data = await response.json()
return data.pagination.total || 0
}
} catch {}
return mockAnalytics.totalOrders
}
export default async function AdminDashboard() {
const [productsCount, ordersCount] = await Promise.all([
getProductsCount(),
getOrdersCount(),
])
const dashboardCards = [
{
title: "Total Revenue",
value: `$${mockAnalytics.totalRevenue.toLocaleString()}`,
description: "Total revenue from all orders",
icon: DollarSign,
trend: "+15.2%",
trendPositive: true,
color: "text-green-600",
},
{
title: "Total Orders",
value: mockAnalytics.totalOrders.toString(),
description: "Total number of orders",
icon: ShoppingCart,
trend: "+12.8%",
trendPositive: true,
color: "text-blue-600",
},
{
title: "Products",
value: productsCount.toString(),
description: "Active products in inventory",
icon: Package,
trend: "+5",
trendPositive: true,
color: "text-purple-600",
},
{
title: "Pending Orders",
value: mockAnalytics.pendingOrders.toString(),
description: "Orders awaiting processing",
icon: Clock,
trend: "-3",
trendPositive: false,
color: "text-orange-600",
},
]
const quickStats = [
{
title: "Average Order Value",
value: `$${mockAnalytics.avgOrderValue.toFixed(2)}`,
description: "Average value per order",
icon: TrendingUp,
},
{
title: "Conversion Rate",
value: `${mockAnalytics.conversionRate}%`,
description: "Visitors to orders ratio",
icon: Users,
},
{
title: "Monthly Growth",
value: `${mockAnalytics.monthlyGrowth}%`,
description: "Revenue growth this month",
icon: BarChart3,
},
{
title: "Low Stock Alert",
value: mockAnalytics.lowStockProducts.toString(),
description: "Products need restocking",
icon: AlertTriangle,
},
]
const recentOrders = [
{
id: "ORD-001234",
customer: "john.doe@email.com",
amount: 2799.98,
status: "paid",
date: "2024-01-15 10:30",
},
{
id: "ORD-001233",
customer: "jane.smith@email.com",
amount: 1499.99,
status: "fulfilled",
date: "2024-01-15 09:45",
},
{
id: "ORD-001232",
customer: "bob.johnson@email.com",
amount: 899.97,
status: "pending",
date: "2024-01-15 08:20",
},
{
id: "ORD-001231",
customer: "alice.wilson@email.com",
amount: 3499.99,
status: "cancelled",
date: "2024-01-14 16:15",
},
]
const popularProducts = [
{
name: "SEAGA HY900 Vending Machine",
orders: 45,
revenue: 112499.55,
},
{
name: "Vending Machine Stand",
orders: 38,
revenue: 11399.62,
},
{
name: "Snack Vending Machine Combo",
orders: 23,
revenue: 45999.77,
},
{
name: "Drink Vending Machine",
orders: 19,
revenue: 37999.81,
},
]
return (
<div className="container mx-auto px-4 py-8">
<div className="space-y-8">
{/* Header */}
<div className="flex justify-between items-start">
<div>
<h1 className="text-4xl md:text-5xl font-bold tracking-tight text-balance">
Admin Dashboard
</h1>
<p className="text-muted-foreground mt-2">
Overview of your store performance and management tools
</p>
</div>
<div className="flex gap-2">
<Link href="/admin/calls">
<Button variant="outline">
<Phone className="h-4 w-4 mr-2" />
Phone Calls
</Button>
</Link>
<Link href="/admin/products">
<Button variant="outline">
<Settings className="h-4 w-4 mr-2" />
Settings
</Button>
</Link>
<Button>Export Report</Button>
</div>
</div>
{/* Main Stats */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{dashboardCards.map((card, index) => {
const Icon = card.icon
return (
<Card key={index}>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
{card.title}
</CardTitle>
<Icon className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{card.value}</div>
<div className="flex items-center gap-1 mt-2">
<span className={`text-sm ${card.color}`}>
{card.trend} {card.trendPositive ? "↑" : "↓"}
</span>
<span className="text-sm text-muted-foreground">
from last month
</span>
</div>
<p className="text-xs text-muted-foreground mt-2">
{card.description}
</p>
</CardContent>
</Card>
)
})}
</div>
{/* Quick Stats */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{quickStats.map((stat, index) => {
const Icon = stat.icon
return (
<Card key={index}>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">
{stat.title}
</CardTitle>
<Icon className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{stat.value}</div>
<p className="text-xs text-muted-foreground mt-2">
{stat.description}
</p>
</CardContent>
</Card>
)
})}
</div>
{/* Main Content Grid */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Recent Orders */}
<Card>
<CardHeader>
<CardTitle className="flex items-center justify-between">
Recent Orders
<Link href="/admin/orders">
<Button variant="outline" size="sm">
View All
</Button>
</Link>
</CardTitle>
<CardDescription>
Latest customer orders and their status
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{recentOrders.map((order) => (
<div
key={order.id}
className="flex items-center justify-between py-3 border-b last:border-b-0"
>
<div className="flex items-center gap-3">
<div className="min-w-0 flex-1">
<div className="font-medium">{order.id}</div>
<div className="text-sm text-muted-foreground truncate max-w-[150px]">
{order.customer}
</div>
<div className="text-xs text-muted-foreground">
{order.date}
</div>
</div>
</div>
<div className="text-right">
<div className="font-medium">
${order.amount.toFixed(2)}
</div>
<Badge
variant={
order.status === "paid"
? "default"
: order.status === "fulfilled"
? "default"
: order.status === "pending"
? "secondary"
: "destructive"
}
className="mt-1"
>
{order.status.charAt(0).toUpperCase() +
order.status.slice(1)}
</Badge>
</div>
</div>
))}
</div>
</CardContent>
</Card>
{/* Popular Products */}
<Card>
<CardHeader>
<CardTitle className="flex items-center justify-between">
Popular Products
<Link href="/admin/products">
<Button variant="outline" size="sm">
View All
</Button>
</Link>
</CardTitle>
<CardDescription>Top-selling products this month</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{popularProducts.map((product, index) => (
<div
key={index}
className="flex items-center justify-between py-3 border-b last:border-b-0"
>
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-md bg-muted flex items-center justify-center text-xs font-bold text-muted-foreground">
{index + 1}
</div>
<div className="min-w-0">
<div className="font-medium truncate max-w-[200px]">
{product.name}
</div>
<div className="text-sm text-muted-foreground">
{product.orders} orders
</div>
</div>
</div>
<div className="text-right">
<div className="font-medium">
${product.revenue.toLocaleString()}
</div>
<div className="text-xs text-muted-foreground">
${(product.revenue / product.orders).toFixed(2)} avg
</div>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</div>
{/* Quick Actions */}
<Card>
<CardHeader>
<CardTitle>Quick Actions</CardTitle>
<CardDescription>
Common administrative tasks and operations
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Link href="/admin/orders">
<Card className="h-full cursor-pointer hover:shadow-md transition-shadow">
<CardContent className="p-6 flex flex-col items-center text-center">
<ShoppingCart className="h-8 w-8 text-blue-600 mb-3" />
<h3 className="font-medium mb-1">Manage Orders</h3>
<p className="text-sm text-muted-foreground">
View and process customer orders
</p>
</CardContent>
</Card>
</Link>
<Link href="/admin/products">
<Card className="h-full cursor-pointer hover:shadow-md transition-shadow">
<CardContent className="p-6 flex flex-col items-center text-center">
<Package className="h-8 w-8 text-purple-600 mb-3" />
<h3 className="font-medium mb-1">Manage Products</h3>
<p className="text-sm text-muted-foreground">
Add and update product inventory
</p>
</CardContent>
</Card>
</Link>
<Link href="/orders">
<Card className="h-full cursor-pointer hover:shadow-md transition-shadow">
<CardContent className="p-6 flex flex-col items-center text-center">
<Truck className="h-8 w-8 text-green-600 mb-3" />
<h3 className="font-medium mb-1">Track Shipments</h3>
<p className="text-sm text-muted-foreground">
Monitor order shipments and deliveries
</p>
</CardContent>
</Card>
</Link>
<Card className="h-full hover:shadow-md transition-shadow">
<CardContent className="p-6 flex flex-col items-center text-center">
<CheckCircle className="h-8 w-8 text-orange-600 mb-3" />
<h3 className="font-medium mb-1">Reports</h3>
<p className="text-sm text-muted-foreground">
Generate sales and analytics reports
</p>
</CardContent>
</Card>
</div>
</CardContent>
</Card>
</div>
</div>
)
}
export const metadata = {
title: "Admin Dashboard | Rocky Mountain Vending",
description:
"Administrative dashboard for managing your vending machine business",
}