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:
Leon Serfaty
2026-06-23 20:36:07 -04:00
co-authored by Claude Opus 4.8
commit 857b9a7811
291 changed files with 38996 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
interface OccupancyRingProps {
rate: number // 0100
occupied: number
total: number
size?: number
}
export function OccupancyRing({ rate, occupied, total, size = 80 }: OccupancyRingProps) {
const radius = (size - 12) / 2
const circumference = 2 * Math.PI * radius
const filled = (rate / 100) * circumference
const empty = circumference - filled
const color =
rate >= 80 ? "#10b981" : rate >= 50 ? "#f59e0b" : "#ef4444"
return (
<div className="flex flex-col items-center gap-1">
<svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} className="-rotate-90">
{/* Track */}
<circle
cx={size / 2}
cy={size / 2}
r={radius}
fill="none"
stroke="rgba(255,255,255,0.06)"
strokeWidth={10}
/>
{/* Fill */}
<circle
cx={size / 2}
cy={size / 2}
r={radius}
fill="none"
stroke={color}
strokeWidth={10}
strokeDasharray={`${filled} ${empty}`}
strokeLinecap="round"
/>
</svg>
<div className="-mt-[calc(80px/2+20px)] flex flex-col items-center" style={{ marginTop: -(size / 2 + 14) }}>
<span className="text-xl font-bold text-white">{rate}%</span>
<span className="text-xs text-white/40">{occupied}/{total}</span>
</div>
</div>
)
}