Consolidate audit-fixes branch: webhooks, integrations, and deploy hardening
Batch commit of the pending working tree on security/audit-fixes-2026-07. Major areas: - Outbound webhooks / Zapier: schema + signed delivery with retries, public v1 API (REST-hook subscribe/unsubscribe), settings UI, cron drain. - Deploy hardening: email via SMTP2GO (Resend fully removed), verified DB TLS (DATABASE_SSL=require + DATABASE_CA), storage fails loud in production when Spaces is unconfigured instead of silently using ephemeral disk. - Integrations & features (concurrent work): accounting (QuickBooks/Xero), e-signature (DocuSign/Dropbox Sign), PayPal, geocoding/maps, onboarding, expanded legal pages. - DB migrations 0006–0009. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
969d5d4c8a
commit
c9968531e4
@@ -4,10 +4,16 @@ import { db } from "@/lib/db"
|
||||
import { tenants, rent_payments, maintenance_requests, leases } from "@/lib/db/schema"
|
||||
import { formatCurrency, formatDate } from "@/lib/utils"
|
||||
import { Home, DollarSign, Wrench, FileText } from "lucide-react"
|
||||
import { getBranding } from "@/lib/branding"
|
||||
import { TenantMaintenanceForm } from "./tenant-maintenance-form"
|
||||
|
||||
export const dynamic = "force-dynamic"
|
||||
|
||||
export const metadata = {
|
||||
title: "Tenant Portal",
|
||||
robots: { index: false, follow: false },
|
||||
}
|
||||
|
||||
export default async function TenantPortalPage({ params }: { params: Promise<{ token: string }> }) {
|
||||
const { token } = await params
|
||||
|
||||
@@ -26,6 +32,10 @@ export default async function TenantPortalPage({ params }: { params: Promise<{ t
|
||||
|
||||
if (!tenant) notFound()
|
||||
|
||||
// White-label: apply the landlord's (portal owner's) branding, gated by plan.
|
||||
const branding = await getBranding(tenant.user_id)
|
||||
const accent = branding.enabled && branding.color ? branding.color : null
|
||||
|
||||
const property = tenant.unit?.property
|
||||
const unit = tenant.unit
|
||||
|
||||
@@ -89,11 +99,21 @@ export default async function TenantPortalPage({ params }: { params: Promise<{ t
|
||||
{/* Header */}
|
||||
<div className="rounded-xl border border-white/[0.06] bg-[#16161f] p-6">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-indigo-600">
|
||||
<Home className="h-5 w-5 text-white" />
|
||||
<div
|
||||
className="flex h-10 w-10 items-center justify-center overflow-hidden rounded-xl bg-indigo-600"
|
||||
style={accent ? { backgroundColor: accent } : undefined}
|
||||
>
|
||||
{branding.enabled && branding.logoUrl ? (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img src={branding.logoUrl} alt={branding.brandName ?? "Logo"} className="h-full w-full object-contain p-1" />
|
||||
) : (
|
||||
<Home className="h-5 w-5 text-white" />
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-white/40">Tenant Portal</p>
|
||||
<p className="text-xs text-white/40">
|
||||
{branding.enabled && branding.brandName ? branding.brandName : "Tenant Portal"}
|
||||
</p>
|
||||
<h1 className="text-lg font-bold text-white">
|
||||
{tenant.first_name} {tenant.last_name}
|
||||
</h1>
|
||||
@@ -117,7 +137,7 @@ export default async function TenantPortalPage({ params }: { params: Promise<{ t
|
||||
{lease && (
|
||||
<div className="rounded-xl border border-white/[0.06] bg-[#16161f] overflow-hidden">
|
||||
<div className="flex items-center gap-3 border-b border-white/[0.06] px-5 py-4">
|
||||
<FileText className="h-4 w-4 text-indigo-400" />
|
||||
<FileText className="h-4 w-4 text-indigo-400" style={accent ? { color: accent } : undefined} />
|
||||
<h2 className="text-sm font-semibold text-white">Current Lease</h2>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4 p-5 sm:grid-cols-4">
|
||||
@@ -139,7 +159,7 @@ export default async function TenantPortalPage({ params }: { params: Promise<{ t
|
||||
{/* Rent Payments */}
|
||||
<div className="rounded-xl border border-white/[0.06] bg-[#16161f] overflow-hidden">
|
||||
<div className="flex items-center gap-3 border-b border-white/[0.06] px-5 py-4">
|
||||
<DollarSign className="h-4 w-4 text-indigo-400" />
|
||||
<DollarSign className="h-4 w-4 text-indigo-400" style={accent ? { color: accent } : undefined} />
|
||||
<h2 className="text-sm font-semibold text-white">Rent History</h2>
|
||||
</div>
|
||||
{!payments?.length ? (
|
||||
@@ -167,7 +187,7 @@ export default async function TenantPortalPage({ params }: { params: Promise<{ t
|
||||
{/* Maintenance */}
|
||||
<div className="rounded-xl border border-white/[0.06] bg-[#16161f] overflow-hidden">
|
||||
<div className="flex items-center gap-3 border-b border-white/[0.06] px-5 py-4">
|
||||
<Wrench className="h-4 w-4 text-indigo-400" />
|
||||
<Wrench className="h-4 w-4 text-indigo-400" style={accent ? { color: accent } : undefined} />
|
||||
<h2 className="text-sm font-semibold text-white">Maintenance Requests</h2>
|
||||
</div>
|
||||
|
||||
@@ -195,7 +215,9 @@ export default async function TenantPortalPage({ params }: { params: Promise<{ t
|
||||
</div>
|
||||
|
||||
<p className="text-center text-xs text-white/20">
|
||||
Powered by Property Management Network · This is a private link for your use only
|
||||
{branding.enabled && branding.hidePoweredBy
|
||||
? "This is a private link for your use only"
|
||||
: "Powered by Property Management Network · This is a private link for your use only"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user