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
+154
View File
@@ -0,0 +1,154 @@
"use client"
import { useState } from "react"
import { FileDown } from "lucide-react"
import { toast } from "sonner"
interface ReceiptProps {
payment: {
id: string
amount: number
due_date: string
paid_date: string | null
payment_method: string | null
status: string
}
tenant: {
first_name: string
last_name: string
email?: string | null
}
property: { name: string }
unit?: { unit_number: string } | null
}
export function RentReceiptButton({ payment, tenant, property, unit }: ReceiptProps) {
const [loading, setLoading] = useState(false)
async function downloadReceipt() {
if (payment.status !== "paid") {
toast.error("Receipt only available for paid payments")
return
}
setLoading(true)
try {
const { jsPDF } = await import("jspdf")
const doc = new jsPDF({ unit: "pt", format: "a4" })
const pageW = doc.internal.pageSize.getWidth()
const margin = 48
// Header background
doc.setFillColor(22, 22, 31)
doc.rect(0, 0, pageW, 100, "F")
// Title
doc.setTextColor(255, 255, 255)
doc.setFontSize(22)
doc.setFont("helvetica", "bold")
doc.text("Property Management Network", margin, 44)
doc.setFontSize(11)
doc.setFont("helvetica", "normal")
doc.setTextColor(160, 160, 180)
doc.text("RENT RECEIPT", margin, 64)
// Receipt number
doc.setTextColor(160, 160, 180)
doc.setFontSize(9)
doc.text(`Receipt #${payment.id.slice(0, 8).toUpperCase()}`, pageW - margin, 44, { align: "right" })
doc.text(new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" }), pageW - margin, 60, { align: "right" })
// Divider
doc.setDrawColor(60, 60, 80)
doc.line(margin, 112, pageW - margin, 112)
// Tenant & Property info
doc.setTextColor(120, 120, 140)
doc.setFontSize(9)
doc.setFont("helvetica", "bold")
doc.text("TENANT", margin, 136)
doc.text("PROPERTY", pageW / 2, 136)
doc.setFont("helvetica", "normal")
doc.setTextColor(30, 30, 30)
doc.setFontSize(11)
doc.text(`${tenant.first_name} ${tenant.last_name}`, margin, 154)
doc.text(property.name, pageW / 2, 154)
if (tenant.email) {
doc.setFontSize(9)
doc.setTextColor(120, 120, 140)
doc.text(tenant.email, margin, 170)
}
if (unit) {
doc.setFontSize(9)
doc.setTextColor(120, 120, 140)
doc.text(`Unit ${unit.unit_number}`, pageW / 2, 170)
}
// Payment box
doc.setFillColor(245, 245, 250)
doc.roundedRect(margin, 200, pageW - margin * 2, 140, 8, 8, "F")
doc.setTextColor(30, 30, 30)
doc.setFontSize(13)
doc.setFont("helvetica", "bold")
doc.text("Payment Details", margin + 20, 228)
const rows = [
["Amount Paid", `$${Number(payment.amount).toFixed(2)}`],
["Due Date", payment.due_date],
["Paid Date", payment.paid_date ?? "—"],
["Payment Method", payment.payment_method ?? "—"],
["Status", "PAID"],
]
doc.setFont("helvetica", "normal")
doc.setFontSize(10)
rows.forEach(([label, value], i) => {
const y = 252 + i * 18
doc.setTextColor(100, 100, 120)
doc.text(label, margin + 20, y)
doc.setTextColor(30, 30, 30)
doc.text(value, pageW - margin - 20, y, { align: "right" })
})
// Paid stamp
doc.setTextColor(34, 197, 94)
doc.setFontSize(32)
doc.setFont("helvetica", "bold")
doc.text("PAID", pageW - margin - 20, 290, { align: "right" })
// Footer
doc.setFont("helvetica", "normal")
doc.setFontSize(8)
doc.setTextColor(160, 160, 180)
doc.text("This receipt was generated by Property Management Network. Please keep for your records.", margin, 780)
doc.text("propertymanagement.network", pageW - margin, 780, { align: "right" })
const filename = `receipt-${tenant.last_name.toLowerCase()}-${payment.due_date}.pdf`
doc.save(filename)
toast.success("Receipt downloaded")
} catch {
toast.error("Failed to generate receipt")
} finally {
setLoading(false)
}
}
if (payment.status !== "paid") return null
return (
<button
onClick={downloadReceipt}
disabled={loading}
title="Download Receipt"
className="flex items-center gap-1.5 rounded-lg border border-white/10 px-2.5 py-1.5 text-xs text-white/60 hover:border-indigo-500/30 hover:text-indigo-400 transition disabled:opacity-40"
>
<FileDown className="h-3.5 w-3.5" />
{loading ? "…" : "Receipt"}
</button>
)
}