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
@@ -1,54 +1,39 @@
|
||||
"use client"
|
||||
|
||||
import { motion } from "framer-motion"
|
||||
import {
|
||||
CreditCard, Mail, Cloud, Zap, BookOpen, MessageSquare,
|
||||
BarChart3, Archive, FileText, Smartphone, Lock, RefreshCw,
|
||||
} from "lucide-react"
|
||||
import { CreditCard, LogIn } from "lucide-react"
|
||||
|
||||
const TOOLS_ROW1 = [
|
||||
{ name: "Stripe", icon: CreditCard, color: "text-violet-400", bg: "bg-violet-500/10 border-violet-500/20", desc: "Payments" },
|
||||
{ name: "Gmail", icon: Mail, color: "text-red-400", bg: "bg-red-500/10 border-red-500/20", desc: "Email" },
|
||||
{ name: "Google Drive", icon: Cloud, color: "text-blue-400", bg: "bg-blue-500/10 border-blue-500/20", desc: "Storage" },
|
||||
{ name: "Zapier", icon: Zap, color: "text-orange-400", bg: "bg-orange-500/10 border-orange-500/20", desc: "Automation" },
|
||||
{ name: "Xero", icon: BarChart3, color: "text-cyan-400", bg: "bg-cyan-500/10 border-cyan-500/20", desc: "Accounting" },
|
||||
{ name: "Dropbox", icon: Archive, color: "text-blue-300", bg: "bg-blue-400/10 border-blue-400/20", desc: "Documents" },
|
||||
// Only integrations that genuinely exist in the product are listed here.
|
||||
// - Stripe: real payment processing (checkout + rent collection).
|
||||
// - Google: real sign-in via OAuth (Better Auth Google provider).
|
||||
// We deliberately do NOT advertise connectors we haven't built.
|
||||
const TOOLS = [
|
||||
{ name: "Stripe", icon: CreditCard, color: "text-violet-400", bg: "bg-violet-500/10 border-violet-500/20", desc: "Payments & rent collection" },
|
||||
{ name: "Google", icon: LogIn, color: "text-blue-400", bg: "bg-blue-500/10 border-blue-500/20", desc: "Sign in with Google (OAuth)" },
|
||||
]
|
||||
|
||||
const TOOLS_ROW2 = [
|
||||
{ name: "Slack", icon: MessageSquare, color: "text-emerald-400", bg: "bg-emerald-500/10 border-emerald-500/20", desc: "Notifications" },
|
||||
{ name: "Notion", icon: FileText, color: "text-white/70", bg: "bg-white/5 border-white/10", desc: "Notes" },
|
||||
{ name: "QuickBooks", icon: BookOpen, color: "text-green-400", bg: "bg-green-500/10 border-green-500/20", desc: "Accounting" },
|
||||
{ name: "WhatsApp", icon: Smartphone, color: "text-emerald-300", bg: "bg-emerald-400/10 border-emerald-400/20", desc: "Reminders" },
|
||||
{ name: "2FA / Auth", icon: Lock, color: "text-indigo-400", bg: "bg-indigo-500/10 border-indigo-500/20", desc: "Security" },
|
||||
{ name: "Auto-sync", icon: RefreshCw, color: "text-amber-400", bg: "bg-amber-500/10 border-amber-500/20", desc: "Sync" },
|
||||
]
|
||||
|
||||
function ToolBadge({ name, icon: Icon, color, bg, desc }: typeof TOOLS_ROW1[0]) {
|
||||
function ToolBadge({ name, icon: Icon, color, bg, desc }: typeof TOOLS[0]) {
|
||||
return (
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.05, y: -2 }}
|
||||
whileHover={{ scale: 1.03, y: -2 }}
|
||||
transition={{ type: "spring", stiffness: 300 }}
|
||||
className={`flex shrink-0 items-center gap-3 rounded-xl border px-4 py-3 ${bg} cursor-default mx-3`}
|
||||
className={`flex items-center gap-3 rounded-xl border px-5 py-4 ${bg} cursor-default`}
|
||||
>
|
||||
<div className={`flex h-8 w-8 items-center justify-center rounded-lg bg-black/20`}>
|
||||
<Icon className={`h-4 w-4 ${color}`} />
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-black/20">
|
||||
<Icon className={`h-5 w-5 ${color}`} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-white">{name}</p>
|
||||
<p className="text-[10px] text-white/40">{desc}</p>
|
||||
<p className="text-sm font-semibold text-white">{name}</p>
|
||||
<p className="text-xs text-white/40">{desc}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Integrations() {
|
||||
const doubled1 = [...TOOLS_ROW1, ...TOOLS_ROW1]
|
||||
const doubled2 = [...TOOLS_ROW2, ...TOOLS_ROW2]
|
||||
|
||||
return (
|
||||
<section className="py-14 sm:py-20 overflow-hidden">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6 mb-10 sm:mb-12">
|
||||
<div className="mx-auto max-w-3xl px-4 sm:px-6">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
@@ -56,50 +41,23 @@ export function Integrations() {
|
||||
className="text-center"
|
||||
>
|
||||
<p className="text-xs font-semibold uppercase tracking-widest text-indigo-400 mb-3">Integrations</p>
|
||||
<h2 className="text-3xl font-bold text-white sm:text-4xl">Works with tools you already use</h2>
|
||||
<h2 className="text-3xl font-bold text-white sm:text-4xl">
|
||||
Works with the tools you already use
|
||||
</h2>
|
||||
<p className="mt-4 text-white/50 max-w-xl mx-auto">
|
||||
Property Management Network connects with your existing stack — from payments to accounting to communication.
|
||||
Take rent payments with Stripe and let landlords sign in with Google — no extra setup, no fragile connectors.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 16 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="mt-10 grid gap-4 sm:grid-cols-2 max-w-xl mx-auto"
|
||||
>
|
||||
{TOOLS.map((t) => <ToolBadge key={t.name} {...t} />)}
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Marquee rows */}
|
||||
<div className="space-y-4">
|
||||
{/* Row 1 — left to right */}
|
||||
<div className="relative flex overflow-hidden">
|
||||
<div className="pointer-events-none absolute left-0 top-0 z-10 h-full w-32 bg-gradient-to-r from-[#09090b] to-transparent" />
|
||||
<div className="pointer-events-none absolute right-0 top-0 z-10 h-full w-32 bg-gradient-to-l from-[#09090b] to-transparent" />
|
||||
<motion.div
|
||||
animate={{ x: ["0%", "-50%"] }}
|
||||
transition={{ duration: 22, ease: "linear", repeat: Infinity }}
|
||||
className="flex"
|
||||
>
|
||||
{doubled1.map((t, i) => <ToolBadge key={i} {...t} />)}
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Row 2 — right to left */}
|
||||
<div className="relative flex overflow-hidden">
|
||||
<div className="pointer-events-none absolute left-0 top-0 z-10 h-full w-32 bg-gradient-to-r from-[#09090b] to-transparent" />
|
||||
<div className="pointer-events-none absolute right-0 top-0 z-10 h-full w-32 bg-gradient-to-l from-[#09090b] to-transparent" />
|
||||
<motion.div
|
||||
animate={{ x: ["-50%", "0%"] }}
|
||||
transition={{ duration: 25, ease: "linear", repeat: Infinity }}
|
||||
className="flex"
|
||||
>
|
||||
{doubled2.map((t, i) => <ToolBadge key={i} {...t} />)}
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<motion.p
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
className="mt-8 text-center text-xs text-white/30"
|
||||
>
|
||||
+ more integrations via Zapier webhooks
|
||||
</motion.p>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user