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 };