Initial import: property management SaaS + security hardening + admin dashboard
Property Management Network — Next.js 16 (App Router), Better Auth, Drizzle ORM over PostgreSQL, Stripe, OpenAI, Resend. Includes: - Security hardening: access-control/IDOR fixes, TLS-by-default DB layer, constant-time cron auth, strict security headers, atomic AI quota gating, HTML/email output encoding, demo-backdoor disabled in production. - Superadmin dashboard at /admin (overview/MRR, server-paginated users with ban/impersonate/plan/delete, billing, platform activity + admin audit log, AI usage, system health) via the Better Auth admin plugin. - Seed/migration utility scripts under scripts/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
"use client"
|
||||
|
||||
import { motion } from "framer-motion"
|
||||
import { Building2, Users, TrendingUp } from "lucide-react"
|
||||
|
||||
const STEPS = [
|
||||
{
|
||||
step: "01",
|
||||
icon: Building2,
|
||||
title: "Add your properties",
|
||||
body: "Create properties, add units with rent amounts, and upload documents. Takes 3 minutes per property.",
|
||||
color: "text-indigo-400",
|
||||
bg: "bg-indigo-500/10 border-indigo-500/30",
|
||||
shadow: "shadow-indigo-500/20",
|
||||
},
|
||||
{
|
||||
step: "02",
|
||||
icon: Users,
|
||||
title: "Invite your tenants",
|
||||
body: "Add tenant profiles, assign them to units, create leases. Each tenant automatically gets a private portal link.",
|
||||
color: "text-violet-400",
|
||||
bg: "bg-violet-500/10 border-violet-500/30",
|
||||
shadow: "shadow-violet-500/20",
|
||||
},
|
||||
{
|
||||
step: "03",
|
||||
icon: TrendingUp,
|
||||
title: "Run on autopilot",
|
||||
body: "Rent reminders go out automatically. Maintenance gets logged. Lease expiries alert you 60 days early.",
|
||||
color: "text-emerald-400",
|
||||
bg: "bg-emerald-500/10 border-emerald-500/30",
|
||||
shadow: "shadow-emerald-500/20",
|
||||
},
|
||||
]
|
||||
|
||||
export function HowItWorks() {
|
||||
return (
|
||||
<section id="how-it-works" className="relative mx-auto max-w-7xl px-4 sm:px-6 py-16 sm:py-24">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<p className="text-xs font-semibold uppercase tracking-widest text-indigo-400 mb-3">How it works</p>
|
||||
<h2 className="text-3xl font-bold text-white sm:text-4xl">Up and running in under 10 minutes</h2>
|
||||
<p className="mt-4 text-white/50 max-w-xl mx-auto">
|
||||
No training, no onboarding call. Sign up, add your properties, and you're managing.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="relative grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{/* Connector line */}
|
||||
<div className="absolute top-10 left-[calc(16.66%+2rem)] right-[calc(16.66%+2rem)] hidden md:block h-px bg-gradient-to-r from-indigo-500/40 via-violet-500/40 to-emerald-500/40" />
|
||||
|
||||
{STEPS.map((step, i) => (
|
||||
<motion.div
|
||||
key={step.step}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: i * 0.15 }}
|
||||
className="flex flex-col items-center text-center"
|
||||
>
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.08 }}
|
||||
transition={{ type: "spring", stiffness: 300 }}
|
||||
className={`relative z-10 mb-6 flex h-20 w-20 items-center justify-center rounded-2xl border shadow-2xl ${step.bg} ${step.shadow}`}
|
||||
>
|
||||
<step.icon className={`h-8 w-8 ${step.color}`} />
|
||||
<div className="absolute -top-2.5 -right-2.5 flex h-6 w-6 items-center justify-center rounded-full bg-[#09090b] border border-white/10 text-[10px] font-bold text-white/50">
|
||||
{step.step}
|
||||
</div>
|
||||
</motion.div>
|
||||
<h3 className="text-lg font-semibold text-white mb-3">{step.title}</h3>
|
||||
<p className="text-sm text-white/50 leading-relaxed max-w-xs">{step.body}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 16 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.5 }}
|
||||
className="mt-14 text-center"
|
||||
>
|
||||
<a
|
||||
href="/signup"
|
||||
className="inline-flex items-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/30 hover:-translate-y-0.5"
|
||||
>
|
||||
Start for free — takes 3 minutes
|
||||
</a>
|
||||
</motion.div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user