Files
claudemesh/apps/web/src/app/[locale]/(apps)/demo/page.tsx
Alejandro Gutiérrez d3163a5bff feat(db): mesh data model — meshes, members, invites, audit log
- pgSchema "mesh" with 4 tables isolating the peer mesh domain
- Enums: visibility, transport, tier, role
- audit_log is metadata-only (E2E encryption enforced at broker/client)
- Cascade on mesh delete, soft-delete via archivedAt/revokedAt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 21:19:32 +01:00

126 lines
4.8 KiB
TypeScript

/* eslint-disable i18next/no-literal-string */
"use client";
import Link from "next/link";
import { cn } from "@turbostarter/ui";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@turbostarter/ui-web/card";
import { Icons } from "@turbostarter/ui-web/icons";
interface DemoItem {
title: string;
description: string;
href: string;
icon: keyof typeof Icons;
status: "stable" | "experimental" | "new";
}
const demos: DemoItem[] = [
{
title: "Scroll Fade",
description: "CSS mask-image based scroll indicators that fade content at edges when scrollable.",
href: "/demo/scroll-test",
icon: "ScrollText",
status: "new",
},
];
const statusStyles = {
stable: "bg-green-500/10 text-green-500 border-green-500/20",
experimental: "bg-amber-500/10 text-amber-500 border-amber-500/20",
new: "bg-blue-500/10 text-blue-500 border-blue-500/20",
};
const statusLabels = {
stable: "Stable",
experimental: "Experimental",
new: "New",
};
export default function DemoHubPage() {
return (
<div className="min-h-screen bg-background">
{/* Header */}
<div className="border-b bg-card/50 backdrop-blur-sm">
<div className="mx-auto max-w-6xl px-6 py-8">
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10">
<Icons.LayoutDashboard className="h-5 w-5 text-primary" />
</div>
<div>
<h1 className="text-2xl font-bold tracking-tight">Component Demos</h1>
<p className="text-muted-foreground text-sm">
Interactive demonstrations of UI components and patterns
</p>
</div>
</div>
</div>
</div>
{/* Content */}
<div className="mx-auto max-w-6xl px-6 py-8">
{demos.length === 0 ? (
<Card className="border-dashed">
<CardContent className="flex flex-col items-center justify-center py-12">
<Icons.Package className="h-12 w-12 text-muted-foreground/50" />
<p className="mt-4 text-muted-foreground">No demos available yet</p>
</CardContent>
</Card>
) : (
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{demos.map((demo) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const Icon = Icons[demo.icon] || Icons.Package;
return (
<Link key={demo.href} href={demo.href}>
<Card className="group h-full transition-all hover:border-primary/50 hover:shadow-md">
<CardHeader className="pb-3">
<div className="flex items-start justify-between">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-muted transition-colors group-hover:bg-primary/10">
<Icon className="h-5 w-5 text-muted-foreground transition-colors group-hover:text-primary" />
</div>
<span
className={cn(
"rounded-full border px-2 py-0.5 text-xs font-medium",
statusStyles[demo.status]
)}
>
{statusLabels[demo.status]}
</span>
</div>
<CardTitle className="mt-3 text-lg">{demo.title}</CardTitle>
<CardDescription className="line-clamp-2">
{demo.description}
</CardDescription>
</CardHeader>
<CardContent className="pt-0">
<div className="flex items-center text-sm text-muted-foreground group-hover:text-primary">
<span>View demo</span>
<Icons.ArrowRight className="ml-1 h-4 w-4 transition-transform group-hover:translate-x-1" />
</div>
</CardContent>
</Card>
</Link>
);
})}
</div>
)}
{/* Footer info */}
<div className="mt-12 rounded-lg border border-dashed bg-muted/30 p-6">
<div className="flex items-start gap-3">
<Icons.Info className="mt-0.5 h-5 w-5 text-muted-foreground" />
<div className="space-y-1">
<p className="text-sm font-medium">Adding new demos</p>
<p className="text-sm text-muted-foreground">
Create a new folder in <code className="rounded bg-muted px-1.5 py-0.5 text-xs">app/[locale]/(apps)/demo/</code> and
add it to the demos array in this page.
</p>
</div>
</div>
</div>
</div>
</div>
);
}