Deploy on DigitalOcean App Platform (GitHub-source build) + consolidate audit-fixes
Deploy config: - .do/app.yaml: build the Dockerfile directly from GitHub (deploy_on_push) instead of a pre-built DOCR image; NEXT_PUBLIC_* set RUN_AND_BUILD_TIME with the propertymanagement.network domain so they bake into the client bundle; add custom domains block (apex + www); wire Sentry DSN (server + browser). Included pending work from the audit-fixes branch: - AI provider abstraction (OpenAI/Anthropic, admin-selectable; Anthropic default) - Per-landlord e-signature (DocuSign OAuth + Dropbox Sign) + migration 0010 - Outbound webhooks / Zapier integration - PayPal removal (Stripe-only billing) - Storage hardening (fail-loud when Spaces unconfigured), security fixes Verified: full production Docker build (same build-args as DO) passes clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
917a06ee85
commit
5495b94924
@@ -1,8 +1,9 @@
|
||||
"use client"
|
||||
|
||||
import { useTransition } from "react"
|
||||
import Link from "next/link"
|
||||
import { toast } from "sonner"
|
||||
import { PenLine, CheckCircle2, Clock, XCircle, AlertTriangle } from "lucide-react"
|
||||
import { PenLine, CheckCircle2, Clock, XCircle, AlertTriangle, Download } from "lucide-react"
|
||||
import { formatDate } from "@/lib/utils"
|
||||
import { sendLeaseForSignatureAction } from "@/app/actions/esign"
|
||||
|
||||
@@ -12,11 +13,12 @@ type Req = {
|
||||
status: string
|
||||
signer_email: string
|
||||
document_name: string | null
|
||||
signed_document_url: string | null
|
||||
sent_at: string | null
|
||||
completed_at: string | null
|
||||
last_error: string | null
|
||||
}
|
||||
type Prov = { id: string; label: string; configured: boolean }
|
||||
type Prov = { id: string; label: string }
|
||||
|
||||
const STATUS: Record<string, { label: string; cls: string; Icon: typeof Clock }> = {
|
||||
sent: { label: "Awaiting signature", cls: "text-amber-400 bg-amber-500/10 border-amber-500/20", Icon: Clock },
|
||||
@@ -29,18 +31,17 @@ const PROVIDER_LABEL: Record<string, string> = { docusign: "DocuSign", dropbox_s
|
||||
|
||||
export function EsignLease({
|
||||
leaseId,
|
||||
providers,
|
||||
connected,
|
||||
requests,
|
||||
canSend,
|
||||
disabledReason,
|
||||
}: {
|
||||
leaseId: string
|
||||
providers: Prov[]
|
||||
connected: Prov[]
|
||||
requests: Req[]
|
||||
canSend: boolean
|
||||
disabledReason: string
|
||||
}) {
|
||||
const configured = providers.filter((p) => p.configured)
|
||||
const [pending, start] = useTransition()
|
||||
|
||||
function send(provider: string) {
|
||||
@@ -74,6 +75,16 @@ export function EsignLease({
|
||||
{r.completed_at ? ` · Signed ${formatDate(r.completed_at)}` : ""}
|
||||
{r.status === "error" && r.last_error ? ` · ${r.last_error}` : ""}
|
||||
</p>
|
||||
{r.signed_document_url && (
|
||||
<a
|
||||
href={r.signed_document_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="mt-1 inline-flex items-center gap-1 text-[11px] text-indigo-400 transition hover:text-indigo-300"
|
||||
>
|
||||
<Download className="h-3 w-3" /> Signed document
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<span className={`flex shrink-0 items-center gap-1 rounded-full border px-2 py-0.5 text-[10px] font-medium ${s.cls}`}>
|
||||
<s.Icon className="h-3 w-3" /> {s.label}
|
||||
@@ -84,11 +95,16 @@ export function EsignLease({
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{configured.length === 0 ? (
|
||||
<p className="text-xs text-white/30">Configure DocuSign or Dropbox Sign on the server to send leases for e-signature.</p>
|
||||
{connected.length === 0 ? (
|
||||
<p className="text-xs text-white/30">
|
||||
<Link href="/settings/integrations" className="text-indigo-400 hover:text-indigo-300">
|
||||
Connect DocuSign or Dropbox Sign
|
||||
</Link>{" "}
|
||||
in Settings → Integrations to send leases for signature.
|
||||
</p>
|
||||
) : canSend ? (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{configured.map((p) => (
|
||||
{connected.map((p) => (
|
||||
<button
|
||||
key={p.id}
|
||||
onClick={() => send(p.id)}
|
||||
|
||||
Reference in New Issue
Block a user