Nicholai 16cee69250 __Admin dashboard scaffolded with D1 database and R2 file uploads__
This commit implements the core admin dashboard functionality including NextAuth authentication, Cloudflare D1 database integration with complete schema, and Cloudflare R2 file upload system for portfolio images. Features include artist management, appointment scheduling, and data migration capabilities.
2025-09-17 16:08:34 -06:00

261 lines
9.5 KiB
TypeScript

import { Metadata } from 'next'
import { requireAuth } from '@/lib/auth'
import { UserRole } from '@/types/database'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
// Note: Charts temporarily disabled for build compatibility
// import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, LineChart, Line, PieChart, Pie, Cell } from 'recharts'
import { TrendingUp, TrendingDown, Minus } from 'lucide-react'
interface StatsCardProps {
title: string
value: string
description: string
trend: 'up' | 'down' | 'neutral'
}
function StatsCard({ title, value, description, trend }: StatsCardProps) {
const TrendIcon = trend === 'up' ? TrendingUp : trend === 'down' ? TrendingDown : Minus
const trendColor = trend === 'up' ? 'text-green-500' : trend === 'down' ? 'text-red-500' : 'text-gray-500'
return (
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">{title}</CardTitle>
<TrendIcon className={`h-4 w-4 ${trendColor}`} />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{value}</div>
<p className={`text-xs ${trendColor}`}>
{description}
</p>
</CardContent>
</Card>
)
}
export const metadata: Metadata = {
title: 'Analytics - United Tattoo Studio',
description: 'Analytics and insights for United Tattoo Studio',
}
// Mock data for demonstration
const monthlyBookings = [
{ month: 'Jan', bookings: 45, revenue: 12500 },
{ month: 'Feb', bookings: 52, revenue: 14200 },
{ month: 'Mar', bookings: 48, revenue: 13800 },
{ month: 'Apr', bookings: 61, revenue: 16900 },
{ month: 'May', bookings: 55, revenue: 15200 },
{ month: 'Jun', bookings: 67, revenue: 18500 },
]
const artistPerformance = [
{ name: 'Sarah Chen', bookings: 28, revenue: 8400 },
{ name: 'Marcus Rodriguez', bookings: 24, revenue: 7200 },
{ name: 'Emma Thompson', bookings: 22, revenue: 6600 },
{ name: 'David Kim', bookings: 19, revenue: 5700 },
]
const serviceTypes = [
{ name: 'Traditional', value: 35, color: '#8884d8' },
{ name: 'Realism', value: 25, color: '#82ca9d' },
{ name: 'Geometric', value: 20, color: '#ffc658' },
{ name: 'Watercolor', value: 12, color: '#ff7300' },
{ name: 'Other', value: 8, color: '#00ff88' },
]
const COLORS = ['#8884d8', '#82ca9d', '#ffc658', '#ff7300', '#00ff88']
export default async function AnalyticsPage() {
// Require admin authentication
await requireAuth(UserRole.SHOP_ADMIN)
return (
<div className="space-y-6">
<div>
<h1 className="text-3xl font-bold tracking-tight">Analytics</h1>
<p className="text-muted-foreground">
Comprehensive insights and analytics for your tattoo studio
</p>
</div>
{/* Key Metrics */}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<StatsCard
title="Total Revenue"
value="$18,500"
description="+12% from last month"
trend="up"
/>
<StatsCard
title="Total Bookings"
value="67"
description="+8% from last month"
trend="up"
/>
<StatsCard
title="Active Artists"
value="4"
description="All artists active"
trend="neutral"
/>
<StatsCard
title="Avg. Session Value"
value="$276"
description="+3% from last month"
trend="up"
/>
</div>
<Tabs defaultValue="overview" className="space-y-4">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="revenue">Revenue</TabsTrigger>
<TabsTrigger value="artists">Artists</TabsTrigger>
<TabsTrigger value="services">Services</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-4">
<div className="grid gap-4 md:grid-cols-2">
<Card>
<CardHeader>
<CardTitle>Monthly Bookings</CardTitle>
<CardDescription>
Number of bookings over the last 6 months
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2">
{monthlyBookings.map((month) => (
<div key={month.month} className="flex justify-between items-center">
<span className="font-medium">{month.month}</span>
<span className="text-sm text-muted-foreground">{month.bookings} bookings</span>
</div>
))}
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Service Distribution</CardTitle>
<CardDescription>
Breakdown of tattoo styles and services
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{serviceTypes.map((service, index) => (
<div key={service.name} className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div
className="w-3 h-3 rounded-full"
style={{ backgroundColor: service.color }}
/>
<span className="font-medium">{service.name}</span>
</div>
<span className="text-muted-foreground">{service.value}%</span>
</div>
))}
</div>
</CardContent>
</Card>
</div>
</TabsContent>
<TabsContent value="revenue" className="space-y-4">
<Card>
<CardHeader>
<CardTitle>Revenue Trends</CardTitle>
<CardDescription>
Monthly revenue over the last 6 months
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2">
{monthlyBookings.map((month) => (
<div key={month.month} className="flex justify-between items-center">
<span className="font-medium">{month.month}</span>
<span className="text-sm text-muted-foreground">${month.revenue.toLocaleString()}</span>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="artists" className="space-y-4">
<Card>
<CardHeader>
<CardTitle>Artist Performance</CardTitle>
<CardDescription>
Bookings and revenue by artist this month
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2">
{artistPerformance.map((artist) => (
<div key={artist.name} className="flex justify-between items-center">
<span className="font-medium">{artist.name}</span>
<div className="text-sm text-muted-foreground">
{artist.bookings} bookings ${artist.revenue.toLocaleString()}
</div>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="services" className="space-y-4">
<div className="grid gap-4 md:grid-cols-2">
<Card>
<CardHeader>
<CardTitle>Popular Services</CardTitle>
<CardDescription>
Most requested tattoo styles
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{serviceTypes.map((service, index) => (
<div key={service.name} className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div
className="w-3 h-3 rounded-full"
style={{ backgroundColor: service.color }}
/>
<span className="font-medium">{service.name}</span>
</div>
<span className="text-muted-foreground">{service.value}%</span>
</div>
))}
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Service Trends</CardTitle>
<CardDescription>
How service preferences have changed
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="text-sm text-muted-foreground">
<p> Traditional tattoos remain the most popular choice</p>
<p> Realism has grown 15% this quarter</p>
<p> Geometric designs are trending upward</p>
<p> Watercolor requests have stabilized</p>
</div>
</div>
</CardContent>
</Card>
</div>
</TabsContent>
</Tabs>
</div>
)
}