Rocky_Mountain_Vending/app/admin/page.tsx

419 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',
}