Convert the bundled backend/internal/plugins/calcomblock package into a standalone reactor-mode wasm plugin, dropping every git.dev.alexdunmow.com/block/cms import (forbidden in standalone plugins): - package calcomblock -> package main; add wasip1 main.go with wasmguest.Serve(Registration). go.mod module git.dev.alexdunmow.com/block/calcomblock, block/core v0.18.2, no replace directives. - Settings persistence: replace the pool-backed poolQuerier (direct SQL against the public-schema `settings` table, which a sandboxed plugin role cannot read) with capabilityQuerier over the SDK settings.Settings / settings.Updater capabilities. GetSiteTimezone now resolves via GetSiteSettings host-side. - Vendor the small CMS-internal helpers the plugin used into internal/helpers (GetRealIP, StartCleanupLoop, MaskSecret, GetStringOr, the PluginSettingsQuerier settings helpers, PluginCrypto for tests) and internal/db (minimal Setting / UpsertSettingParams), each with a provenance header. - Inline the captcha widget: vendor blocks.CaptchaWidget as a local CaptchaWidget templ (captcha_widget.templ) and regenerate templ; drop the block/cms/blocks import from booking.templ. - blockConfig (server-authoritative captcha requirement via a page_block_snapshots scan) has no wasm-ABI capability, so it is left nil: honeypot + per-IP rate limit still apply. Documented as a follow-up. - web: @block-ninja/ui workspace:* -> ^0.1.0 registry version; eslint.config.js repointed at ../../../cms/web/eslint.config.js; rebuild web/dist. - Rewrite CLAUDE.md for the standalone wasm reality; add .gitignore. Full test suite preserved and passing; builds to calcomblock-2.0.0.bnp; check-safety passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
620 lines
25 KiB
JavaScript
620 lines
25 KiB
JavaScript
import { importShared } from "./__federation_fn_import-hlt2XzeI.js";
|
|
import { j as jsxRuntimeExports } from "./jsx-runtime-CvJTHeKY.js";
|
|
|
|
const { useState, useCallback, useEffect } = await importShared("react");
|
|
|
|
const {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
Input,
|
|
Label,
|
|
Button,
|
|
Alert,
|
|
AlertDescription,
|
|
Badge,
|
|
ScrollArea,
|
|
Loader2,
|
|
CheckCircle2,
|
|
XCircle,
|
|
AlertCircle,
|
|
Eye,
|
|
EyeOff,
|
|
Key,
|
|
Zap,
|
|
Settings,
|
|
Send,
|
|
Copy,
|
|
RefreshCw,
|
|
toast,
|
|
t,
|
|
useAlertDialog,
|
|
} = await importShared("@block-ninja/ui");
|
|
|
|
const copyToClipboard = async (text) => {
|
|
if (!text) return;
|
|
try {
|
|
await navigator.clipboard.writeText(text);
|
|
toast.success(t("calcom.settings.copied", "Copied to clipboard"));
|
|
} catch (_error) {
|
|
toast.error(t("calcom.settings.copyFailed", "Could not copy to clipboard"));
|
|
}
|
|
};
|
|
const formatRelativeTime = (iso) => {
|
|
const parsed = Date.parse(iso);
|
|
if (Number.isNaN(parsed)) return iso;
|
|
const diffMs = Date.now() - parsed;
|
|
const minutes = Math.round(diffMs / 6e4);
|
|
if (minutes < 1) return t("calcom.settings.relative.justNow", "just now");
|
|
if (minutes < 60) {
|
|
return minutes === 1 ? t("calcom.settings.relative.minute", "1 minute ago") : t("calcom.settings.relative.minutes", "{count} minutes ago").replace("{count}", String(minutes));
|
|
}
|
|
const hours = Math.round(minutes / 60);
|
|
if (hours < 24) {
|
|
return hours === 1 ? t("calcom.settings.relative.hour", "1 hour ago") : t("calcom.settings.relative.hours", "{count} hours ago").replace("{count}", String(hours));
|
|
}
|
|
const days = Math.round(hours / 24);
|
|
return days === 1 ? t("calcom.settings.relative.day", "1 day ago") : t("calcom.settings.relative.days", "{count} days ago").replace("{count}", String(days));
|
|
};
|
|
function CalcomSettings({ pluginName }) {
|
|
const { confirm } = useAlertDialog();
|
|
const [apiKey, setApiKey] = useState("");
|
|
const [showApiKey, setShowApiKey] = useState(false);
|
|
const [loading, setLoading] = useState(true);
|
|
const [saving, setSaving] = useState(false);
|
|
const [testing, setTesting] = useState(false);
|
|
const [rotating, setRotating] = useState(false);
|
|
const [showSecret, setShowSecret] = useState(false);
|
|
const [settings, setSettings] = useState(null);
|
|
const [testResult, setTestResult] = useState(null);
|
|
const [saveMessage, setSaveMessage] = useState(null);
|
|
const baseUrl = `/api/plugins/${pluginName}`;
|
|
const loadSettings = useCallback(async () => {
|
|
try {
|
|
const res = await fetch(`${baseUrl}/settings`);
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
setSettings(data);
|
|
}
|
|
} catch (_error) {
|
|
console.error("Failed to load settings");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [baseUrl]);
|
|
useEffect(() => {
|
|
loadSettings();
|
|
}, [loadSettings]);
|
|
const handleSave = async () => {
|
|
if (!apiKey.trim()) {
|
|
setSaveMessage({ type: "error", text: t("calcom.settings.apiKey.errorEmpty", "Please enter an API key") });
|
|
return;
|
|
}
|
|
setSaving(true);
|
|
setSaveMessage(null);
|
|
setTestResult(null);
|
|
try {
|
|
const res = await fetch(`${baseUrl}/settings`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ api_key: apiKey }),
|
|
});
|
|
if (res.ok) {
|
|
await loadSettings();
|
|
setApiKey("");
|
|
setSaveMessage({ type: "success", text: t("calcom.settings.apiKey.saved", "API key saved successfully") });
|
|
} else {
|
|
const text = await res.text();
|
|
setSaveMessage({ type: "error", text: text || t("calcom.settings.apiKey.saveFailed", "Failed to save API key") });
|
|
}
|
|
} catch (_error) {
|
|
setSaveMessage({ type: "error", text: t("calcom.settings.serverError", "Failed to connect to server") });
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
const handleTest = async () => {
|
|
setTesting(true);
|
|
setTestResult(null);
|
|
try {
|
|
const res = await fetch(`${baseUrl}/test`, { method: "POST" });
|
|
const data = await res.json();
|
|
setTestResult(data);
|
|
} catch (_error) {
|
|
setTestResult({ success: false, error: t("calcom.settings.serverError", "Failed to connect to server") });
|
|
} finally {
|
|
setTesting(false);
|
|
}
|
|
};
|
|
const handleClear = async () => {
|
|
setSaving(true);
|
|
setSaveMessage(null);
|
|
try {
|
|
const res = await fetch(`${baseUrl}/settings`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ api_key: "" }),
|
|
});
|
|
if (res.ok) {
|
|
await loadSettings();
|
|
setTestResult(null);
|
|
setSaveMessage({ type: "success", text: t("calcom.settings.apiKey.removed", "API key removed") });
|
|
}
|
|
} catch (_error) {
|
|
setSaveMessage({ type: "error", text: t("calcom.settings.apiKey.clearFailed", "Failed to clear API key") });
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
const handleRotate = async () => {
|
|
const confirmed = await confirm({
|
|
title: t("calcom.settings.webhook.rotateConfirmTitle", "Rotate webhook secret?"),
|
|
message: t("calcom.settings.webhook.rotateConfirmMessage", "This invalidates the existing secret immediately. You'll need to update Cal.com to keep webhooks flowing."),
|
|
confirmLabel: t("calcom.settings.webhook.rotateConfirmLabel", "Rotate"),
|
|
variant: "destructive",
|
|
});
|
|
if (!confirmed) return;
|
|
setRotating(true);
|
|
setSaveMessage(null);
|
|
try {
|
|
const res = await fetch(`${baseUrl}/settings/rotate-webhook-secret`, { method: "POST" });
|
|
if (res.ok) {
|
|
const data = await res.json();
|
|
setSettings((prev) => (prev ? { ...prev, webhook_secret_masked: data.webhook_secret_masked, webhook_secret_configured: true } : prev));
|
|
const successMsg = t("calcom.settings.webhook.rotated", "Webhook secret rotated. Update it in Cal.com.");
|
|
setSaveMessage({ type: "success", text: successMsg });
|
|
toast.success(successMsg);
|
|
} else {
|
|
setSaveMessage({ type: "error", text: t("calcom.settings.webhook.rotateFailed", "Failed to rotate webhook secret") });
|
|
}
|
|
} catch (_error) {
|
|
setSaveMessage({ type: "error", text: t("calcom.settings.serverError", "Failed to connect to server") });
|
|
} finally {
|
|
setRotating(false);
|
|
}
|
|
};
|
|
if (loading) {
|
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", {
|
|
className: "flex items-center justify-center py-8",
|
|
children: /* @__PURE__ */ jsxRuntimeExports.jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }),
|
|
});
|
|
}
|
|
const apiKeyVisibilityLabel = showApiKey ? t("calcom.settings.toggleVisibility.hide", "Hide") : t("calcom.settings.toggleVisibility.show", "Show");
|
|
const secretVisibilityLabel = showSecret ? t("calcom.settings.toggleVisibility.hide", "Hide") : t("calcom.settings.toggleVisibility.show", "Show");
|
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "space-y-6",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("h2", {
|
|
className: "text-xl font-semibold flex items-center gap-2",
|
|
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { className: "h-5 w-5" }), t("calcom.settings.heading", "Cal.com Integration")],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", {
|
|
className: "text-sm text-muted-foreground",
|
|
children: t("calcom.settings.headingDescription", "Configure your Cal.com API key to enable booking functionality"),
|
|
}),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Card, {
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, {
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardTitle, {
|
|
className: "text-base flex items-center gap-2",
|
|
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Key, { className: "h-4 w-4" }), t("calcom.settings.apiKey.title", "API Key Status")],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, {
|
|
children: t("calcom.settings.apiKey.description", "Your Cal.com API key is required for the booking widget to function"),
|
|
}),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, {
|
|
className: "space-y-4",
|
|
children: [
|
|
settings?.api_key_configured ?
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "flex items-center justify-between rounded-lg border bg-muted/30 p-3",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "flex items-center gap-3",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, {
|
|
variant: "default",
|
|
className: "gap-1.5",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CheckCircle2, { className: "h-3.5 w-3.5" }),
|
|
t("calcom.settings.apiKey.configuredLabel", "Configured"),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", {
|
|
className: "text-xs text-muted-foreground font-mono",
|
|
children: settings.api_key_masked || t("calcom.settings.apiKey.maskedFallback", "***configured***"),
|
|
}),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, {
|
|
action: "delete",
|
|
entity: "plugin-setting",
|
|
variant: "ghost",
|
|
size: "sm",
|
|
onClick: handleClear,
|
|
disabled: saving,
|
|
children: t("calcom.settings.apiKey.remove", "Remove"),
|
|
}),
|
|
],
|
|
})
|
|
: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "flex items-center gap-3 rounded-lg border bg-muted/30 p-3",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, {
|
|
variant: "outline",
|
|
className: "gap-1.5",
|
|
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(XCircle, { className: "h-3.5 w-3.5" }), t("calcom.settings.apiKey.notConfiguredLabel", "Not configured")],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", {
|
|
className: "text-xs text-muted-foreground",
|
|
children: t("calcom.settings.apiKey.notConfiguredHelp", "Enter your Cal.com API key below to enable booking"),
|
|
}),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "space-y-2",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Label, {
|
|
htmlFor: "apiKey",
|
|
children: settings?.api_key_configured ? t("calcom.settings.apiKey.updateLabel", "Update API Key") : t("calcom.settings.apiKey.label", "API Key"),
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "flex gap-2",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "relative flex-1",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Input, {
|
|
id: "apiKey",
|
|
type: showApiKey ? "text" : "password",
|
|
value: apiKey,
|
|
onChange: (e) => setApiKey(e.target.value),
|
|
placeholder: t("calcom.settings.apiKey.placeholder", "cal_live_..."),
|
|
className: "pr-10",
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, {
|
|
"type": "button",
|
|
"variant": "ghost",
|
|
"action": "toggle",
|
|
"entity": "api-key-visibility",
|
|
"onClick": () => setShowApiKey(!showApiKey),
|
|
"aria-label": apiKeyVisibilityLabel,
|
|
"className": "absolute right-3 top-1/2 -translate-y-1/2 h-auto p-0 text-muted-foreground hover:bg-transparent hover:text-foreground",
|
|
"children":
|
|
showApiKey ?
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { className: "h-4 w-4" })
|
|
: /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "h-4 w-4" }),
|
|
}),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, {
|
|
action: "save",
|
|
entity: "plugin-setting",
|
|
onClick: handleSave,
|
|
disabled: saving || !apiKey.trim(),
|
|
children: saving ? /* @__PURE__ */ jsxRuntimeExports.jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : t("calcom.settings.apiKey.save", "Save"),
|
|
}),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", {
|
|
className: "text-xs text-muted-foreground",
|
|
children: [
|
|
t("calcom.settings.apiKey.helpPrefix", "Get your API key from"),
|
|
" ",
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("a", {
|
|
href: "https://app.cal.com/settings/developer/api-keys",
|
|
target: "_blank",
|
|
rel: "noopener noreferrer",
|
|
className: "text-primary hover:underline",
|
|
children: t("calcom.settings.apiKey.helpLink", "Cal.com API Settings"),
|
|
}),
|
|
],
|
|
}),
|
|
],
|
|
}),
|
|
saveMessage &&
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Alert, {
|
|
variant: saveMessage.type === "error" ? "destructive" : "default",
|
|
children: /* @__PURE__ */ jsxRuntimeExports.jsx(AlertDescription, { children: saveMessage.text }),
|
|
}),
|
|
],
|
|
}),
|
|
],
|
|
}),
|
|
settings?.api_key_configured &&
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Card, {
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, {
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardTitle, {
|
|
className: "text-base flex items-center gap-2",
|
|
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "h-4 w-4" }), t("calcom.settings.test.title", "Test Connection")],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { children: t("calcom.settings.test.description", "Verify your API key works and see available event types") }),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, {
|
|
className: "space-y-4",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, {
|
|
action: "settings",
|
|
entity: "plugin-connection",
|
|
onClick: handleTest,
|
|
disabled: testing,
|
|
children:
|
|
testing ?
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
|
|
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), t("calcom.settings.test.testing", "Testing...")],
|
|
})
|
|
: t("calcom.settings.test.button", "Test Connection"),
|
|
}),
|
|
testResult &&
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", {
|
|
className: "space-y-3",
|
|
children:
|
|
testResult.success ?
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Alert, {
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CheckCircle2, { className: "h-4 w-4 text-success" }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(AlertDescription, {
|
|
className: "ml-2",
|
|
children: [
|
|
t("calcom.settings.test.successPrefix", "Connection successful! Found"),
|
|
" ",
|
|
testResult.event_types?.length || 0,
|
|
" ",
|
|
t("calcom.settings.test.successSuffix", "event types."),
|
|
],
|
|
}),
|
|
],
|
|
}),
|
|
testResult.event_types &&
|
|
testResult.event_types.length > 0 &&
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, {
|
|
className: "max-h-96 rounded-lg border",
|
|
children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", {
|
|
className: "w-full text-sm",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("thead", {
|
|
className: "bg-muted",
|
|
children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", {
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("th", {
|
|
className: "text-left px-3 py-2 font-medium",
|
|
children: t("calcom.settings.test.tableEventType", "Event Type"),
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("th", {
|
|
className: "text-left px-3 py-2 font-medium",
|
|
children: t("calcom.settings.test.tableSlug", "Slug"),
|
|
}),
|
|
],
|
|
}),
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("tbody", {
|
|
children: testResult.event_types.map((et) =>
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
"tr",
|
|
{
|
|
className: "border-t",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2", children: et.title }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("td", {
|
|
className: "px-3 py-2 font-mono text-xs",
|
|
children: et.slug,
|
|
}),
|
|
],
|
|
},
|
|
et.id,
|
|
),
|
|
),
|
|
}),
|
|
],
|
|
}),
|
|
}),
|
|
],
|
|
})
|
|
: /* @__PURE__ */ jsxRuntimeExports.jsxs(Alert, {
|
|
variant: "destructive",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(XCircle, { className: "h-4 w-4" }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(AlertDescription, {
|
|
className: "ml-2",
|
|
children: testResult.error || t("calcom.settings.test.failure", "Connection failed"),
|
|
}),
|
|
],
|
|
}),
|
|
}),
|
|
],
|
|
}),
|
|
],
|
|
}),
|
|
settings?.api_key_configured &&
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Card, {
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, {
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardTitle, {
|
|
className: "text-base flex items-center gap-2",
|
|
children: [/* @__PURE__ */ jsxRuntimeExports.jsx(Send, { className: "h-4 w-4" }), t("calcom.settings.webhook.title", "Webhook")],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, {
|
|
children: t("calcom.settings.webhook.description", "Configure this URL in Cal.com → Settings → Developer → Webhooks to receive booking events."),
|
|
}),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, {
|
|
className: "space-y-4",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "space-y-2",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "webhookUrl", children: t("calcom.settings.webhook.urlLabel", "Webhook URL") }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "flex gap-2",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Input, {
|
|
id: "webhookUrl",
|
|
readOnly: true,
|
|
value: settings?.webhook_url || "",
|
|
className: "flex-1 font-mono text-xs",
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, {
|
|
"action": "copy",
|
|
"entity": "webhook-url",
|
|
"variant": "outline",
|
|
"size": "sm",
|
|
"aria-label": t("calcom.settings.webhook.copyUrl", "Copy webhook URL"),
|
|
"onClick": () => copyToClipboard(settings?.webhook_url || ""),
|
|
"children": /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "h-4 w-4" }),
|
|
}),
|
|
],
|
|
}),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "space-y-2",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Label, { htmlFor: "webhookSecret", children: t("calcom.settings.webhook.secretLabel", "Webhook Secret") }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "flex gap-2",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "relative flex-1",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Input, {
|
|
id: "webhookSecret",
|
|
type: showSecret ? "text" : "password",
|
|
readOnly: true,
|
|
value: settings?.webhook_secret_masked || t("calcom.settings.webhook.secretNotConfigured", "Not configured"),
|
|
className: "pr-10 font-mono text-xs",
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, {
|
|
"type": "button",
|
|
"variant": "ghost",
|
|
"action": "toggle",
|
|
"entity": "webhook-secret-visibility",
|
|
"onClick": () => setShowSecret(!showSecret),
|
|
"aria-label": secretVisibilityLabel,
|
|
"className": "absolute right-3 top-1/2 -translate-y-1/2 h-auto p-0 text-muted-foreground hover:bg-transparent hover:text-foreground",
|
|
"children":
|
|
showSecret ?
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { className: "h-4 w-4" })
|
|
: /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "h-4 w-4" }),
|
|
}),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, {
|
|
"action": "copy",
|
|
"entity": "webhook-secret",
|
|
"variant": "outline",
|
|
"size": "sm",
|
|
"aria-label": t("calcom.settings.webhook.copySecret", "Copy webhook secret"),
|
|
"onClick": () => copyToClipboard(settings?.webhook_secret_masked || ""),
|
|
"children": /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "h-4 w-4" }),
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, {
|
|
"action": "rotate",
|
|
"entity": "webhook-secret",
|
|
"variant": "outline",
|
|
"size": "sm",
|
|
"aria-label": t("calcom.settings.webhook.rotate", "Rotate webhook secret"),
|
|
"onClick": handleRotate,
|
|
"disabled": rotating,
|
|
"children":
|
|
rotating ?
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Loader2, { className: "h-4 w-4 animate-spin" })
|
|
: /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "h-4 w-4" }),
|
|
}),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", {
|
|
className: "text-xs text-muted-foreground",
|
|
children: t(
|
|
"calcom.settings.webhook.secretHelp",
|
|
"Cal.com displays the secret only once. Regenerating invalidates the previous value immediately — update it in Cal.com after rotating.",
|
|
),
|
|
}),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", {
|
|
className: "text-sm",
|
|
children:
|
|
settings?.last_event_at ?
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "flex items-center gap-2 text-foreground",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CheckCircle2, { className: "h-4 w-4 text-success" }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", {
|
|
children: [
|
|
t("calcom.settings.webhook.lastEventPrefix", "Last event"),
|
|
" ",
|
|
formatRelativeTime(settings.last_event_at),
|
|
settings.last_event_type &&
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
|
|
children: [
|
|
" — ",
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "font-mono text-xs", children: settings.last_event_type }),
|
|
],
|
|
}),
|
|
],
|
|
}),
|
|
],
|
|
})
|
|
: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", {
|
|
className: "flex items-center gap-2 text-muted-foreground",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(AlertCircle, { className: "h-4 w-4" }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("calcom.settings.webhook.waitingForEvent", "Status: Waiting for first event") }),
|
|
],
|
|
}),
|
|
}),
|
|
],
|
|
}),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Card, {
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardHeader, {
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CardTitle, { className: "text-base", children: t("calcom.settings.docs.title", "Documentation") }),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CardDescription, { children: t("calcom.settings.docs.description", "Reference material for working with the Cal.com plugin.") }),
|
|
],
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(CardContent, {
|
|
className: "space-y-2 text-sm",
|
|
children: [
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("a", {
|
|
href: "https://cal.com/docs/api-reference/v2",
|
|
target: "_blank",
|
|
rel: "noopener noreferrer",
|
|
className: "text-primary hover:underline block",
|
|
children: t("calcom.settings.docs.calcomApi", "Cal.com API documentation"),
|
|
}),
|
|
/* @__PURE__ */ jsxRuntimeExports.jsx("a", {
|
|
href: "/admin/docs/calcom-plugin",
|
|
target: "_blank",
|
|
rel: "noopener noreferrer",
|
|
className: "text-primary hover:underline block",
|
|
children: t("calcom.settings.docs.pluginGuide", "BlockNinja plugin guide"),
|
|
}),
|
|
],
|
|
}),
|
|
],
|
|
}),
|
|
],
|
|
});
|
|
}
|
|
|
|
export { CalcomSettings as default };
|