Files
property-management-network/app/(dashboard)/settings/demo/page.tsx
T

136 lines
6.9 KiB
TypeScript
Raw Normal View History

import { seedDemoData, clearDemoData, setTestPlan } from "@/app/actions/seed-demo"
import { eq } from "drizzle-orm"
import { db } from "@/lib/db"
import { profiles } from "@/lib/db/schema"
import { getSessionUser, isAdminUser } from "@/lib/session"
import { redirect, notFound } from "next/navigation"
import {
Building2, Users, CreditCard, Wrench,
FileText, Receipt, Sparkles, Trash2, CheckCircle2, Zap, Crown, Infinity
} from "lucide-react"
const DEMO_CONTENTS = [
{ icon: Building2, color: "text-indigo-400 bg-indigo-500/10", label: "3 Properties", detail: "Maple Court, Riverdale Flats, Crestwood Villa" },
{ icon: Building2, color: "text-violet-400 bg-violet-500/10", label: "7 Units", detail: "Mix of 1, 2 & 3-bedroom units across all properties" },
{ icon: Users, color: "text-blue-400 bg-blue-500/10", label: "6 Tenants", detail: "Sarah, Marcus, Priya, David, Emily & James — with contacts" },
{ icon: FileText, color: "text-emerald-400 bg-emerald-500/10", label: "6 Leases", detail: "Active leases, 2 expiring soon to trigger alerts" },
{ icon: CreditCard, color: "text-teal-400 bg-teal-500/10", label: "30+ Payments", detail: "6 months of history — paid, pending & overdue statuses" },
{ icon: Wrench, color: "text-amber-400 bg-amber-500/10", label: "6 Maintenance Requests", detail: "Open, in-progress & resolved — with priorities" },
{ icon: Receipt, color: "text-rose-400 bg-rose-500/10", label: "8 Expenses", detail: "Repairs, insurance, utilities, taxes — with vendors" },
]
const PLANS = [
{ value: "starter", label: "Starter", icon: Zap, color: "text-white/60 border-white/10 hover:border-white/20", desc: "Free — no AI" },
{ value: "pro", label: "Pro", icon: Sparkles, color: "text-indigo-300 border-indigo-500/30 hover:border-indigo-500/60 bg-indigo-500/5", desc: "50 AI calls/mo" },
{ value: "landlord", label: "Landlord", icon: Crown, color: "text-violet-300 border-violet-500/30 hover:border-violet-500/60 bg-violet-500/5", desc: "200 AI calls/mo" },
{ value: "lifetime", label: "Lifetime", icon: Infinity, color: "text-amber-300 border-amber-500/30 hover:border-amber-500/60 bg-amber-500/5", desc: "Unlimited — all features" },
] as const
export default async function DemoDataPage() {
const user = await getSessionUser()
if (!user) redirect("/login")
// Admin-only testing tool — regular users get a 404 (and never see the nav link).
if (!isAdminUser(user)) notFound()
const profile = await db.query.profiles.findFirst({
where: eq(profiles.id, user.id),
columns: { plan: true },
})
const currentPlan = profile?.plan ?? "starter"
return (
<div className="max-w-2xl mx-auto space-y-6">
{/* Plan switcher — most prominent */}
<div className="rounded-2xl border border-indigo-500/20 bg-gradient-to-br from-indigo-600/10 via-[#16161f] to-violet-600/5 overflow-hidden">
<div className="px-5 py-4 border-b border-white/[0.06]">
<div className="flex items-center gap-2">
<Sparkles className="h-4 w-4 text-indigo-400" />
<p className="text-sm font-semibold text-white">Test Plan</p>
</div>
<p className="text-xs text-white/40 mt-0.5">
Switch plans instantly to test different features current: <span className="text-indigo-300 font-semibold capitalize">{currentPlan}</span>
</p>
</div>
<div className="grid grid-cols-2 sm:grid-cols-4 gap-2 p-4">
{PLANS.map((plan) => {
const isActive = currentPlan === plan.value
return (
<form key={plan.value} action={setTestPlan.bind(null, plan.value)}>
<button
type="submit"
className={`w-full flex flex-col items-center gap-1.5 rounded-xl border px-3 py-3 transition-all ${plan.color} ${isActive ? "ring-2 ring-indigo-500/50 ring-offset-1 ring-offset-[#16161f]" : ""}`}
>
<plan.icon className="h-4 w-4" />
<span className="text-xs font-semibold">{plan.label}</span>
<span className="text-[10px] opacity-60">{plan.desc}</span>
{isActive && <span className="text-[9px] font-bold text-emerald-400 uppercase tracking-wider">Active</span>}
</button>
</form>
)
})}
</div>
<div className="px-5 pb-4">
<p className="text-[10px] text-white/25 text-center">
For testing only does not affect real billing
</p>
</div>
</div>
{/* Demo data section */}
<div className="rounded-2xl border border-white/[0.06] bg-[#16161f] overflow-hidden">
<div className="px-5 py-4 border-b border-white/[0.06]">
<p className="text-sm font-semibold text-white">Demo Data</p>
<p className="text-xs text-white/40 mt-0.5">Populate your account with realistic sample data</p>
</div>
<div className="divide-y divide-white/[0.04]">
{DEMO_CONTENTS.map((item) => (
<div key={item.label} className="flex items-center gap-4 px-5 py-3">
<div className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-xl ${item.color}`}>
<item.icon className="h-3.5 w-3.5" />
</div>
<div className="min-w-0">
<p className="text-sm font-medium text-white">{item.label}</p>
<p className="text-xs text-white/40 truncate">{item.detail}</p>
</div>
<CheckCircle2 className="h-4 w-4 text-emerald-400/40 shrink-0 ml-auto" />
</div>
))}
</div>
</div>
{/* Warning */}
<div className="rounded-xl border border-amber-500/20 bg-amber-500/5 px-4 py-3">
<p className="text-xs text-amber-400/80 leading-relaxed">
<span className="font-semibold text-amber-400">Note:</span> "Load demo data" adds records to your account. Use "Clear all data" to wipe everything when done testing.
</p>
</div>
{/* Actions */}
<div className="flex flex-col sm:flex-row gap-3">
<form action={seedDemoData} className="flex-1">
<button
type="submit"
className="w-full flex items-center justify-center gap-2 rounded-xl bg-indigo-600 px-6 py-3 text-sm font-semibold text-white hover:bg-indigo-500 transition-all hover:shadow-lg hover:shadow-indigo-500/25"
>
<Sparkles className="h-4 w-4" />
Load demo data
</button>
</form>
<form action={clearDemoData}>
<button
type="submit"
className="w-full sm:w-auto flex items-center justify-center gap-2 rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-3 text-sm font-semibold text-red-400 hover:bg-red-500/10 hover:border-red-500/40 transition-all"
>
<Trash2 className="h-4 w-4" />
Clear all data
</button>
</form>
</div>
</div>
)
}