diff --git a/canvas/src/components/ApprovalBanner.tsx b/canvas/src/components/ApprovalBanner.tsx index e9baa776..7c33d9b2 100644 --- a/canvas/src/components/ApprovalBanner.tsx +++ b/canvas/src/components/ApprovalBanner.tsx @@ -16,6 +16,8 @@ interface PendingApproval { export function ApprovalBanner() { const [approvals, setApprovals] = useState([]); + // Guards double-click / double-keypress during in-flight POST. + const [pendingApprovalId, setPendingApprovalId] = useState(null); // Single endpoint — no N+1 per-workspace polling const pollApprovals = useCallback(async () => { @@ -35,6 +37,8 @@ export function ApprovalBanner() { }, [pollApprovals]); const handleDecide = async (approval: PendingApproval, decision: "approved" | "denied") => { + if (pendingApprovalId !== null) return; // guard double-submit + setPendingApprovalId(approval.id); try { await api.post(`/workspaces/${approval.workspace_id}/approvals/${approval.id}/decide`, { decision, @@ -44,6 +48,8 @@ export function ApprovalBanner() { setApprovals((prev) => prev.filter((a) => a.id !== approval.id)); } catch { showToast("Failed to submit decision", "error"); + } finally { + setPendingApprovalId(null); } }; @@ -72,22 +78,25 @@ export function ApprovalBanner() {
diff --git a/canvas/src/components/ConfirmDialog.tsx b/canvas/src/components/ConfirmDialog.tsx index 9e799c5a..59cfddf2 100644 --- a/canvas/src/components/ConfirmDialog.tsx +++ b/canvas/src/components/ConfirmDialog.tsx @@ -98,7 +98,7 @@ export function ConfirmDialog({ confirmVariant === "danger" ? "bg-red-600 hover:bg-red-700 text-white" : confirmVariant === "warning" - ? "bg-amber-600 hover:bg-amber-700 text-white" + ? "bg-amber-800 hover:bg-amber-700 text-white" : "bg-accent hover:bg-accent-strong text-white"; // Render via Portal so the fixed-position dialog escapes any containing block diff --git a/canvas/src/components/OrgImportPreflightModal.tsx b/canvas/src/components/OrgImportPreflightModal.tsx index 3a1b22ad..6bc4ea48 100644 --- a/canvas/src/components/OrgImportPreflightModal.tsx +++ b/canvas/src/components/OrgImportPreflightModal.tsx @@ -308,7 +308,7 @@ export function OrgImportPreflightModal({ type="button" onClick={onProceed} disabled={!canProceed} - className="px-4 py-1.5 text-[11px] font-semibold rounded bg-accent hover:bg-accent-strong text-white disabled:bg-surface-card disabled:text-white-soft disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1" + className="px-4 py-1.5 text-[11px] font-semibold rounded bg-accent hover:bg-accent-strong text-white disabled:bg-surface-card disabled:text-ink-soft disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1" > Import diff --git a/canvas/src/components/PricingTable.tsx b/canvas/src/components/PricingTable.tsx index 5f3bc210..1e400a47 100644 --- a/canvas/src/components/PricingTable.tsx +++ b/canvas/src/components/PricingTable.tsx @@ -117,7 +117,7 @@ function PlanCard({