212d6f7335
Root layout used title.template ("%s | Epicure") and 38 pages set
their own title, producing "Recipes | Epicure", "Messages — Epicure",
etc. Drop the template, set a flat "Epicure" default, and clear every
page's title override so they all inherit it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
52 lines
2.2 KiB
TypeScript
52 lines
2.2 KiB
TypeScript
import type { Metadata } from "next";
|
|
import { db } from "@epicure/db";
|
|
import { users, recipes, userUsage, recipePhotos } from "@epicure/db";
|
|
import { count, eq, sql } from "@epicure/db";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Users, BookOpen, Sparkles, Image } from "lucide-react";
|
|
|
|
export const metadata: Metadata = {};
|
|
|
|
export default async function AdminPage() {
|
|
const currentMonth = new Date().toISOString().slice(0, 7);
|
|
|
|
const [userCount, recipeCount, proUserCount, aiUsage, imageCount] = await Promise.all([
|
|
db.select({ count: count() }).from(users).then((r) => r[0]),
|
|
db.select({ count: count() }).from(recipes).then((r) => r[0]),
|
|
db.select({ count: count() }).from(users).where(eq(users.tier, "pro")).then((r) => r[0]),
|
|
db
|
|
.select({ total: sql<string>`coalesce(sum(${userUsage.aiCallsUsed}), 0)` })
|
|
.from(userUsage)
|
|
.where(eq(userUsage.month, currentMonth))
|
|
.then((r) => r[0]),
|
|
db.select({ count: count() }).from(recipePhotos).then((r) => r[0]),
|
|
]);
|
|
|
|
const stats = [
|
|
{ label: "Total Users", value: userCount?.count ?? 0, sub: `${proUserCount?.count ?? 0} pro`, icon: Users },
|
|
{ label: "Total Recipes", value: recipeCount?.count ?? 0, icon: BookOpen },
|
|
{ label: "AI Calls This Month", value: Number(aiUsage?.total ?? 0), icon: Sparkles },
|
|
{ label: "Recipe Photos", value: imageCount?.count ?? 0, icon: Image },
|
|
];
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<h1 className="text-2xl font-bold tracking-tight">Overview</h1>
|
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{stats.map(({ label, value, sub, icon: Icon }) => (
|
|
<Card key={label}>
|
|
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
<CardTitle className="text-sm font-medium text-muted-foreground">{label}</CardTitle>
|
|
<Icon className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{value.toLocaleString()}</div>
|
|
{sub && <p className="text-xs text-muted-foreground mt-1">{sub}</p>}
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|