@invect/webhooks 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +79 -0
  2. package/dist/backend/index.cjs +12 -3
  3. package/dist/backend/index.cjs.map +1 -1
  4. package/dist/backend/index.d.cts +115 -0
  5. package/dist/backend/index.d.cts.map +1 -0
  6. package/dist/backend/index.d.mts +115 -0
  7. package/dist/backend/index.d.mts.map +1 -0
  8. package/dist/backend/index.d.ts +1 -1
  9. package/dist/backend/index.d.ts.map +1 -1
  10. package/dist/backend/index.mjs +12 -3
  11. package/dist/backend/index.mjs.map +1 -1
  12. package/dist/backend/plugin.d.ts +9 -2
  13. package/dist/backend/plugin.d.ts.map +1 -1
  14. package/dist/backend/webhook-dedup.service.d.ts.map +1 -1
  15. package/dist/backend/webhook-signature.service.d.ts.map +1 -1
  16. package/dist/frontend/components/CreateWebhookModal.d.ts.map +1 -1
  17. package/dist/frontend/components/WebhookDetailPanel.d.ts.map +1 -1
  18. package/dist/frontend/components/WebhooksPage.d.ts.map +1 -1
  19. package/dist/frontend/hooks/useWebhookQueries.d.ts +1 -1
  20. package/dist/frontend/hooks/useWebhookQueries.d.ts.map +1 -1
  21. package/dist/frontend/index.cjs +46 -46
  22. package/dist/frontend/index.cjs.map +1 -1
  23. package/dist/frontend/index.d.cts +70 -0
  24. package/dist/frontend/index.d.cts.map +1 -0
  25. package/dist/frontend/index.d.mts +70 -0
  26. package/dist/frontend/index.d.mts.map +1 -0
  27. package/dist/frontend/index.d.ts +2 -2
  28. package/dist/frontend/index.d.ts.map +1 -1
  29. package/dist/frontend/index.mjs +28 -28
  30. package/dist/frontend/index.mjs.map +1 -1
  31. package/dist/frontend/plugins/webhooksFrontendPlugin.d.ts +2 -2
  32. package/dist/frontend/plugins/webhooksFrontendPlugin.d.ts.map +1 -1
  33. package/dist/shared/types.d.cts +2 -0
  34. package/dist/shared/types.d.mts +2 -0
  35. package/dist/types-cBOT0AqR.d.cts +83 -0
  36. package/dist/types-cBOT0AqR.d.cts.map +1 -0
  37. package/dist/types-zH7xu7mA.d.mts +83 -0
  38. package/dist/types-zH7xu7mA.d.mts.map +1 -0
  39. package/package.json +49 -50
@@ -1,13 +1,13 @@
1
1
  import { Check, ChevronDown, ChevronRight, Clock, Copy, ExternalLink, Eye, EyeOff, Globe, Hash, Loader2, Plus, Search, ToggleLeft, ToggleRight, Trash2, Workflow } from "lucide-react";
2
2
  import { useRef, useState } from "react";
3
- import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, PageLayout, useApiBaseURL, useFlows } from "@invect/frontend";
3
+ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, PageLayout, useApiBaseURL, useFlows } from "@invect/ui";
4
4
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/frontend/hooks/useWebhookQueries.ts
7
7
  /**
8
8
  * React hooks for the webhooks plugin API.
9
9
  *
10
- * Uses @invect/frontend's ApiContext for the base URL.
10
+ * Uses @invect/ui's ApiContext for the base URL.
11
11
  */
12
12
  async function apiFetch(baseUrl, path, init) {
13
13
  const res = await fetch(`${baseUrl}${path}`, {
@@ -466,7 +466,7 @@ const WebhookDetailPanel = ({ trigger, flowName, onClose }) => {
466
466
  })]
467
467
  })]
468
468
  }) }), /* @__PURE__ */ jsx("div", {
469
- className: "flex gap-1 border-b -mx-6 px-6 mt-4",
469
+ className: "flex gap-1 px-6 mt-4 -mx-6 border-b",
470
470
  children: [{
471
471
  key: "overview",
472
472
  label: "Overview"
@@ -480,7 +480,7 @@ const WebhookDetailPanel = ({ trigger, flowName, onClose }) => {
480
480
  }, key))
481
481
  })]
482
482
  }), /* @__PURE__ */ jsx("div", {
483
- className: "flex-1 overflow-y-auto px-6 pb-6",
483
+ className: "flex-1 px-6 pb-6 overflow-y-auto",
484
484
  children: section === "overview" ? /* @__PURE__ */ jsx(OverviewSection, {
485
485
  trigger,
486
486
  flowName,
@@ -500,24 +500,24 @@ const OverviewSection = ({ trigger, flowName, onToggleEnabled, onDelete, isToggl
500
500
  /* @__PURE__ */ jsxs("div", {
501
501
  className: "space-y-1",
502
502
  children: [/* @__PURE__ */ jsx("label", {
503
- className: "text-xs font-medium text-muted-foreground uppercase tracking-wide",
503
+ className: "text-xs font-medium tracking-wide uppercase text-muted-foreground",
504
504
  children: "Webhook URL"
505
505
  }), /* @__PURE__ */ jsx(CopyableField, { value: `/plugins/webhooks/receive/${trigger.webhookPath}` })]
506
506
  }),
507
507
  trigger.hmacEnabled && trigger.hmacHeaderName && /* @__PURE__ */ jsxs("div", {
508
508
  className: "space-y-1",
509
509
  children: [/* @__PURE__ */ jsx("label", {
510
- className: "text-xs font-medium text-muted-foreground uppercase tracking-wide",
510
+ className: "text-xs font-medium tracking-wide uppercase text-muted-foreground",
511
511
  children: "HMAC Authentication"
512
512
  }), /* @__PURE__ */ jsxs("div", {
513
- className: "rounded-lg border bg-muted/30 p-3 space-y-1",
513
+ className: "p-3 space-y-1 border rounded-lg bg-muted/30",
514
514
  children: [/* @__PURE__ */ jsxs("div", {
515
515
  className: "flex items-center justify-between",
516
516
  children: [/* @__PURE__ */ jsx("span", {
517
517
  className: "text-xs text-muted-foreground",
518
518
  children: "Signature Header"
519
519
  }), /* @__PURE__ */ jsx("span", {
520
- className: "text-sm font-mono",
520
+ className: "font-mono text-sm",
521
521
  children: trigger.hmacHeaderName
522
522
  })]
523
523
  }), /* @__PURE__ */ jsxs("div", {
@@ -535,12 +535,12 @@ const OverviewSection = ({ trigger, flowName, onToggleEnabled, onDelete, isToggl
535
535
  trigger.allowedIps && /* @__PURE__ */ jsxs("div", {
536
536
  className: "space-y-1",
537
537
  children: [/* @__PURE__ */ jsx("label", {
538
- className: "text-xs font-medium text-muted-foreground uppercase tracking-wide",
538
+ className: "text-xs font-medium tracking-wide uppercase text-muted-foreground",
539
539
  children: "IP Whitelist"
540
540
  }), /* @__PURE__ */ jsx("div", {
541
- className: "rounded-lg border bg-muted/30 p-3",
541
+ className: "p-3 border rounded-lg bg-muted/30",
542
542
  children: /* @__PURE__ */ jsx("span", {
543
- className: "text-sm font-mono break-all",
543
+ className: "font-mono text-sm break-all",
544
544
  children: trigger.allowedIps
545
545
  })
546
546
  })]
@@ -549,21 +549,21 @@ const OverviewSection = ({ trigger, flowName, onToggleEnabled, onDelete, isToggl
549
549
  className: "grid grid-cols-2 gap-4 py-2",
550
550
  children: [
551
551
  /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("span", {
552
- className: "text-xs text-muted-foreground block mb-1",
552
+ className: "block mb-1 text-xs text-muted-foreground",
553
553
  children: "Methods"
554
554
  }), /* @__PURE__ */ jsx("span", {
555
- className: "text-sm font-mono",
555
+ className: "font-mono text-sm",
556
556
  children: trigger.allowedMethods
557
557
  })] }),
558
558
  /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("span", {
559
- className: "text-xs text-muted-foreground block mb-1",
559
+ className: "block mb-1 text-xs text-muted-foreground",
560
560
  children: "Authentication"
561
561
  }), /* @__PURE__ */ jsx("span", {
562
562
  className: "text-sm",
563
563
  children: getAuthModeConfig$1(trigger).label
564
564
  })] }),
565
565
  /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("span", {
566
- className: "text-xs text-muted-foreground block mb-1",
566
+ className: "block mb-1 text-xs text-muted-foreground",
567
567
  children: "Created"
568
568
  }), /* @__PURE__ */ jsx("span", {
569
569
  className: "text-sm",
@@ -600,29 +600,29 @@ const OverviewSection = ({ trigger, flowName, onToggleEnabled, onDelete, isToggl
600
600
  ]
601
601
  }),
602
602
  trigger.flowId && /* @__PURE__ */ jsx("div", {
603
- className: "rounded-lg border bg-muted/30 p-3",
603
+ className: "p-3 border rounded-lg bg-muted/30",
604
604
  children: /* @__PURE__ */ jsxs("div", {
605
605
  className: "flex items-center gap-2",
606
606
  children: [/* @__PURE__ */ jsx(Workflow, { className: "w-4 h-4 text-muted-foreground shrink-0" }), /* @__PURE__ */ jsxs("div", {
607
607
  className: "flex-1 min-w-0",
608
608
  children: [/* @__PURE__ */ jsx("span", {
609
- className: "text-xs text-muted-foreground block",
609
+ className: "block text-xs text-muted-foreground",
610
610
  children: "Linked Flow"
611
611
  }), /* @__PURE__ */ jsxs("a", {
612
612
  href: `/invect/flow/${trigger.flowId}`,
613
- className: "text-sm font-medium text-primary hover:underline inline-flex items-center gap-1",
613
+ className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline",
614
614
  children: [flowName ?? trigger.flowId, /* @__PURE__ */ jsx(ExternalLink, { className: "w-3 h-3" })]
615
615
  })]
616
616
  })]
617
617
  })
618
618
  }),
619
- trigger.lastPayload != null && /* @__PURE__ */ jsxs("div", {
619
+ trigger.lastPayload !== null && trigger.lastPayload !== void 0 && /* @__PURE__ */ jsxs("div", {
620
620
  className: "space-y-1",
621
621
  children: [/* @__PURE__ */ jsx("label", {
622
- className: "text-xs font-medium text-muted-foreground uppercase tracking-wide",
622
+ className: "text-xs font-medium tracking-wide uppercase text-muted-foreground",
623
623
  children: "Last Payload"
624
624
  }), /* @__PURE__ */ jsx("pre", {
625
- className: "text-xs font-mono bg-muted rounded-lg p-3 overflow-auto max-h-32",
625
+ className: "p-3 overflow-auto font-mono text-xs rounded-lg bg-muted max-h-32",
626
626
  children: JSON.stringify(trigger.lastPayload, null, 2)
627
627
  })]
628
628
  }),
@@ -636,7 +636,7 @@ const OverviewSection = ({ trigger, flowName, onToggleEnabled, onDelete, isToggl
636
636
  }), /* @__PURE__ */ jsxs("button", {
637
637
  onClick: onDelete,
638
638
  disabled: isDeleting,
639
- className: "inline-flex items-center gap-2 rounded-md text-sm font-medium h-8 px-3 border border-red-200 text-red-600 hover:bg-red-50 dark:border-red-800 dark:text-red-400 dark:hover:bg-red-950/30 transition-colors",
639
+ className: "inline-flex items-center h-8 gap-2 px-3 text-sm font-medium text-imp-destructive transition-colors border border-imp-destructive/30 rounded-md hover:bg-imp-destructive/10",
640
640
  children: [isDeleting ? /* @__PURE__ */ jsx(Loader2, { className: "w-3.5 h-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Trash2, { className: "w-3.5 h-3.5" }), "Delete"]
641
641
  })]
642
642
  })
@@ -701,7 +701,7 @@ const EditSection = ({ trigger, onSuccess }) => {
701
701
  })]
702
702
  }),
703
703
  /* @__PURE__ */ jsxs("div", {
704
- className: "space-y-3 rounded-lg border border-border bg-muted/30 p-3",
704
+ className: "p-3 space-y-3 border rounded-lg border-border bg-muted/30",
705
705
  children: [/* @__PURE__ */ jsxs("div", {
706
706
  className: "flex items-center justify-between",
707
707
  children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
@@ -717,7 +717,7 @@ const EditSection = ({ trigger, onSuccess }) => {
717
717
  children: /* @__PURE__ */ jsx("span", { className: `pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform ${hmacEnabled ? "translate-x-4" : "translate-x-0"}` })
718
718
  })]
719
719
  }), hmacEnabled && /* @__PURE__ */ jsxs("div", {
720
- className: "space-y-3 pt-1",
720
+ className: "pt-1 space-y-3",
721
721
  children: [/* @__PURE__ */ jsxs("div", {
722
722
  className: "space-y-1.5",
723
723
  children: [/* @__PURE__ */ jsx("label", {
@@ -807,12 +807,12 @@ const EditSection = ({ trigger, onSuccess }) => {
807
807
  children: [/* @__PURE__ */ jsx("button", {
808
808
  type: "button",
809
809
  onClick: onSuccess,
810
- className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-8 px-3 border bg-background shadow-xs hover:bg-accent transition-colors",
810
+ className: "inline-flex items-center justify-center h-8 px-3 text-sm font-medium transition-colors border rounded-md shadow-xs bg-background hover:bg-accent",
811
811
  children: "Cancel"
812
812
  }), /* @__PURE__ */ jsx("button", {
813
813
  type: "submit",
814
814
  disabled: !name.trim() || updateMutation.isPending,
815
- className: "inline-flex items-center justify-center rounded-md text-sm font-medium h-8 px-3 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 transition-colors",
815
+ className: "inline-flex items-center justify-center h-8 px-3 text-sm font-medium transition-colors rounded-md bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
816
816
  children: updateMutation.isPending ? "Saving…" : "Save Changes"
817
817
  })]
818
818
  })
@@ -1055,7 +1055,7 @@ const WebhooksPage = () => {
1055
1055
  * Webhooks Frontend Plugin — registers the sidebar item and route
1056
1056
  * for webhook management.
1057
1057
  */
1058
- const webhooksFrontendPlugin = {
1058
+ const webhooksFrontend = {
1059
1059
  id: "webhooks",
1060
1060
  name: "Webhooks",
1061
1061
  sidebar: [{
@@ -1169,6 +1169,6 @@ const WebhookTriggerSelector = ({ selectedId, onSelect, flowId, nodeId }) => {
1169
1169
  });
1170
1170
  };
1171
1171
  //#endregion
1172
- export { CopyableField, CreateWebhookModal, WebhookDetailPanel, WebhookTriggerSelector, WebhooksPage, useCreateWebhookTrigger, useDeleteWebhookTrigger, useTestWebhookTrigger, useUpdateWebhookTrigger, useWebhookTrigger, useWebhookTriggerInfo, useWebhookTriggers, webhooksFrontendPlugin };
1172
+ export { CopyableField, CreateWebhookModal, WebhookDetailPanel, WebhookTriggerSelector, WebhooksPage, useCreateWebhookTrigger, useDeleteWebhookTrigger, useTestWebhookTrigger, useUpdateWebhookTrigger, useWebhookTrigger, useWebhookTriggerInfo, useWebhookTriggers, webhooksFrontend };
1173
1173
 
1174
1174
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["AUTH_MODE_CONFIG","getAuthModeConfig"],"sources":["../../src/frontend/hooks/useWebhookQueries.ts","../../src/frontend/components/CopyableField.tsx","../../src/frontend/components/CreateWebhookModal.tsx","../../src/frontend/components/WebhookDetailPanel.tsx","../../src/frontend/components/WebhooksPage.tsx","../../src/frontend/plugins/webhooksFrontendPlugin.ts","../../src/frontend/components/WebhookTriggerSelector.tsx"],"sourcesContent":["/**\n * React hooks for the webhooks plugin API.\n *\n * Uses @invect/frontend's ApiContext for the base URL.\n */\n\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';\nimport { useApiBaseURL } from '@invect/frontend';\nimport type {\n WebhookTrigger,\n CreateWebhookTriggerInput,\n UpdateWebhookTriggerInput,\n WebhookTriggerInfo,\n} from '../../shared/types';\n\n// ─── API helper ─────────────────────────────────────────────────────\n\nasync function apiFetch<T>(baseUrl: string, path: string, init?: RequestInit): Promise<T> {\n const res = await fetch(`${baseUrl}${path}`, {\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n credentials: 'include',\n ...init,\n });\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n throw new Error((body as { error?: string }).error || `HTTP ${res.status}`);\n }\n return res.json() as Promise<T>;\n}\n\n// ─── Query Keys ─────────────────────────────────────────────────────\n\nconst keys = {\n all: ['webhooks'] as const,\n list: () => [...keys.all, 'list'] as const,\n detail: (id: string) => [...keys.all, 'detail', id] as const,\n info: (id: string) => [...keys.all, 'info', id] as const,\n};\n\n// ─── Queries ────────────────────────────────────────────────────────\n\nexport function useWebhookTriggers() {\n const baseUrl = useApiBaseURL();\n return useQuery({\n queryKey: keys.list(),\n queryFn: () =>\n apiFetch<{ data: WebhookTrigger[] }>(baseUrl, '/plugins/webhooks/triggers').then(\n (r) => r.data,\n ),\n });\n}\n\nexport function useWebhookTrigger(id: string | undefined) {\n const baseUrl = useApiBaseURL();\n return useQuery({\n queryKey: keys.detail(id ?? ''),\n queryFn: () => apiFetch<WebhookTrigger>(baseUrl, `/plugins/webhooks/triggers/${id}`),\n enabled: !!id,\n });\n}\n\nexport function useWebhookTriggerInfo(id: string | undefined) {\n const baseUrl = useApiBaseURL();\n return useQuery({\n queryKey: keys.info(id ?? ''),\n queryFn: () =>\n apiFetch<WebhookTriggerInfo>(baseUrl, `/plugins/webhooks/triggers/${id}/info`),\n enabled: !!id,\n });\n}\n\n// ─── Mutations ──────────────────────────────────────────────────────\n\nexport function useCreateWebhookTrigger() {\n const baseUrl = useApiBaseURL();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: CreateWebhookTriggerInput) =>\n apiFetch<WebhookTrigger & { fullUrl?: string }>(baseUrl, '/plugins/webhooks/triggers', {\n method: 'POST',\n body: JSON.stringify(input),\n }),\n onSuccess: () => qc.invalidateQueries({ queryKey: keys.list() }),\n });\n}\n\nexport function useUpdateWebhookTrigger() {\n const baseUrl = useApiBaseURL();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: ({ id, ...input }: UpdateWebhookTriggerInput & { id: string }) =>\n apiFetch<WebhookTrigger>(baseUrl, `/plugins/webhooks/triggers/${id}`, {\n method: 'PUT',\n body: JSON.stringify(input),\n }),\n onSuccess: (_data, vars) => {\n qc.invalidateQueries({ queryKey: keys.list() });\n qc.invalidateQueries({ queryKey: keys.detail(vars.id) });\n },\n });\n}\n\nexport function useDeleteWebhookTrigger() {\n const baseUrl = useApiBaseURL();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<{ success: boolean }>(baseUrl, `/plugins/webhooks/triggers/${id}`, {\n method: 'DELETE',\n }),\n onSuccess: () => qc.invalidateQueries({ queryKey: keys.list() }),\n });\n}\n\nexport function useTestWebhookTrigger() {\n const baseUrl = useApiBaseURL();\n return useMutation({\n mutationFn: ({ id, payload }: { id: string; payload?: unknown }) =>\n apiFetch<{ status: string }>(baseUrl, `/plugins/webhooks/triggers/${id}/test`, {\n method: 'POST',\n body: JSON.stringify(payload ?? { test: true }),\n }),\n });\n}\n","/**\n * CopyableField — Displays a value with copy-to-clipboard and optional masking.\n *\n * Follows the pattern from the Credentials page.\n */\n\nimport { useState, type FC } from 'react';\nimport { Copy, Check, Eye, EyeOff } from 'lucide-react';\n\ninterface CopyableFieldProps {\n value: string;\n masked?: boolean;\n}\n\nexport const CopyableField: FC<CopyableFieldProps> = ({ value, masked = false }) => {\n const [copied, setCopied] = useState(false);\n const [revealed, setRevealed] = useState(!masked);\n\n const copy = async () => {\n await navigator.clipboard.writeText(value);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n const displayValue = revealed ? value : '•'.repeat(Math.min(value.length, 32));\n\n return (\n <div className=\"flex items-center gap-1.5\">\n <code className=\"flex-1 truncate rounded bg-muted px-2 py-1.5 font-mono text-[11px]\">\n {displayValue}\n </code>\n {masked && (\n <button\n onClick={() => setRevealed(!revealed)}\n className=\"shrink-0 rounded p-1 hover:bg-muted text-muted-foreground transition-colors\"\n title={revealed ? 'Hide' : 'Reveal'}\n >\n {revealed ? <EyeOff className=\"h-3.5 w-3.5\" /> : <Eye className=\"h-3.5 w-3.5\" />}\n </button>\n )}\n <button\n onClick={copy}\n className=\"shrink-0 rounded p-1 hover:bg-muted text-muted-foreground transition-colors\"\n title=\"Copy to clipboard\"\n >\n {copied ? (\n <Check className=\"h-3.5 w-3.5 text-emerald-500\" />\n ) : (\n <Copy className=\"h-3.5 w-3.5\" />\n )}\n </button>\n </div>\n );\n};\n","/**\n * CreateWebhookModal — Dialog for creating a new webhook trigger.\n *\n * Step 1: Name + endpoint settings\n * Step 2: Success screen with URL + secret to copy\n */\n\nimport { useState, type FC, type RefObject } from 'react';\nimport { Check } from 'lucide-react';\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogDescription,\n} from '@invect/frontend';\nimport { useCreateWebhookTrigger } from '../hooks/useWebhookQueries';\nimport { CopyableField } from './CopyableField';\nimport type { CreateWebhookTriggerInput } from '../../shared/types';\n\n// ─── Component ──────────────────────────────────────────────────────\n\ninterface CreateWebhookModalProps {\n open: boolean;\n onClose: () => void;\n containerRef?: RefObject<HTMLElement | null>;\n /** Pre-fill flowId + nodeId when creating from flow editor */\n flowId?: string;\n nodeId?: string;\n}\n\nexport const CreateWebhookModal: FC<CreateWebhookModalProps> = ({\n open,\n onClose,\n containerRef,\n flowId,\n nodeId,\n}) => {\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [methods, setMethods] = useState('POST');\n const [hmacEnabled, setHmacEnabled] = useState(false);\n const [hmacHeaderName, setHmacHeaderName] = useState('');\n const [hmacSecret, setHmacSecret] = useState('');\n const [allowedIps, setAllowedIps] = useState('');\n const [createdUrl, setCreatedUrl] = useState<string | null>(null);\n\n const createMutation = useCreateWebhookTrigger();\n\n const handleCreate = async () => {\n if (!name.trim()) return;\n\n const input: CreateWebhookTriggerInput = {\n name: name.trim(),\n description: description.trim() || undefined,\n provider: 'generic',\n allowedMethods: methods,\n hmacEnabled,\n hmacHeaderName: hmacEnabled ? hmacHeaderName.trim() || undefined : undefined,\n hmacSecret: hmacEnabled ? hmacSecret.trim() || undefined : undefined,\n allowedIps: allowedIps.trim() || undefined,\n flowId,\n nodeId,\n };\n\n try {\n const result = await createMutation.mutateAsync(input);\n setCreatedUrl(result.fullUrl ?? `/plugins/webhooks/receive/${result.webhookPath}`);\n } catch {\n // Error handled by mutation state\n }\n };\n\n const handleClose = () => {\n setName('');\n setDescription('');\n setMethods('POST');\n setHmacEnabled(false);\n setHmacHeaderName('');\n setHmacSecret('');\n setAllowedIps('');\n setCreatedUrl(null);\n onClose();\n };\n\n return (\n <Dialog open={open} onOpenChange={(v) => { if (!v) handleClose(); }}>\n <DialogContent\n container={containerRef?.current}\n className=\"max-w-md gap-0 p-0 overflow-hidden\"\n showCloseButton\n >\n {/* Header */}\n <div className=\"px-6 pt-6 pb-4\">\n <DialogHeader>\n <DialogTitle className=\"text-base\">\n {createdUrl ? 'Webhook Created' : 'Create Webhook'}\n </DialogTitle>\n <DialogDescription>\n {createdUrl\n ? 'Copy the URL below and configure it in your external service.'\n : 'Set up a generic endpoint to receive webhook events from any external system.'}\n </DialogDescription>\n </DialogHeader>\n </div>\n\n {/* Content */}\n <div className=\"px-6 pb-6 space-y-4\">\n {createdUrl ? (\n /* ── Success ── */\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-2 p-3 text-sm border rounded-lg border-emerald-200 bg-emerald-50 dark:border-emerald-800 dark:bg-emerald-950/30 text-emerald-700 dark:text-emerald-400\">\n <Check className=\"w-4 h-4 shrink-0\" />\n Webhook is ready to receive events.\n </div>\n\n <div className=\"space-y-1\">\n <label className=\"text-xs font-medium tracking-wide uppercase text-muted-foreground\">\n Webhook URL\n </label>\n <CopyableField value={createdUrl} />\n </div>\n\n <button\n onClick={handleClose}\n className=\"inline-flex items-center justify-center w-full px-4 text-sm font-medium transition-colors rounded-md h-9 bg-primary text-primary-foreground hover:bg-primary/90\"\n >\n Done\n </button>\n </div>\n ) : (\n /* ── Form ── */\n <>\n {/* Name */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-create-name\">\n Name *\n </label>\n <input\n id=\"wh-create-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Partner API Events\"\n autoFocus\n className=\"h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n\n {/* Description */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-create-desc\">\n Description\n </label>\n <input\n id=\"wh-create-desc\"\n type=\"text\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Optional description\"\n className=\"h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Authentication</label>\n <div className=\"space-y-3 rounded-lg border border-border bg-muted/30 p-3\">\n <div className=\"flex items-center justify-between\">\n <div>\n <div className=\"text-sm font-medium\">HMAC Verification</div>\n <p className=\"mt-0.5 text-xs text-muted-foreground\">\n Verify requests using an HMAC signature header.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => setHmacEnabled(!hmacEnabled)}\n className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors ${\n hmacEnabled ? 'bg-primary' : 'bg-muted-foreground/30'\n }`}\n >\n <span\n className={`pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform ${\n hmacEnabled ? 'translate-x-4' : 'translate-x-0'\n }`}\n />\n </button>\n </div>\n {hmacEnabled && (\n <div className=\"space-y-3 pt-1\">\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium\" htmlFor=\"wh-create-hmac-header\">\n Signature Header Name\n </label>\n <input\n id=\"wh-create-hmac-header\"\n type=\"text\"\n autoComplete=\"off\"\n value={hmacHeaderName}\n onChange={(e) => setHmacHeaderName(e.target.value)}\n placeholder=\"e.g. x-signature\"\n className=\"h-8 w-full rounded-md border border-input bg-background px-2.5 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium\" htmlFor=\"wh-create-hmac-secret\">\n Signing Secret\n </label>\n <input\n id=\"wh-create-hmac-secret\"\n type=\"password\"\n autoComplete=\"new-password\"\n value={hmacSecret}\n onChange={(e) => setHmacSecret(e.target.value)}\n placeholder=\"Enter secret key\"\n className=\"h-8 w-full rounded-md border border-input bg-background px-2.5 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* IP Whitelist */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-create-ips\">\n IP Whitelist\n </label>\n <input\n id=\"wh-create-ips\"\n type=\"text\"\n value={allowedIps}\n onChange={(e) => setAllowedIps(e.target.value)}\n placeholder=\"e.g. 192.168.1.1, 10.0.0.0/24\"\n className=\"h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm font-mono shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Leave empty to allow all IPs. Separate multiple addresses with commas.\n </p>\n </div>\n\n {/* HTTP Methods */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-create-methods\">\n HTTP Methods\n </label>\n <select\n id=\"wh-create-methods\"\n value={methods}\n onChange={(e) => setMethods(e.target.value)}\n className=\"flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n >\n <option value=\"POST\">POST only</option>\n <option value=\"POST,PUT\">POST + PUT</option>\n <option value=\"ANY\">Any method</option>\n </select>\n </div>\n\n {/* Error */}\n {createMutation.isError && (\n <p className=\"text-sm text-red-500\">\n {createMutation.error?.message || 'Failed to create webhook'}\n </p>\n )}\n\n {/* Buttons */}\n <div className=\"flex gap-2 pt-2\">\n <button\n type=\"button\"\n onClick={handleClose}\n className=\"inline-flex items-center justify-center flex-1 px-3 text-sm font-medium transition-colors border rounded-md shadow-xs h-9 bg-background hover:bg-accent\"\n >\n Cancel\n </button>\n <button\n onClick={handleCreate}\n disabled={!name.trim() || createMutation.isPending}\n className=\"inline-flex items-center justify-center flex-1 px-3 text-sm font-medium transition-colors rounded-md h-9 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n {createMutation.isPending ? 'Creating…' : 'Create Webhook'}\n </button>\n </div>\n </>\n )}\n </div>\n </DialogContent>\n </Dialog>\n );\n};\n","/**\n * WebhookDetailPanel — Detail view rendered inside a Dialog.\n *\n * Tabs: Overview | Edit\n * Shows webhook URL, secret, status, linked flow, stats.\n * Allows toggling enabled/disabled, editing, and deleting.\n */\n\nimport { useState, type FC } from 'react';\nimport {\n Globe,\n Trash2,\n ExternalLink,\n Clock,\n Hash,\n ToggleLeft,\n ToggleRight,\n Workflow,\n Loader2,\n} from 'lucide-react';\nimport { DialogHeader, DialogTitle, DialogDescription } from '@invect/frontend';\nimport {\n useUpdateWebhookTrigger,\n useDeleteWebhookTrigger,\n} from '../hooks/useWebhookQueries';\nimport { CopyableField } from './CopyableField';\nimport type { WebhookTrigger, UpdateWebhookTriggerInput } from '../../shared/types';\n\n// ─── Constants ──────────────────────────────────────────────────────\n\nconst AUTH_MODE_CONFIG: Record<string, { label: string; color: string }> = {\n generic: {\n label: 'Unauthenticated',\n color: 'bg-zinc-100 text-zinc-600 dark:bg-zinc-800/40 dark:text-zinc-400',\n },\n hmac: {\n label: 'HMAC',\n color: 'bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-300',\n },\n ip_whitelist: {\n label: 'IP Whitelist',\n color: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300',\n },\n signed: {\n label: 'Signed',\n color: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',\n },\n};\n\nfunction getAuthModeConfig(trigger: WebhookTrigger) {\n if (trigger.provider !== 'generic') {\n return AUTH_MODE_CONFIG.signed;\n }\n if (trigger.hmacEnabled) {\n return AUTH_MODE_CONFIG.hmac;\n }\n if (trigger.allowedIps) {\n return AUTH_MODE_CONFIG.ip_whitelist;\n }\n return AUTH_MODE_CONFIG.generic;\n}\n\nfunction formatFullDate(iso?: string): string {\n if (!iso) return 'Never';\n return new Date(iso).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\n// ─── Component ──────────────────────────────────────────────────────\n\ntype Section = 'overview' | 'edit';\n\ninterface WebhookDetailPanelProps {\n trigger: WebhookTrigger;\n flowName?: string;\n onClose: () => void;\n}\n\nexport const WebhookDetailPanel: FC<WebhookDetailPanelProps> = ({\n trigger,\n flowName,\n onClose,\n}) => {\n const [section, setSection] = useState<Section>('overview');\n const updateMutation = useUpdateWebhookTrigger();\n const deleteMutation = useDeleteWebhookTrigger();\n\n const authModeConfig = getAuthModeConfig(trigger);\n\n const handleToggleEnabled = () => {\n updateMutation.mutate({ id: trigger.id, isEnabled: !trigger.isEnabled });\n };\n\n const handleDelete = () => {\n if (confirm(`Delete webhook \"${trigger.name}\"? This cannot be undone.`)) {\n deleteMutation.mutate(trigger.id, { onSuccess: onClose });\n }\n };\n\n return (\n <>\n {/* Fixed header */}\n <div className=\"px-6 pt-6 pb-0\">\n <DialogHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center justify-center w-10 h-10 rounded-lg bg-muted/60 shrink-0\">\n <Globe className=\"w-5 h-5 text-muted-foreground\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <DialogTitle className=\"text-base\">{trigger.name}</DialogTitle>\n <DialogDescription className=\"flex items-center gap-2 mt-0.5 text-xs\">\n <span\n className={`inline-flex items-center rounded-full border border-transparent px-2 py-0.5 text-[10px] font-medium ${authModeConfig.color}`}\n >\n {authModeConfig.label}\n </span>\n <span\n className={`w-1.5 h-1.5 rounded-full ${\n trigger.isEnabled ? 'bg-emerald-500' : 'bg-muted-foreground/40'\n }`}\n />\n <span>{trigger.isEnabled ? 'Enabled' : 'Disabled'}</span>\n </DialogDescription>\n </div>\n </div>\n </DialogHeader>\n\n {/* Tab nav */}\n <div className=\"flex gap-1 border-b -mx-6 px-6 mt-4\">\n {([\n { key: 'overview' as const, label: 'Overview' },\n { key: 'edit' as const, label: 'Edit' },\n ]).map(({ key, label }) => (\n <button\n key={key}\n onClick={() => setSection(key)}\n className={`px-3 py-2 text-sm font-medium border-b-2 -mb-px transition-colors ${\n section === key\n ? 'border-foreground text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {label}\n </button>\n ))}\n </div>\n </div>\n\n {/* Scrollable content */}\n <div className=\"flex-1 overflow-y-auto px-6 pb-6\">\n {section === 'overview' ? (\n <OverviewSection\n trigger={trigger}\n flowName={flowName}\n onToggleEnabled={handleToggleEnabled}\n onDelete={handleDelete}\n isToggling={updateMutation.isPending}\n isDeleting={deleteMutation.isPending}\n />\n ) : (\n <EditSection\n trigger={trigger}\n onSuccess={() => setSection('overview')}\n />\n )}\n </div>\n </>\n );\n};\n\n// ─── Overview Section ───────────────────────────────────────────────\n\nconst OverviewSection: FC<{\n trigger: WebhookTrigger;\n flowName?: string;\n onToggleEnabled: () => void;\n onDelete: () => void;\n isToggling: boolean;\n isDeleting: boolean;\n}> = ({ trigger, flowName, onToggleEnabled, onDelete, isToggling, isDeleting }) => (\n <div className=\"pt-4 space-y-5\">\n {/* Endpoint URL */}\n <div className=\"space-y-1\">\n <label className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Webhook URL\n </label>\n <CopyableField value={`/plugins/webhooks/receive/${trigger.webhookPath}`} />\n </div>\n\n {/* HMAC Authentication */}\n {trigger.hmacEnabled && trigger.hmacHeaderName && (\n <div className=\"space-y-1\">\n <label className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n HMAC Authentication\n </label>\n <div className=\"rounded-lg border bg-muted/30 p-3 space-y-1\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-muted-foreground\">Signature Header</span>\n <span className=\"text-sm font-mono\">{trigger.hmacHeaderName}</span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-muted-foreground\">Secret</span>\n <CopyableField value={trigger.hmacSecret ?? ''} masked />\n </div>\n </div>\n </div>\n )}\n\n {/* IP Whitelist */}\n {trigger.allowedIps && (\n <div className=\"space-y-1\">\n <label className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n IP Whitelist\n </label>\n <div className=\"rounded-lg border bg-muted/30 p-3\">\n <span className=\"text-sm font-mono break-all\">{trigger.allowedIps}</span>\n </div>\n </div>\n )}\n\n {/* Info grid */}\n <div className=\"grid grid-cols-2 gap-4 py-2\">\n <div>\n <span className=\"text-xs text-muted-foreground block mb-1\">Methods</span>\n <span className=\"text-sm font-mono\">{trigger.allowedMethods}</span>\n </div>\n <div>\n <span className=\"text-xs text-muted-foreground block mb-1\">Authentication</span>\n <span className=\"text-sm\">{getAuthModeConfig(trigger).label}</span>\n </div>\n <div>\n <span className=\"text-xs text-muted-foreground block mb-1\">Created</span>\n <span className=\"text-sm\">{formatFullDate(trigger.createdAt)}</span>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <Hash className=\"w-3 h-3 text-muted-foreground\" />\n <span className=\"text-xs text-muted-foreground\">Triggers:</span>\n <span className=\"text-sm font-medium\">{trigger.triggerCount}</span>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <Clock className=\"w-3 h-3 text-muted-foreground\" />\n <span className=\"text-xs text-muted-foreground\">Last:</span>\n <span className=\"text-sm\">{formatFullDate(trigger.lastTriggeredAt)}</span>\n </div>\n </div>\n\n {/* Linked flow */}\n {trigger.flowId && (\n <div className=\"rounded-lg border bg-muted/30 p-3\">\n <div className=\"flex items-center gap-2\">\n <Workflow className=\"w-4 h-4 text-muted-foreground shrink-0\" />\n <div className=\"flex-1 min-w-0\">\n <span className=\"text-xs text-muted-foreground block\">Linked Flow</span>\n <a\n href={`/invect/flow/${trigger.flowId}`}\n className=\"text-sm font-medium text-primary hover:underline inline-flex items-center gap-1\"\n >\n {flowName ?? trigger.flowId}\n <ExternalLink className=\"w-3 h-3\" />\n </a>\n </div>\n </div>\n </div>\n )}\n\n {/* Last payload */}\n {trigger.lastPayload != null && (\n <div className=\"space-y-1\">\n <label className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Last Payload\n </label>\n <pre className=\"text-xs font-mono bg-muted rounded-lg p-3 overflow-auto max-h-32\">\n {JSON.stringify(trigger.lastPayload, null, 2)}\n </pre>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center justify-between pt-3 border-t\">\n <button\n onClick={onToggleEnabled}\n disabled={isToggling}\n className={`inline-flex items-center gap-2 rounded-md text-sm font-medium h-8 px-3 border transition-colors ${\n trigger.isEnabled\n ? 'border-amber-200 text-amber-700 hover:bg-amber-50 dark:border-amber-800 dark:text-amber-400 dark:hover:bg-amber-950/30'\n : 'border-emerald-200 text-emerald-700 hover:bg-emerald-50 dark:border-emerald-800 dark:text-emerald-400 dark:hover:bg-emerald-950/30'\n }`}\n >\n {isToggling ? (\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n ) : trigger.isEnabled ? (\n <ToggleLeft className=\"w-3.5 h-3.5\" />\n ) : (\n <ToggleRight className=\"w-3.5 h-3.5\" />\n )}\n {trigger.isEnabled ? 'Disable' : 'Enable'}\n </button>\n\n <button\n onClick={onDelete}\n disabled={isDeleting}\n className=\"inline-flex items-center gap-2 rounded-md text-sm font-medium h-8 px-3 border border-red-200 text-red-600 hover:bg-red-50 dark:border-red-800 dark:text-red-400 dark:hover:bg-red-950/30 transition-colors\"\n >\n {isDeleting ? (\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n ) : (\n <Trash2 className=\"w-3.5 h-3.5\" />\n )}\n Delete\n </button>\n </div>\n </div>\n);\n\n// ─── Edit Section ───────────────────────────────────────────────────\n\nconst EditSection: FC<{\n trigger: WebhookTrigger;\n onSuccess: () => void;\n}> = ({ trigger, onSuccess }) => {\n const [name, setName] = useState(trigger.name);\n const [description, setDescription] = useState(trigger.description ?? '');\n const [methods, setMethods] = useState(trigger.allowedMethods);\n const [hmacEnabled, setHmacEnabled] = useState(trigger.hmacEnabled);\n const [hmacHeaderName, setHmacHeaderName] = useState(trigger.hmacHeaderName ?? '');\n const [hmacSecret, setHmacSecret] = useState(trigger.hmacSecret ?? '');\n const [allowedIps, setAllowedIps] = useState(trigger.allowedIps ?? '');\n const updateMutation = useUpdateWebhookTrigger();\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!name.trim()) return;\n\n const input: UpdateWebhookTriggerInput & { id: string } = {\n id: trigger.id,\n name: name.trim(),\n description: description.trim() || undefined,\n allowedMethods: methods,\n hmacEnabled,\n hmacHeaderName: hmacEnabled ? hmacHeaderName.trim() || undefined : undefined,\n hmacSecret: hmacEnabled ? hmacSecret.trim() || undefined : undefined,\n allowedIps: allowedIps.trim() || undefined,\n };\n updateMutation.mutate(input, { onSuccess });\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"pt-4 space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-edit-name\">\n Name *\n </label>\n <input\n id=\"wh-edit-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n required\n className=\"h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-edit-desc\">\n Description\n </label>\n <textarea\n id=\"wh-edit-desc\"\n rows={2}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Optional description\"\n className=\"w-full min-h-16 rounded-md border border-input bg-background px-3 py-2 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20 field-sizing-content\"\n />\n </div>\n\n {/* HMAC Authentication */}\n <div className=\"space-y-3 rounded-lg border border-border bg-muted/30 p-3\">\n <div className=\"flex items-center justify-between\">\n <div>\n <div className=\"text-sm font-medium\">HMAC Authentication</div>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n Verify incoming requests using an HMAC signature header.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => setHmacEnabled(!hmacEnabled)}\n className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors ${\n hmacEnabled ? 'bg-primary' : 'bg-muted-foreground/30'\n }`}\n >\n <span\n className={`pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform ${\n hmacEnabled ? 'translate-x-4' : 'translate-x-0'\n }`}\n />\n </button>\n </div>\n {hmacEnabled && (\n <div className=\"space-y-3 pt-1\">\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium\" htmlFor=\"wh-edit-hmac-header\">\n Signature Header Name\n </label>\n <input\n id=\"wh-edit-hmac-header\"\n type=\"text\"\n value={hmacHeaderName}\n onChange={(e) => setHmacHeaderName(e.target.value)}\n placeholder=\"e.g. x-signature\"\n className=\"h-8 w-full rounded-md border border-input bg-background px-2.5 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium\" htmlFor=\"wh-edit-hmac-secret\">\n Signing Secret\n </label>\n <input\n id=\"wh-edit-hmac-secret\"\n type=\"password\"\n value={hmacSecret}\n onChange={(e) => setHmacSecret(e.target.value)}\n placeholder=\"Enter secret key\"\n className=\"h-8 w-full rounded-md border border-input bg-background px-2.5 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n </div>\n )}\n </div>\n\n {/* IP Whitelist */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-edit-ips\">\n IP Whitelist\n </label>\n <textarea\n id=\"wh-edit-ips\"\n rows={2}\n value={allowedIps}\n onChange={(e) => setAllowedIps(e.target.value)}\n placeholder=\"Comma-separated IPs, e.g. 192.168.1.1, 10.0.0.0/24\"\n className=\"w-full min-h-16 rounded-md border border-input bg-background px-3 py-2 text-sm font-mono shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20 field-sizing-content\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Leave empty to allow all IPs. Separate multiple addresses with commas.\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-edit-methods\">\n HTTP Methods\n </label>\n <select\n id=\"wh-edit-methods\"\n value={methods}\n onChange={(e) => setMethods(e.target.value)}\n className=\"flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n >\n <option value=\"POST\">POST only</option>\n <option value=\"POST,PUT\">POST + PUT</option>\n <option value=\"ANY\">Any method</option>\n </select>\n </div>\n\n {updateMutation.isError && (\n <p className=\"text-sm text-red-500\">\n {updateMutation.error?.message || 'Failed to save changes'}\n </p>\n )}\n\n <div className=\"flex justify-end gap-2 pt-2\">\n <button\n type=\"button\"\n onClick={onSuccess}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-8 px-3 border bg-background shadow-xs hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={!name.trim() || updateMutation.isPending}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-8 px-3 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 transition-colors\"\n >\n {updateMutation.isPending ? 'Saving…' : 'Save Changes'}\n </button>\n </div>\n </form>\n );\n};\n","/**\n * WebhooksPage — Main webhook management page.\n *\n * Lists all webhook triggers with status, auth mode, linked flows, and last activity.\n * Click a row to open the detail dialog for editing, toggling, or deleting.\n */\n\nimport { useState, useRef, type FC } from 'react';\nimport {\n Globe,\n Plus,\n Search,\n Clock,\n Hash,\n ChevronRight,\n Loader2,\n Workflow,\n} from 'lucide-react';\nimport {\n PageLayout,\n Dialog,\n DialogContent,\n useFlows,\n} from '@invect/frontend';\nimport { useWebhookTriggers } from '../hooks/useWebhookQueries';\nimport { CreateWebhookModal } from './CreateWebhookModal';\nimport { WebhookDetailPanel } from './WebhookDetailPanel';\nimport type { WebhookTrigger } from '../../shared/types';\n\n// ─── Constants ──────────────────────────────────────────────────────\n\nconst AUTH_MODE_CONFIG: Record<string, { label: string; color: string }> = {\n generic: {\n label: 'Unauthenticated',\n color: 'bg-zinc-100 text-zinc-600 dark:bg-zinc-800/40 dark:text-zinc-400',\n },\n hmac: {\n label: 'HMAC',\n color: 'bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-300',\n },\n ip_whitelist: {\n label: 'IP Whitelist',\n color: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300',\n },\n signed: {\n label: 'Signed',\n color: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',\n },\n};\n\nfunction getAuthModeConfig(trigger: WebhookTrigger) {\n if (trigger.provider !== 'generic') {\n return AUTH_MODE_CONFIG.signed;\n }\n if (trigger.hmacEnabled) {\n return AUTH_MODE_CONFIG.hmac;\n }\n if (trigger.allowedIps) {\n return AUTH_MODE_CONFIG.ip_whitelist;\n }\n return AUTH_MODE_CONFIG.generic;\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────\n\nfunction formatRelativeTime(iso?: string): string {\n if (!iso) return 'Never';\n const diff = Date.now() - new Date(iso).getTime();\n const mins = Math.floor(diff / 60_000);\n if (mins < 1) return 'Just now';\n if (mins < 60) return `${mins}m ago`;\n const hours = Math.floor(mins / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n if (days < 30) return `${days}d ago`;\n return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n}\n\n// ─── Webhook Row ────────────────────────────────────────────────────\n\nconst WebhookRow: FC<{\n trigger: WebhookTrigger;\n flowName?: string;\n onClick: () => void;\n}> = ({ trigger, flowName, onClick }) => {\n const authModeConfig = getAuthModeConfig(trigger);\n\n return (\n <button\n className=\"w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-muted/50 transition-colors\"\n onClick={onClick}\n >\n {/* Icon */}\n <div className=\"flex items-center justify-center w-9 h-9 rounded-lg bg-muted/60 shrink-0\">\n <Globe className=\"w-4 h-4 text-muted-foreground\" />\n </div>\n\n {/* Name + subtitle */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium truncate\">{trigger.name}</span>\n <span\n className={`w-1.5 h-1.5 rounded-full shrink-0 ${\n trigger.isEnabled ? 'bg-emerald-500' : 'bg-muted-foreground/40'\n }`}\n />\n </div>\n <div className=\"flex items-center gap-2 mt-0.5\">\n {flowName ? (\n <span className=\"text-xs text-muted-foreground flex items-center gap-1 truncate\">\n <Workflow className=\"w-3 h-3 shrink-0\" />\n {flowName}\n </span>\n ) : trigger.description ? (\n <span className=\"text-xs text-muted-foreground truncate\">{trigger.description}</span>\n ) : (\n <span className=\"text-xs text-muted-foreground/50\">No flow linked</span>\n )}\n </div>\n </div>\n\n {/* Auth badge */}\n <span\n className={`hidden sm:inline-flex items-center rounded-full border border-transparent px-2 py-0.5 text-[10px] font-medium ${authModeConfig.color}`}\n >\n {authModeConfig.label}\n </span>\n\n {/* Stats */}\n <div className=\"hidden md:flex items-center gap-4 text-xs text-muted-foreground shrink-0\">\n <span className=\"flex items-center gap-1\" title=\"Total triggers\">\n <Hash className=\"w-3 h-3\" />\n {trigger.triggerCount}\n </span>\n <span className=\"flex items-center gap-1 w-16 justify-end\" title=\"Last triggered\">\n <Clock className=\"w-3 h-3\" />\n {formatRelativeTime(trigger.lastTriggeredAt)}\n </span>\n </div>\n\n <ChevronRight className=\"w-4 h-4 text-muted-foreground/50 shrink-0\" />\n </button>\n );\n};\n\n// ─── Main Page ──────────────────────────────────────────────────────\n\nexport const WebhooksPage: FC<{ basePath: string }> = () => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [createOpen, setCreateOpen] = useState(false);\n const [selectedTrigger, setSelectedTrigger] = useState<WebhookTrigger | null>(null);\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState<'all' | 'enabled' | 'disabled'>('all');\n\n const { data: triggers, isLoading, error } = useWebhookTriggers();\n const { data: flowsResponse } = useFlows();\n\n // Build flow name lookup\n const flowNameMap = new Map<string, string>();\n for (const flow of flowsResponse?.data ?? []) {\n flowNameMap.set(flow.id, flow.name);\n }\n\n // Filter\n const filtered = (triggers ?? []).filter((t) => {\n if (statusFilter === 'enabled' && !t.isEnabled) return false;\n if (statusFilter === 'disabled' && t.isEnabled) return false;\n if (searchQuery) {\n const q = searchQuery.toLowerCase();\n const nameMatch = t.name.toLowerCase().includes(q);\n const flowMatch = t.flowId\n ? (flowNameMap.get(t.flowId) ?? '').toLowerCase().includes(q)\n : false;\n if (!nameMatch && !flowMatch) return false;\n }\n return true;\n });\n\n const enabledCount = (triggers ?? []).filter((t) => t.isEnabled).length;\n const disabledCount = (triggers ?? []).filter((t) => !t.isEnabled).length;\n\n // Keep detail dialog in sync with list data\n const liveTrigger = selectedTrigger\n ? (triggers ?? []).find((t) => t.id === selectedTrigger.id) ?? selectedTrigger\n : null;\n\n return (\n <PageLayout\n title=\"Webhooks\"\n subtitle=\"Receive events from external systems and route them into your flows.\"\n icon={Globe}\n actions={\n <button\n onClick={() => setCreateOpen(true)}\n className=\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium h-9 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n New Webhook\n </button>\n }\n >\n <div ref={containerRef}>\n {/* Search + Filters */}\n <div className=\"flex flex-col gap-3 mb-4 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1 max-w-sm\">\n <Search className=\"absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 pointer-events-none text-muted-foreground\" />\n <input\n type=\"text\"\n placeholder=\"Search webhooks…\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full rounded-lg border border-border bg-transparent py-2 pl-9 pr-3 text-sm outline-none placeholder:text-muted-foreground focus:border-primary/50\"\n />\n </div>\n\n <div className=\"flex items-center gap-1.5 flex-wrap\">\n {([\n { key: 'all' as const, label: `All (${triggers?.length ?? 0})` },\n { key: 'enabled' as const, label: `Enabled (${enabledCount})` },\n { key: 'disabled' as const, label: `Disabled (${disabledCount})` },\n ]).map(({ key, label }) => (\n <button\n key={key}\n onClick={() => setStatusFilter(key)}\n className={`px-2.5 py-1 text-xs font-medium rounded-full border transition-colors ${\n statusFilter === key\n ? 'bg-foreground text-background border-foreground'\n : 'bg-card text-muted-foreground border-border hover:border-foreground/30'\n }`}\n >\n {label}\n </button>\n ))}\n </div>\n </div>\n\n {/* Content */}\n {isLoading ? (\n <div className=\"flex items-center justify-center gap-2 py-16 text-sm text-muted-foreground\">\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n Loading webhooks…\n </div>\n ) : error ? (\n <div className=\"rounded-lg border border-red-200 bg-red-50 dark:border-red-900 dark:bg-red-950/30 p-4 text-sm text-red-600 dark:text-red-400\">\n Failed to load webhooks: {(error as Error).message}\n </div>\n ) : filtered.length === 0 ? (\n <div className=\"flex flex-col items-center py-16 text-center\">\n <div className=\"rounded-full bg-muted p-3 mb-4\">\n <Globe className=\"w-6 h-6 text-muted-foreground\" />\n </div>\n <h3 className=\"text-sm font-semibold mb-1\">\n {triggers?.length === 0 ? 'No webhooks yet' : 'No webhooks match your filter'}\n </h3>\n <p className=\"max-w-sm text-sm text-muted-foreground mb-4\">\n {triggers?.length === 0\n ? 'Create a generic webhook endpoint to receive events from external systems.'\n : 'Try adjusting your search or filter criteria.'}\n </p>\n {triggers?.length === 0 && (\n <button\n onClick={() => setCreateOpen(true)}\n className=\"inline-flex items-center gap-2 rounded-md text-sm font-medium h-9 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n Create Webhook\n </button>\n )}\n </div>\n ) : (\n <div className=\"overflow-hidden rounded-lg border bg-card\">\n <div className=\"divide-y\">\n {filtered.map((trigger) => (\n <WebhookRow\n key={trigger.id}\n trigger={trigger}\n flowName={trigger.flowId ? flowNameMap.get(trigger.flowId) : undefined}\n onClick={() => setSelectedTrigger(trigger)}\n />\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Detail Dialog */}\n <Dialog\n open={!!selectedTrigger}\n onOpenChange={(open) => {\n if (!open) setSelectedTrigger(null);\n }}\n >\n <DialogContent\n container={containerRef.current}\n className=\"max-w-2xl h-[32rem] flex flex-col gap-0 p-0 overflow-hidden\"\n showCloseButton\n >\n {liveTrigger && (\n <WebhookDetailPanel\n trigger={liveTrigger}\n flowName={\n liveTrigger.flowId ? flowNameMap.get(liveTrigger.flowId) : undefined\n }\n onClose={() => setSelectedTrigger(null)}\n />\n )}\n </DialogContent>\n </Dialog>\n\n {/* Create Modal */}\n <CreateWebhookModal\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n containerRef={containerRef}\n />\n </PageLayout>\n );\n};\n","/**\n * Webhooks Frontend Plugin — registers the sidebar item and route\n * for webhook management.\n */\n\nimport { Globe } from 'lucide-react';\nimport { WebhooksPage } from '../components/WebhooksPage';\nimport type { InvectFrontendPlugin } from '@invect/frontend';\n\nexport const webhooksFrontendPlugin: InvectFrontendPlugin = {\n id: 'webhooks',\n name: 'Webhooks',\n\n sidebar: [\n {\n label: 'Webhooks',\n icon: Globe,\n path: '/webhooks',\n position: 'top',\n },\n ],\n\n routes: [\n {\n path: '/webhooks',\n component: WebhooksPage,\n },\n ],\n};\n","/**\n * WebhookTriggerSelector — Dropdown to pick an existing webhook trigger\n * or create a new one inline. Used in the trigger.webhook node's config panel.\n */\n\nimport { useState, type FC } from 'react';\nimport { Globe, Plus, ChevronDown, ExternalLink } from 'lucide-react';\nimport { useWebhookTriggers } from '../hooks/useWebhookQueries';\nimport { CreateWebhookModal } from './CreateWebhookModal';\nimport { CopyableField } from './CopyableField';\nimport type { WebhookTrigger } from '../../shared/types';\n\ninterface WebhookTriggerSelectorProps {\n selectedId?: string;\n onSelect: (trigger: WebhookTrigger) => void;\n flowId?: string;\n nodeId?: string;\n}\n\nexport const WebhookTriggerSelector: FC<WebhookTriggerSelectorProps> = ({\n selectedId,\n onSelect,\n flowId,\n nodeId,\n}) => {\n const [dropdownOpen, setDropdownOpen] = useState(false);\n const [createOpen, setCreateOpen] = useState(false);\n const { data: triggers, isLoading } = useWebhookTriggers();\n\n const selected = triggers?.find((t) => t.id === selectedId);\n\n return (\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Webhook Trigger</label>\n\n {/* Dropdown */}\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setDropdownOpen(!dropdownOpen)}\n className=\"flex h-9 w-full items-center justify-between rounded-md border border-input bg-background px-3 text-sm shadow-xs hover:bg-accent/50 transition-colors\"\n >\n <span className=\"flex items-center gap-2 truncate\">\n <Globe className=\"h-3.5 w-3.5 text-muted-foreground shrink-0\" />\n {selected ? (\n <>\n <span className=\"truncate\">{selected.name}</span>\n <span\n className={`w-1.5 h-1.5 rounded-full shrink-0 ${\n selected.isEnabled ? 'bg-emerald-500' : 'bg-muted-foreground/40'\n }`}\n />\n </>\n ) : (\n <span className=\"text-muted-foreground\">\n {isLoading ? 'Loading…' : 'Select a webhook trigger…'}\n </span>\n )}\n </span>\n <ChevronDown className=\"h-3.5 w-3.5 text-muted-foreground shrink-0\" />\n </button>\n\n {dropdownOpen && (\n <>\n <div className=\"fixed inset-0 z-40\" onClick={() => setDropdownOpen(false)} />\n\n <div className=\"absolute z-50 w-full mt-1 bg-popover border border-border rounded-lg shadow-lg max-h-60 overflow-auto\">\n {/* Create new */}\n <button\n onClick={() => {\n setDropdownOpen(false);\n setCreateOpen(true);\n }}\n className=\"w-full flex items-center gap-2 px-3 py-2 text-sm text-primary hover:bg-accent/50 border-b border-border transition-colors\"\n >\n <Plus className=\"h-3.5 w-3.5\" />\n Create New Webhook\n </button>\n\n {/* Existing triggers */}\n {(triggers ?? []).length === 0 ? (\n <div className=\"px-3 py-4 text-xs text-muted-foreground text-center\">\n No webhook triggers yet\n </div>\n ) : (\n (triggers ?? []).map((trigger) => (\n <button\n key={trigger.id}\n onClick={() => {\n onSelect(trigger);\n setDropdownOpen(false);\n }}\n className={`w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-accent/50 transition-colors ${\n trigger.id === selectedId ? 'bg-accent' : ''\n }`}\n >\n <span\n className={`w-1.5 h-1.5 rounded-full shrink-0 ${\n trigger.isEnabled ? 'bg-emerald-500' : 'bg-muted-foreground/40'\n }`}\n />\n <span className=\"truncate\">{trigger.name}</span>\n <span className=\"text-xs text-muted-foreground ml-auto shrink-0\">\n {trigger.provider}\n </span>\n </button>\n ))\n )}\n </div>\n </>\n )}\n </div>\n\n {/* Selected trigger info */}\n {selected && (\n <div className=\"space-y-2 p-3 rounded-lg bg-muted/30 border\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium\">{selected.name}</span>\n <span\n className={`inline-flex items-center rounded-full border border-transparent px-2 py-0.5 text-[10px] font-medium ${\n selected.isEnabled\n ? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400'\n : 'bg-zinc-100 text-zinc-600 dark:bg-zinc-800/40 dark:text-zinc-400'\n }`}\n >\n {selected.isEnabled ? 'Active' : 'Disabled'}\n </span>\n </div>\n <CopyableField value={`/plugins/webhooks/receive/${selected.webhookPath}`} />\n <a\n href=\"/invect/webhooks\"\n className=\"text-xs text-primary hover:underline inline-flex items-center gap-1\"\n >\n Manage webhooks <ExternalLink className=\"h-3 w-3\" />\n </a>\n </div>\n )}\n\n <CreateWebhookModal\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n flowId={flowId}\n nodeId={nodeId}\n />\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["AUTH_MODE_CONFIG","getAuthModeConfig"],"sources":["../../src/frontend/hooks/useWebhookQueries.ts","../../src/frontend/components/CopyableField.tsx","../../src/frontend/components/CreateWebhookModal.tsx","../../src/frontend/components/WebhookDetailPanel.tsx","../../src/frontend/components/WebhooksPage.tsx","../../src/frontend/plugins/webhooksFrontendPlugin.ts","../../src/frontend/components/WebhookTriggerSelector.tsx"],"sourcesContent":["/**\n * React hooks for the webhooks plugin API.\n *\n * Uses @invect/ui's ApiContext for the base URL.\n */\n\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';\nimport { useApiBaseURL } from '@invect/ui';\nimport type {\n WebhookTrigger,\n CreateWebhookTriggerInput,\n UpdateWebhookTriggerInput,\n WebhookTriggerInfo,\n} from '../../shared/types';\n\n// ─── API helper ─────────────────────────────────────────────────────\n\nasync function apiFetch<T>(baseUrl: string, path: string, init?: RequestInit): Promise<T> {\n const res = await fetch(`${baseUrl}${path}`, {\n headers: { 'Content-Type': 'application/json', ...init?.headers },\n credentials: 'include',\n ...init,\n });\n if (!res.ok) {\n const body = await res.json().catch(() => ({}));\n throw new Error((body as { error?: string }).error || `HTTP ${res.status}`);\n }\n return res.json() as Promise<T>;\n}\n\n// ─── Query Keys ─────────────────────────────────────────────────────\n\nconst keys = {\n all: ['webhooks'] as const,\n list: () => [...keys.all, 'list'] as const,\n detail: (id: string) => [...keys.all, 'detail', id] as const,\n info: (id: string) => [...keys.all, 'info', id] as const,\n};\n\n// ─── Queries ────────────────────────────────────────────────────────\n\nexport function useWebhookTriggers() {\n const baseUrl = useApiBaseURL();\n return useQuery({\n queryKey: keys.list(),\n queryFn: () =>\n apiFetch<{ data: WebhookTrigger[] }>(baseUrl, '/plugins/webhooks/triggers').then(\n (r) => r.data,\n ),\n });\n}\n\nexport function useWebhookTrigger(id: string | undefined) {\n const baseUrl = useApiBaseURL();\n return useQuery({\n queryKey: keys.detail(id ?? ''),\n queryFn: () => apiFetch<WebhookTrigger>(baseUrl, `/plugins/webhooks/triggers/${id}`),\n enabled: !!id,\n });\n}\n\nexport function useWebhookTriggerInfo(id: string | undefined) {\n const baseUrl = useApiBaseURL();\n return useQuery({\n queryKey: keys.info(id ?? ''),\n queryFn: () => apiFetch<WebhookTriggerInfo>(baseUrl, `/plugins/webhooks/triggers/${id}/info`),\n enabled: !!id,\n });\n}\n\n// ─── Mutations ──────────────────────────────────────────────────────\n\nexport function useCreateWebhookTrigger() {\n const baseUrl = useApiBaseURL();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (input: CreateWebhookTriggerInput) =>\n apiFetch<WebhookTrigger & { fullUrl?: string }>(baseUrl, '/plugins/webhooks/triggers', {\n method: 'POST',\n body: JSON.stringify(input),\n }),\n onSuccess: () => qc.invalidateQueries({ queryKey: keys.list() }),\n });\n}\n\nexport function useUpdateWebhookTrigger() {\n const baseUrl = useApiBaseURL();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: ({ id, ...input }: UpdateWebhookTriggerInput & { id: string }) =>\n apiFetch<WebhookTrigger>(baseUrl, `/plugins/webhooks/triggers/${id}`, {\n method: 'PUT',\n body: JSON.stringify(input),\n }),\n onSuccess: (_data, vars) => {\n qc.invalidateQueries({ queryKey: keys.list() });\n qc.invalidateQueries({ queryKey: keys.detail(vars.id) });\n },\n });\n}\n\nexport function useDeleteWebhookTrigger() {\n const baseUrl = useApiBaseURL();\n const qc = useQueryClient();\n return useMutation({\n mutationFn: (id: string) =>\n apiFetch<{ success: boolean }>(baseUrl, `/plugins/webhooks/triggers/${id}`, {\n method: 'DELETE',\n }),\n onSuccess: () => qc.invalidateQueries({ queryKey: keys.list() }),\n });\n}\n\nexport function useTestWebhookTrigger() {\n const baseUrl = useApiBaseURL();\n return useMutation({\n mutationFn: ({ id, payload }: { id: string; payload?: unknown }) =>\n apiFetch<{ status: string }>(baseUrl, `/plugins/webhooks/triggers/${id}/test`, {\n method: 'POST',\n body: JSON.stringify(payload ?? { test: true }),\n }),\n });\n}\n","/**\n * CopyableField — Displays a value with copy-to-clipboard and optional masking.\n *\n * Follows the pattern from the Credentials page.\n */\n\nimport { useState, type FC } from 'react';\nimport { Copy, Check, Eye, EyeOff } from 'lucide-react';\n\ninterface CopyableFieldProps {\n value: string;\n masked?: boolean;\n}\n\nexport const CopyableField: FC<CopyableFieldProps> = ({ value, masked = false }) => {\n const [copied, setCopied] = useState(false);\n const [revealed, setRevealed] = useState(!masked);\n\n const copy = async () => {\n await navigator.clipboard.writeText(value);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n const displayValue = revealed ? value : '•'.repeat(Math.min(value.length, 32));\n\n return (\n <div className=\"flex items-center gap-1.5\">\n <code className=\"flex-1 truncate rounded bg-muted px-2 py-1.5 font-mono text-[11px]\">\n {displayValue}\n </code>\n {masked && (\n <button\n onClick={() => setRevealed(!revealed)}\n className=\"shrink-0 rounded p-1 hover:bg-muted text-muted-foreground transition-colors\"\n title={revealed ? 'Hide' : 'Reveal'}\n >\n {revealed ? <EyeOff className=\"h-3.5 w-3.5\" /> : <Eye className=\"h-3.5 w-3.5\" />}\n </button>\n )}\n <button\n onClick={copy}\n className=\"shrink-0 rounded p-1 hover:bg-muted text-muted-foreground transition-colors\"\n title=\"Copy to clipboard\"\n >\n {copied ? (\n <Check className=\"h-3.5 w-3.5 text-emerald-500\" />\n ) : (\n <Copy className=\"h-3.5 w-3.5\" />\n )}\n </button>\n </div>\n );\n};\n","/**\n * CreateWebhookModal — Dialog for creating a new webhook trigger.\n *\n * Step 1: Name + endpoint settings\n * Step 2: Success screen with URL + secret to copy\n */\n\nimport { useState, type FC, type RefObject } from 'react';\nimport { Check } from 'lucide-react';\nimport { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@invect/ui';\nimport { useCreateWebhookTrigger } from '../hooks/useWebhookQueries';\nimport { CopyableField } from './CopyableField';\nimport type { CreateWebhookTriggerInput } from '../../shared/types';\n\n// ─── Component ──────────────────────────────────────────────────────\n\ninterface CreateWebhookModalProps {\n open: boolean;\n onClose: () => void;\n containerRef?: RefObject<HTMLElement | null>;\n /** Pre-fill flowId + nodeId when creating from flow editor */\n flowId?: string;\n nodeId?: string;\n}\n\nexport const CreateWebhookModal: FC<CreateWebhookModalProps> = ({\n open,\n onClose,\n containerRef,\n flowId,\n nodeId,\n}) => {\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [methods, setMethods] = useState('POST');\n const [hmacEnabled, setHmacEnabled] = useState(false);\n const [hmacHeaderName, setHmacHeaderName] = useState('');\n const [hmacSecret, setHmacSecret] = useState('');\n const [allowedIps, setAllowedIps] = useState('');\n const [createdUrl, setCreatedUrl] = useState<string | null>(null);\n\n const createMutation = useCreateWebhookTrigger();\n\n const handleCreate = async () => {\n if (!name.trim()) {\n return;\n }\n\n const input: CreateWebhookTriggerInput = {\n name: name.trim(),\n description: description.trim() || undefined,\n provider: 'generic',\n allowedMethods: methods,\n hmacEnabled,\n hmacHeaderName: hmacEnabled ? hmacHeaderName.trim() || undefined : undefined,\n hmacSecret: hmacEnabled ? hmacSecret.trim() || undefined : undefined,\n allowedIps: allowedIps.trim() || undefined,\n flowId,\n nodeId,\n };\n\n try {\n const result = await createMutation.mutateAsync(input);\n setCreatedUrl(result.fullUrl ?? `/plugins/webhooks/receive/${result.webhookPath}`);\n } catch {\n // Error handled by mutation state\n }\n };\n\n const handleClose = () => {\n setName('');\n setDescription('');\n setMethods('POST');\n setHmacEnabled(false);\n setHmacHeaderName('');\n setHmacSecret('');\n setAllowedIps('');\n setCreatedUrl(null);\n onClose();\n };\n\n return (\n <Dialog\n open={open}\n onOpenChange={(v) => {\n if (!v) {\n handleClose();\n }\n }}\n >\n <DialogContent\n container={containerRef?.current}\n className=\"max-w-md gap-0 p-0 overflow-hidden\"\n showCloseButton\n >\n {/* Header */}\n <div className=\"px-6 pt-6 pb-4\">\n <DialogHeader>\n <DialogTitle className=\"text-base\">\n {createdUrl ? 'Webhook Created' : 'Create Webhook'}\n </DialogTitle>\n <DialogDescription>\n {createdUrl\n ? 'Copy the URL below and configure it in your external service.'\n : 'Set up a generic endpoint to receive webhook events from any external system.'}\n </DialogDescription>\n </DialogHeader>\n </div>\n\n {/* Content */}\n <div className=\"px-6 pb-6 space-y-4\">\n {createdUrl ? (\n /* ── Success ── */\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-2 p-3 text-sm border rounded-lg border-emerald-200 bg-emerald-50 dark:border-emerald-800 dark:bg-emerald-950/30 text-emerald-700 dark:text-emerald-400\">\n <Check className=\"w-4 h-4 shrink-0\" />\n Webhook is ready to receive events.\n </div>\n\n <div className=\"space-y-1\">\n <label className=\"text-xs font-medium tracking-wide uppercase text-muted-foreground\">\n Webhook URL\n </label>\n <CopyableField value={createdUrl} />\n </div>\n\n <button\n onClick={handleClose}\n className=\"inline-flex items-center justify-center w-full px-4 text-sm font-medium transition-colors rounded-md h-9 bg-primary text-primary-foreground hover:bg-primary/90\"\n >\n Done\n </button>\n </div>\n ) : (\n /* ── Form ── */\n <>\n {/* Name */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-create-name\">\n Name *\n </label>\n <input\n id=\"wh-create-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Partner API Events\"\n autoFocus\n className=\"h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n\n {/* Description */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-create-desc\">\n Description\n </label>\n <input\n id=\"wh-create-desc\"\n type=\"text\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Optional description\"\n className=\"h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Authentication</label>\n <div className=\"space-y-3 rounded-lg border border-border bg-muted/30 p-3\">\n <div className=\"flex items-center justify-between\">\n <div>\n <div className=\"text-sm font-medium\">HMAC Verification</div>\n <p className=\"mt-0.5 text-xs text-muted-foreground\">\n Verify requests using an HMAC signature header.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => setHmacEnabled(!hmacEnabled)}\n className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors ${\n hmacEnabled ? 'bg-primary' : 'bg-muted-foreground/30'\n }`}\n >\n <span\n className={`pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform ${\n hmacEnabled ? 'translate-x-4' : 'translate-x-0'\n }`}\n />\n </button>\n </div>\n {hmacEnabled && (\n <div className=\"space-y-3 pt-1\">\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium\" htmlFor=\"wh-create-hmac-header\">\n Signature Header Name\n </label>\n <input\n id=\"wh-create-hmac-header\"\n type=\"text\"\n autoComplete=\"off\"\n value={hmacHeaderName}\n onChange={(e) => setHmacHeaderName(e.target.value)}\n placeholder=\"e.g. x-signature\"\n className=\"h-8 w-full rounded-md border border-input bg-background px-2.5 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium\" htmlFor=\"wh-create-hmac-secret\">\n Signing Secret\n </label>\n <input\n id=\"wh-create-hmac-secret\"\n type=\"password\"\n autoComplete=\"new-password\"\n value={hmacSecret}\n onChange={(e) => setHmacSecret(e.target.value)}\n placeholder=\"Enter secret key\"\n className=\"h-8 w-full rounded-md border border-input bg-background px-2.5 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* IP Whitelist */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-create-ips\">\n IP Whitelist\n </label>\n <input\n id=\"wh-create-ips\"\n type=\"text\"\n value={allowedIps}\n onChange={(e) => setAllowedIps(e.target.value)}\n placeholder=\"e.g. 192.168.1.1, 10.0.0.0/24\"\n className=\"h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm font-mono shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Leave empty to allow all IPs. Separate multiple addresses with commas.\n </p>\n </div>\n\n {/* HTTP Methods */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-create-methods\">\n HTTP Methods\n </label>\n <select\n id=\"wh-create-methods\"\n value={methods}\n onChange={(e) => setMethods(e.target.value)}\n className=\"flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n >\n <option value=\"POST\">POST only</option>\n <option value=\"POST,PUT\">POST + PUT</option>\n <option value=\"ANY\">Any method</option>\n </select>\n </div>\n\n {/* Error */}\n {createMutation.isError && (\n <p className=\"text-sm text-red-500\">\n {createMutation.error?.message || 'Failed to create webhook'}\n </p>\n )}\n\n {/* Buttons */}\n <div className=\"flex gap-2 pt-2\">\n <button\n type=\"button\"\n onClick={handleClose}\n className=\"inline-flex items-center justify-center flex-1 px-3 text-sm font-medium transition-colors border rounded-md shadow-xs h-9 bg-background hover:bg-accent\"\n >\n Cancel\n </button>\n <button\n onClick={handleCreate}\n disabled={!name.trim() || createMutation.isPending}\n className=\"inline-flex items-center justify-center flex-1 px-3 text-sm font-medium transition-colors rounded-md h-9 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n {createMutation.isPending ? 'Creating…' : 'Create Webhook'}\n </button>\n </div>\n </>\n )}\n </div>\n </DialogContent>\n </Dialog>\n );\n};\n","/**\n * WebhookDetailPanel — Detail view rendered inside a Dialog.\n *\n * Tabs: Overview | Edit\n * Shows webhook URL, secret, status, linked flow, stats.\n * Allows toggling enabled/disabled, editing, and deleting.\n */\n\nimport { useState, type FC } from 'react';\nimport {\n Globe,\n Trash2,\n ExternalLink,\n Clock,\n Hash,\n ToggleLeft,\n ToggleRight,\n Workflow,\n Loader2,\n} from 'lucide-react';\nimport { DialogHeader, DialogTitle, DialogDescription } from '@invect/ui';\nimport { useUpdateWebhookTrigger, useDeleteWebhookTrigger } from '../hooks/useWebhookQueries';\nimport { CopyableField } from './CopyableField';\nimport type { WebhookTrigger, UpdateWebhookTriggerInput } from '../../shared/types';\n\n// ─── Constants ──────────────────────────────────────────────────────\n\nconst AUTH_MODE_CONFIG: Record<string, { label: string; color: string }> = {\n generic: {\n label: 'Unauthenticated',\n color: 'bg-zinc-100 text-zinc-600 dark:bg-zinc-800/40 dark:text-zinc-400',\n },\n hmac: {\n label: 'HMAC',\n color: 'bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-300',\n },\n ip_whitelist: {\n label: 'IP Whitelist',\n color: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300',\n },\n signed: {\n label: 'Signed',\n color: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',\n },\n};\n\nfunction getAuthModeConfig(trigger: WebhookTrigger) {\n if (trigger.provider !== 'generic') {\n return AUTH_MODE_CONFIG.signed;\n }\n if (trigger.hmacEnabled) {\n return AUTH_MODE_CONFIG.hmac;\n }\n if (trigger.allowedIps) {\n return AUTH_MODE_CONFIG.ip_whitelist;\n }\n return AUTH_MODE_CONFIG.generic;\n}\n\nfunction formatFullDate(iso?: string): string {\n if (!iso) {\n return 'Never';\n }\n return new Date(iso).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\n// ─── Component ──────────────────────────────────────────────────────\n\ntype Section = 'overview' | 'edit';\n\ninterface WebhookDetailPanelProps {\n trigger: WebhookTrigger;\n flowName?: string;\n onClose: () => void;\n}\n\nexport const WebhookDetailPanel: FC<WebhookDetailPanelProps> = ({ trigger, flowName, onClose }) => {\n const [section, setSection] = useState<Section>('overview');\n const updateMutation = useUpdateWebhookTrigger();\n const deleteMutation = useDeleteWebhookTrigger();\n\n const authModeConfig = getAuthModeConfig(trigger);\n\n const handleToggleEnabled = () => {\n updateMutation.mutate({ id: trigger.id, isEnabled: !trigger.isEnabled });\n };\n\n const handleDelete = () => {\n if (confirm(`Delete webhook \"${trigger.name}\"? This cannot be undone.`)) {\n deleteMutation.mutate(trigger.id, { onSuccess: onClose });\n }\n };\n\n return (\n <>\n {/* Fixed header */}\n <div className=\"px-6 pt-6 pb-0\">\n <DialogHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center justify-center w-10 h-10 rounded-lg bg-muted/60 shrink-0\">\n <Globe className=\"w-5 h-5 text-muted-foreground\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <DialogTitle className=\"text-base\">{trigger.name}</DialogTitle>\n <DialogDescription className=\"flex items-center gap-2 mt-0.5 text-xs\">\n <span\n className={`inline-flex items-center rounded-full border border-transparent px-2 py-0.5 text-[10px] font-medium ${authModeConfig.color}`}\n >\n {authModeConfig.label}\n </span>\n <span\n className={`w-1.5 h-1.5 rounded-full ${\n trigger.isEnabled ? 'bg-emerald-500' : 'bg-muted-foreground/40'\n }`}\n />\n <span>{trigger.isEnabled ? 'Enabled' : 'Disabled'}</span>\n </DialogDescription>\n </div>\n </div>\n </DialogHeader>\n\n {/* Tab nav */}\n <div className=\"flex gap-1 px-6 mt-4 -mx-6 border-b\">\n {[\n { key: 'overview' as const, label: 'Overview' },\n { key: 'edit' as const, label: 'Edit' },\n ].map(({ key, label }) => (\n <button\n key={key}\n onClick={() => setSection(key)}\n className={`px-3 py-2 text-sm font-medium border-b-2 -mb-px transition-colors ${\n section === key\n ? 'border-foreground text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {label}\n </button>\n ))}\n </div>\n </div>\n\n {/* Scrollable content */}\n <div className=\"flex-1 px-6 pb-6 overflow-y-auto\">\n {section === 'overview' ? (\n <OverviewSection\n trigger={trigger}\n flowName={flowName}\n onToggleEnabled={handleToggleEnabled}\n onDelete={handleDelete}\n isToggling={updateMutation.isPending}\n isDeleting={deleteMutation.isPending}\n />\n ) : (\n <EditSection trigger={trigger} onSuccess={() => setSection('overview')} />\n )}\n </div>\n </>\n );\n};\n\n// ─── Overview Section ───────────────────────────────────────────────\n\nconst OverviewSection: FC<{\n trigger: WebhookTrigger;\n flowName?: string;\n onToggleEnabled: () => void;\n onDelete: () => void;\n isToggling: boolean;\n isDeleting: boolean;\n}> = ({ trigger, flowName, onToggleEnabled, onDelete, isToggling, isDeleting }) => (\n <div className=\"pt-4 space-y-5\">\n {/* Endpoint URL */}\n <div className=\"space-y-1\">\n <label className=\"text-xs font-medium tracking-wide uppercase text-muted-foreground\">\n Webhook URL\n </label>\n <CopyableField value={`/plugins/webhooks/receive/${trigger.webhookPath}`} />\n </div>\n\n {/* HMAC Authentication */}\n {trigger.hmacEnabled && trigger.hmacHeaderName && (\n <div className=\"space-y-1\">\n <label className=\"text-xs font-medium tracking-wide uppercase text-muted-foreground\">\n HMAC Authentication\n </label>\n <div className=\"p-3 space-y-1 border rounded-lg bg-muted/30\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-muted-foreground\">Signature Header</span>\n <span className=\"font-mono text-sm\">{trigger.hmacHeaderName}</span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-xs text-muted-foreground\">Secret</span>\n <CopyableField value={trigger.hmacSecret ?? ''} masked />\n </div>\n </div>\n </div>\n )}\n\n {/* IP Whitelist */}\n {trigger.allowedIps && (\n <div className=\"space-y-1\">\n <label className=\"text-xs font-medium tracking-wide uppercase text-muted-foreground\">\n IP Whitelist\n </label>\n <div className=\"p-3 border rounded-lg bg-muted/30\">\n <span className=\"font-mono text-sm break-all\">{trigger.allowedIps}</span>\n </div>\n </div>\n )}\n\n {/* Info grid */}\n <div className=\"grid grid-cols-2 gap-4 py-2\">\n <div>\n <span className=\"block mb-1 text-xs text-muted-foreground\">Methods</span>\n <span className=\"font-mono text-sm\">{trigger.allowedMethods}</span>\n </div>\n <div>\n <span className=\"block mb-1 text-xs text-muted-foreground\">Authentication</span>\n <span className=\"text-sm\">{getAuthModeConfig(trigger).label}</span>\n </div>\n <div>\n <span className=\"block mb-1 text-xs text-muted-foreground\">Created</span>\n <span className=\"text-sm\">{formatFullDate(trigger.createdAt)}</span>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <Hash className=\"w-3 h-3 text-muted-foreground\" />\n <span className=\"text-xs text-muted-foreground\">Triggers:</span>\n <span className=\"text-sm font-medium\">{trigger.triggerCount}</span>\n </div>\n <div className=\"flex items-center gap-1.5\">\n <Clock className=\"w-3 h-3 text-muted-foreground\" />\n <span className=\"text-xs text-muted-foreground\">Last:</span>\n <span className=\"text-sm\">{formatFullDate(trigger.lastTriggeredAt)}</span>\n </div>\n </div>\n\n {/* Linked flow */}\n {trigger.flowId && (\n <div className=\"p-3 border rounded-lg bg-muted/30\">\n <div className=\"flex items-center gap-2\">\n <Workflow className=\"w-4 h-4 text-muted-foreground shrink-0\" />\n <div className=\"flex-1 min-w-0\">\n <span className=\"block text-xs text-muted-foreground\">Linked Flow</span>\n <a\n href={`/invect/flow/${trigger.flowId}`}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {flowName ?? trigger.flowId}\n <ExternalLink className=\"w-3 h-3\" />\n </a>\n </div>\n </div>\n </div>\n )}\n\n {/* Last payload */}\n {trigger.lastPayload !== null && trigger.lastPayload !== undefined && (\n <div className=\"space-y-1\">\n <label className=\"text-xs font-medium tracking-wide uppercase text-muted-foreground\">\n Last Payload\n </label>\n <pre className=\"p-3 overflow-auto font-mono text-xs rounded-lg bg-muted max-h-32\">\n {JSON.stringify(trigger.lastPayload, null, 2)}\n </pre>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center justify-between pt-3 border-t\">\n <button\n onClick={onToggleEnabled}\n disabled={isToggling}\n className={`inline-flex items-center gap-2 rounded-md text-sm font-medium h-8 px-3 border transition-colors ${\n trigger.isEnabled\n ? 'border-amber-200 text-amber-700 hover:bg-amber-50 dark:border-amber-800 dark:text-amber-400 dark:hover:bg-amber-950/30'\n : 'border-emerald-200 text-emerald-700 hover:bg-emerald-50 dark:border-emerald-800 dark:text-emerald-400 dark:hover:bg-emerald-950/30'\n }`}\n >\n {isToggling ? (\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n ) : trigger.isEnabled ? (\n <ToggleLeft className=\"w-3.5 h-3.5\" />\n ) : (\n <ToggleRight className=\"w-3.5 h-3.5\" />\n )}\n {trigger.isEnabled ? 'Disable' : 'Enable'}\n </button>\n\n <button\n onClick={onDelete}\n disabled={isDeleting}\n className=\"inline-flex items-center h-8 gap-2 px-3 text-sm font-medium text-imp-destructive transition-colors border border-imp-destructive/30 rounded-md hover:bg-imp-destructive/10\"\n >\n {isDeleting ? (\n <Loader2 className=\"w-3.5 h-3.5 animate-spin\" />\n ) : (\n <Trash2 className=\"w-3.5 h-3.5\" />\n )}\n Delete\n </button>\n </div>\n </div>\n);\n\n// ─── Edit Section ───────────────────────────────────────────────────\n\nconst EditSection: FC<{\n trigger: WebhookTrigger;\n onSuccess: () => void;\n}> = ({ trigger, onSuccess }) => {\n const [name, setName] = useState(trigger.name);\n const [description, setDescription] = useState(trigger.description ?? '');\n const [methods, setMethods] = useState(trigger.allowedMethods);\n const [hmacEnabled, setHmacEnabled] = useState(trigger.hmacEnabled);\n const [hmacHeaderName, setHmacHeaderName] = useState(trigger.hmacHeaderName ?? '');\n const [hmacSecret, setHmacSecret] = useState(trigger.hmacSecret ?? '');\n const [allowedIps, setAllowedIps] = useState(trigger.allowedIps ?? '');\n const updateMutation = useUpdateWebhookTrigger();\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!name.trim()) {\n return;\n }\n\n const input: UpdateWebhookTriggerInput & { id: string } = {\n id: trigger.id,\n name: name.trim(),\n description: description.trim() || undefined,\n allowedMethods: methods,\n hmacEnabled,\n hmacHeaderName: hmacEnabled ? hmacHeaderName.trim() || undefined : undefined,\n hmacSecret: hmacEnabled ? hmacSecret.trim() || undefined : undefined,\n allowedIps: allowedIps.trim() || undefined,\n };\n updateMutation.mutate(input, { onSuccess });\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"pt-4 space-y-4\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-edit-name\">\n Name *\n </label>\n <input\n id=\"wh-edit-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n required\n className=\"h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-edit-desc\">\n Description\n </label>\n <textarea\n id=\"wh-edit-desc\"\n rows={2}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Optional description\"\n className=\"w-full min-h-16 rounded-md border border-input bg-background px-3 py-2 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20 field-sizing-content\"\n />\n </div>\n\n {/* HMAC Authentication */}\n <div className=\"p-3 space-y-3 border rounded-lg border-border bg-muted/30\">\n <div className=\"flex items-center justify-between\">\n <div>\n <div className=\"text-sm font-medium\">HMAC Authentication</div>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n Verify incoming requests using an HMAC signature header.\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => setHmacEnabled(!hmacEnabled)}\n className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors ${\n hmacEnabled ? 'bg-primary' : 'bg-muted-foreground/30'\n }`}\n >\n <span\n className={`pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform ${\n hmacEnabled ? 'translate-x-4' : 'translate-x-0'\n }`}\n />\n </button>\n </div>\n {hmacEnabled && (\n <div className=\"pt-1 space-y-3\">\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium\" htmlFor=\"wh-edit-hmac-header\">\n Signature Header Name\n </label>\n <input\n id=\"wh-edit-hmac-header\"\n type=\"text\"\n value={hmacHeaderName}\n onChange={(e) => setHmacHeaderName(e.target.value)}\n placeholder=\"e.g. x-signature\"\n className=\"h-8 w-full rounded-md border border-input bg-background px-2.5 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium\" htmlFor=\"wh-edit-hmac-secret\">\n Signing Secret\n </label>\n <input\n id=\"wh-edit-hmac-secret\"\n type=\"password\"\n value={hmacSecret}\n onChange={(e) => setHmacSecret(e.target.value)}\n placeholder=\"Enter secret key\"\n className=\"h-8 w-full rounded-md border border-input bg-background px-2.5 text-sm shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n />\n </div>\n </div>\n )}\n </div>\n\n {/* IP Whitelist */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-edit-ips\">\n IP Whitelist\n </label>\n <textarea\n id=\"wh-edit-ips\"\n rows={2}\n value={allowedIps}\n onChange={(e) => setAllowedIps(e.target.value)}\n placeholder=\"Comma-separated IPs, e.g. 192.168.1.1, 10.0.0.0/24\"\n className=\"w-full min-h-16 rounded-md border border-input bg-background px-3 py-2 text-sm font-mono shadow-xs placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20 field-sizing-content\"\n />\n <p className=\"text-xs text-muted-foreground\">\n Leave empty to allow all IPs. Separate multiple addresses with commas.\n </p>\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor=\"wh-edit-methods\">\n HTTP Methods\n </label>\n <select\n id=\"wh-edit-methods\"\n value={methods}\n onChange={(e) => setMethods(e.target.value)}\n className=\"flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-xs focus-visible:border-ring focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n >\n <option value=\"POST\">POST only</option>\n <option value=\"POST,PUT\">POST + PUT</option>\n <option value=\"ANY\">Any method</option>\n </select>\n </div>\n\n {updateMutation.isError && (\n <p className=\"text-sm text-red-500\">\n {updateMutation.error?.message || 'Failed to save changes'}\n </p>\n )}\n\n <div className=\"flex justify-end gap-2 pt-2\">\n <button\n type=\"button\"\n onClick={onSuccess}\n className=\"inline-flex items-center justify-center h-8 px-3 text-sm font-medium transition-colors border rounded-md shadow-xs bg-background hover:bg-accent\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={!name.trim() || updateMutation.isPending}\n className=\"inline-flex items-center justify-center h-8 px-3 text-sm font-medium transition-colors rounded-md bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n {updateMutation.isPending ? 'Saving…' : 'Save Changes'}\n </button>\n </div>\n </form>\n );\n};\n","/**\n * WebhooksPage — Main webhook management page.\n *\n * Lists all webhook triggers with status, auth mode, linked flows, and last activity.\n * Click a row to open the detail dialog for editing, toggling, or deleting.\n */\n\nimport { useState, useRef, type FC } from 'react';\nimport { Globe, Plus, Search, Clock, Hash, ChevronRight, Loader2, Workflow } from 'lucide-react';\nimport { PageLayout, Dialog, DialogContent, useFlows } from '@invect/ui';\nimport { useWebhookTriggers } from '../hooks/useWebhookQueries';\nimport { CreateWebhookModal } from './CreateWebhookModal';\nimport { WebhookDetailPanel } from './WebhookDetailPanel';\nimport type { WebhookTrigger } from '../../shared/types';\n\n// ─── Constants ──────────────────────────────────────────────────────\n\nconst AUTH_MODE_CONFIG: Record<string, { label: string; color: string }> = {\n generic: {\n label: 'Unauthenticated',\n color: 'bg-zinc-100 text-zinc-600 dark:bg-zinc-800/40 dark:text-zinc-400',\n },\n hmac: {\n label: 'HMAC',\n color: 'bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-300',\n },\n ip_whitelist: {\n label: 'IP Whitelist',\n color: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300',\n },\n signed: {\n label: 'Signed',\n color: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300',\n },\n};\n\nfunction getAuthModeConfig(trigger: WebhookTrigger) {\n if (trigger.provider !== 'generic') {\n return AUTH_MODE_CONFIG.signed;\n }\n if (trigger.hmacEnabled) {\n return AUTH_MODE_CONFIG.hmac;\n }\n if (trigger.allowedIps) {\n return AUTH_MODE_CONFIG.ip_whitelist;\n }\n return AUTH_MODE_CONFIG.generic;\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────\n\nfunction formatRelativeTime(iso?: string): string {\n if (!iso) {\n return 'Never';\n }\n const diff = Date.now() - new Date(iso).getTime();\n const mins = Math.floor(diff / 60_000);\n if (mins < 1) {\n return 'Just now';\n }\n if (mins < 60) {\n return `${mins}m ago`;\n }\n const hours = Math.floor(mins / 60);\n if (hours < 24) {\n return `${hours}h ago`;\n }\n const days = Math.floor(hours / 24);\n if (days < 30) {\n return `${days}d ago`;\n }\n return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n}\n\n// ─── Webhook Row ────────────────────────────────────────────────────\n\nconst WebhookRow: FC<{\n trigger: WebhookTrigger;\n flowName?: string;\n onClick: () => void;\n}> = ({ trigger, flowName, onClick }) => {\n const authModeConfig = getAuthModeConfig(trigger);\n\n return (\n <button\n className=\"w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-muted/50 transition-colors\"\n onClick={onClick}\n >\n {/* Icon */}\n <div className=\"flex items-center justify-center w-9 h-9 rounded-lg bg-muted/60 shrink-0\">\n <Globe className=\"w-4 h-4 text-muted-foreground\" />\n </div>\n\n {/* Name + subtitle */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium truncate\">{trigger.name}</span>\n <span\n className={`w-1.5 h-1.5 rounded-full shrink-0 ${\n trigger.isEnabled ? 'bg-emerald-500' : 'bg-muted-foreground/40'\n }`}\n />\n </div>\n <div className=\"flex items-center gap-2 mt-0.5\">\n {flowName ? (\n <span className=\"text-xs text-muted-foreground flex items-center gap-1 truncate\">\n <Workflow className=\"w-3 h-3 shrink-0\" />\n {flowName}\n </span>\n ) : trigger.description ? (\n <span className=\"text-xs text-muted-foreground truncate\">{trigger.description}</span>\n ) : (\n <span className=\"text-xs text-muted-foreground/50\">No flow linked</span>\n )}\n </div>\n </div>\n\n {/* Auth badge */}\n <span\n className={`hidden sm:inline-flex items-center rounded-full border border-transparent px-2 py-0.5 text-[10px] font-medium ${authModeConfig.color}`}\n >\n {authModeConfig.label}\n </span>\n\n {/* Stats */}\n <div className=\"hidden md:flex items-center gap-4 text-xs text-muted-foreground shrink-0\">\n <span className=\"flex items-center gap-1\" title=\"Total triggers\">\n <Hash className=\"w-3 h-3\" />\n {trigger.triggerCount}\n </span>\n <span className=\"flex items-center gap-1 w-16 justify-end\" title=\"Last triggered\">\n <Clock className=\"w-3 h-3\" />\n {formatRelativeTime(trigger.lastTriggeredAt)}\n </span>\n </div>\n\n <ChevronRight className=\"w-4 h-4 text-muted-foreground/50 shrink-0\" />\n </button>\n );\n};\n\n// ─── Main Page ──────────────────────────────────────────────────────\n\nexport const WebhooksPage: FC<{ basePath: string }> = () => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [createOpen, setCreateOpen] = useState(false);\n const [selectedTrigger, setSelectedTrigger] = useState<WebhookTrigger | null>(null);\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState<'all' | 'enabled' | 'disabled'>('all');\n\n const { data: triggers, isLoading, error } = useWebhookTriggers();\n const { data: flowsResponse } = useFlows();\n\n // Build flow name lookup\n const flowNameMap = new Map<string, string>();\n for (const flow of flowsResponse?.data ?? []) {\n flowNameMap.set(flow.id, flow.name);\n }\n\n // Filter\n const filtered = (triggers ?? []).filter((t) => {\n if (statusFilter === 'enabled' && !t.isEnabled) {\n return false;\n }\n if (statusFilter === 'disabled' && t.isEnabled) {\n return false;\n }\n if (searchQuery) {\n const q = searchQuery.toLowerCase();\n const nameMatch = t.name.toLowerCase().includes(q);\n const flowMatch = t.flowId\n ? (flowNameMap.get(t.flowId) ?? '').toLowerCase().includes(q)\n : false;\n if (!nameMatch && !flowMatch) {\n return false;\n }\n }\n return true;\n });\n\n const enabledCount = (triggers ?? []).filter((t) => t.isEnabled).length;\n const disabledCount = (triggers ?? []).filter((t) => !t.isEnabled).length;\n\n // Keep detail dialog in sync with list data\n const liveTrigger = selectedTrigger\n ? ((triggers ?? []).find((t) => t.id === selectedTrigger.id) ?? selectedTrigger)\n : null;\n\n return (\n <PageLayout\n title=\"Webhooks\"\n subtitle=\"Receive events from external systems and route them into your flows.\"\n icon={Globe}\n actions={\n <button\n onClick={() => setCreateOpen(true)}\n className=\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium h-9 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n New Webhook\n </button>\n }\n >\n <div ref={containerRef}>\n {/* Search + Filters */}\n <div className=\"flex flex-col gap-3 mb-4 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1 max-w-sm\">\n <Search className=\"absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 pointer-events-none text-muted-foreground\" />\n <input\n type=\"text\"\n placeholder=\"Search webhooks…\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full rounded-lg border border-border bg-transparent py-2 pl-9 pr-3 text-sm outline-none placeholder:text-muted-foreground focus:border-primary/50\"\n />\n </div>\n\n <div className=\"flex items-center gap-1.5 flex-wrap\">\n {[\n { key: 'all' as const, label: `All (${triggers?.length ?? 0})` },\n { key: 'enabled' as const, label: `Enabled (${enabledCount})` },\n { key: 'disabled' as const, label: `Disabled (${disabledCount})` },\n ].map(({ key, label }) => (\n <button\n key={key}\n onClick={() => setStatusFilter(key)}\n className={`px-2.5 py-1 text-xs font-medium rounded-full border transition-colors ${\n statusFilter === key\n ? 'bg-foreground text-background border-foreground'\n : 'bg-card text-muted-foreground border-border hover:border-foreground/30'\n }`}\n >\n {label}\n </button>\n ))}\n </div>\n </div>\n\n {/* Content */}\n {isLoading ? (\n <div className=\"flex items-center justify-center gap-2 py-16 text-sm text-muted-foreground\">\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n Loading webhooks…\n </div>\n ) : error ? (\n <div className=\"rounded-lg border border-red-200 bg-red-50 dark:border-red-900 dark:bg-red-950/30 p-4 text-sm text-red-600 dark:text-red-400\">\n Failed to load webhooks: {(error as Error).message}\n </div>\n ) : filtered.length === 0 ? (\n <div className=\"flex flex-col items-center py-16 text-center\">\n <div className=\"rounded-full bg-muted p-3 mb-4\">\n <Globe className=\"w-6 h-6 text-muted-foreground\" />\n </div>\n <h3 className=\"text-sm font-semibold mb-1\">\n {triggers?.length === 0 ? 'No webhooks yet' : 'No webhooks match your filter'}\n </h3>\n <p className=\"max-w-sm text-sm text-muted-foreground mb-4\">\n {triggers?.length === 0\n ? 'Create a generic webhook endpoint to receive events from external systems.'\n : 'Try adjusting your search or filter criteria.'}\n </p>\n {triggers?.length === 0 && (\n <button\n onClick={() => setCreateOpen(true)}\n className=\"inline-flex items-center gap-2 rounded-md text-sm font-medium h-9 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n Create Webhook\n </button>\n )}\n </div>\n ) : (\n <div className=\"overflow-hidden rounded-lg border bg-card\">\n <div className=\"divide-y\">\n {filtered.map((trigger) => (\n <WebhookRow\n key={trigger.id}\n trigger={trigger}\n flowName={trigger.flowId ? flowNameMap.get(trigger.flowId) : undefined}\n onClick={() => setSelectedTrigger(trigger)}\n />\n ))}\n </div>\n </div>\n )}\n </div>\n\n {/* Detail Dialog */}\n <Dialog\n open={!!selectedTrigger}\n onOpenChange={(open) => {\n if (!open) {\n setSelectedTrigger(null);\n }\n }}\n >\n <DialogContent\n container={containerRef.current}\n className=\"max-w-2xl h-[32rem] flex flex-col gap-0 p-0 overflow-hidden\"\n showCloseButton\n >\n {liveTrigger && (\n <WebhookDetailPanel\n trigger={liveTrigger}\n flowName={liveTrigger.flowId ? flowNameMap.get(liveTrigger.flowId) : undefined}\n onClose={() => setSelectedTrigger(null)}\n />\n )}\n </DialogContent>\n </Dialog>\n\n {/* Create Modal */}\n <CreateWebhookModal\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n containerRef={containerRef}\n />\n </PageLayout>\n );\n};\n","/**\n * Webhooks Frontend Plugin — registers the sidebar item and route\n * for webhook management.\n */\n\nimport { Globe } from 'lucide-react';\nimport { WebhooksPage } from '../components/WebhooksPage';\nimport type { InvectFrontendPlugin } from '@invect/ui';\n\nexport const webhooksFrontend: InvectFrontendPlugin = {\n id: 'webhooks',\n name: 'Webhooks',\n\n sidebar: [\n {\n label: 'Webhooks',\n icon: Globe,\n path: '/webhooks',\n position: 'top',\n },\n ],\n\n routes: [\n {\n path: '/webhooks',\n component: WebhooksPage,\n },\n ],\n};\n","/**\n * WebhookTriggerSelector — Dropdown to pick an existing webhook trigger\n * or create a new one inline. Used in the trigger.webhook node's config panel.\n */\n\nimport { useState, type FC } from 'react';\nimport { Globe, Plus, ChevronDown, ExternalLink } from 'lucide-react';\nimport { useWebhookTriggers } from '../hooks/useWebhookQueries';\nimport { CreateWebhookModal } from './CreateWebhookModal';\nimport { CopyableField } from './CopyableField';\nimport type { WebhookTrigger } from '../../shared/types';\n\ninterface WebhookTriggerSelectorProps {\n selectedId?: string;\n onSelect: (trigger: WebhookTrigger) => void;\n flowId?: string;\n nodeId?: string;\n}\n\nexport const WebhookTriggerSelector: FC<WebhookTriggerSelectorProps> = ({\n selectedId,\n onSelect,\n flowId,\n nodeId,\n}) => {\n const [dropdownOpen, setDropdownOpen] = useState(false);\n const [createOpen, setCreateOpen] = useState(false);\n const { data: triggers, isLoading } = useWebhookTriggers();\n\n const selected = triggers?.find((t) => t.id === selectedId);\n\n return (\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\">Webhook Trigger</label>\n\n {/* Dropdown */}\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setDropdownOpen(!dropdownOpen)}\n className=\"flex h-9 w-full items-center justify-between rounded-md border border-input bg-background px-3 text-sm shadow-xs hover:bg-accent/50 transition-colors\"\n >\n <span className=\"flex items-center gap-2 truncate\">\n <Globe className=\"h-3.5 w-3.5 text-muted-foreground shrink-0\" />\n {selected ? (\n <>\n <span className=\"truncate\">{selected.name}</span>\n <span\n className={`w-1.5 h-1.5 rounded-full shrink-0 ${\n selected.isEnabled ? 'bg-emerald-500' : 'bg-muted-foreground/40'\n }`}\n />\n </>\n ) : (\n <span className=\"text-muted-foreground\">\n {isLoading ? 'Loading…' : 'Select a webhook trigger…'}\n </span>\n )}\n </span>\n <ChevronDown className=\"h-3.5 w-3.5 text-muted-foreground shrink-0\" />\n </button>\n\n {dropdownOpen && (\n <>\n <div className=\"fixed inset-0 z-40\" onClick={() => setDropdownOpen(false)} />\n\n <div className=\"absolute z-50 w-full mt-1 bg-popover border border-border rounded-lg shadow-lg max-h-60 overflow-auto\">\n {/* Create new */}\n <button\n onClick={() => {\n setDropdownOpen(false);\n setCreateOpen(true);\n }}\n className=\"w-full flex items-center gap-2 px-3 py-2 text-sm text-primary hover:bg-accent/50 border-b border-border transition-colors\"\n >\n <Plus className=\"h-3.5 w-3.5\" />\n Create New Webhook\n </button>\n\n {/* Existing triggers */}\n {(triggers ?? []).length === 0 ? (\n <div className=\"px-3 py-4 text-xs text-muted-foreground text-center\">\n No webhook triggers yet\n </div>\n ) : (\n (triggers ?? []).map((trigger) => (\n <button\n key={trigger.id}\n onClick={() => {\n onSelect(trigger);\n setDropdownOpen(false);\n }}\n className={`w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-accent/50 transition-colors ${\n trigger.id === selectedId ? 'bg-accent' : ''\n }`}\n >\n <span\n className={`w-1.5 h-1.5 rounded-full shrink-0 ${\n trigger.isEnabled ? 'bg-emerald-500' : 'bg-muted-foreground/40'\n }`}\n />\n <span className=\"truncate\">{trigger.name}</span>\n <span className=\"text-xs text-muted-foreground ml-auto shrink-0\">\n {trigger.provider}\n </span>\n </button>\n ))\n )}\n </div>\n </>\n )}\n </div>\n\n {/* Selected trigger info */}\n {selected && (\n <div className=\"space-y-2 p-3 rounded-lg bg-muted/30 border\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium\">{selected.name}</span>\n <span\n className={`inline-flex items-center rounded-full border border-transparent px-2 py-0.5 text-[10px] font-medium ${\n selected.isEnabled\n ? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400'\n : 'bg-zinc-100 text-zinc-600 dark:bg-zinc-800/40 dark:text-zinc-400'\n }`}\n >\n {selected.isEnabled ? 'Active' : 'Disabled'}\n </span>\n </div>\n <CopyableField value={`/plugins/webhooks/receive/${selected.webhookPath}`} />\n <a\n href=\"/invect/webhooks\"\n className=\"text-xs text-primary hover:underline inline-flex items-center gap-1\"\n >\n Manage webhooks <ExternalLink className=\"h-3 w-3\" />\n </a>\n </div>\n )}\n\n <CreateWebhookModal\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n flowId={flowId}\n nodeId={nodeId}\n />\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;AAiBA,eAAe,SAAY,SAAiB,MAAc,MAAgC;CACxF,MAAM,MAAM,MAAM,MAAM,GAAG,UAAU,QAAQ;EAC3C,SAAS;GAAE,gBAAgB;GAAoB,GAAG,MAAM;GAAS;EACjE,aAAa;EACb,GAAG;EACJ,CAAC;AACF,KAAI,CAAC,IAAI,IAAI;EACX,MAAM,OAAO,MAAM,IAAI,MAAM,CAAC,aAAa,EAAE,EAAE;AAC/C,QAAM,IAAI,MAAO,KAA4B,SAAS,QAAQ,IAAI,SAAS;;AAE7E,QAAO,IAAI,MAAM;;AAKnB,MAAM,OAAO;CACX,KAAK,CAAC,WAAW;CACjB,YAAY,CAAC,GAAG,KAAK,KAAK,OAAO;CACjC,SAAS,OAAe;EAAC,GAAG,KAAK;EAAK;EAAU;EAAG;CACnD,OAAO,OAAe;EAAC,GAAG,KAAK;EAAK;EAAQ;EAAG;CAChD;AAID,SAAgB,qBAAqB;CACnC,MAAM,UAAU,eAAe;AAC/B,QAAO,SAAS;EACd,UAAU,KAAK,MAAM;EACrB,eACE,SAAqC,SAAS,6BAA6B,CAAC,MACzE,MAAM,EAAE,KACV;EACJ,CAAC;;AAGJ,SAAgB,kBAAkB,IAAwB;CACxD,MAAM,UAAU,eAAe;AAC/B,QAAO,SAAS;EACd,UAAU,KAAK,OAAO,MAAM,GAAG;EAC/B,eAAe,SAAyB,SAAS,8BAA8B,KAAK;EACpF,SAAS,CAAC,CAAC;EACZ,CAAC;;AAGJ,SAAgB,sBAAsB,IAAwB;CAC5D,MAAM,UAAU,eAAe;AAC/B,QAAO,SAAS;EACd,UAAU,KAAK,KAAK,MAAM,GAAG;EAC7B,eAAe,SAA6B,SAAS,8BAA8B,GAAG,OAAO;EAC7F,SAAS,CAAC,CAAC;EACZ,CAAC;;AAKJ,SAAgB,0BAA0B;CACxC,MAAM,UAAU,eAAe;CAC/B,MAAM,KAAK,gBAAgB;AAC3B,QAAO,YAAY;EACjB,aAAa,UACX,SAAgD,SAAS,8BAA8B;GACrF,QAAQ;GACR,MAAM,KAAK,UAAU,MAAM;GAC5B,CAAC;EACJ,iBAAiB,GAAG,kBAAkB,EAAE,UAAU,KAAK,MAAM,EAAE,CAAC;EACjE,CAAC;;AAGJ,SAAgB,0BAA0B;CACxC,MAAM,UAAU,eAAe;CAC/B,MAAM,KAAK,gBAAgB;AAC3B,QAAO,YAAY;EACjB,aAAa,EAAE,IAAI,GAAG,YACpB,SAAyB,SAAS,8BAA8B,MAAM;GACpE,QAAQ;GACR,MAAM,KAAK,UAAU,MAAM;GAC5B,CAAC;EACJ,YAAY,OAAO,SAAS;AAC1B,MAAG,kBAAkB,EAAE,UAAU,KAAK,MAAM,EAAE,CAAC;AAC/C,MAAG,kBAAkB,EAAE,UAAU,KAAK,OAAO,KAAK,GAAG,EAAE,CAAC;;EAE3D,CAAC;;AAGJ,SAAgB,0BAA0B;CACxC,MAAM,UAAU,eAAe;CAC/B,MAAM,KAAK,gBAAgB;AAC3B,QAAO,YAAY;EACjB,aAAa,OACX,SAA+B,SAAS,8BAA8B,MAAM,EAC1E,QAAQ,UACT,CAAC;EACJ,iBAAiB,GAAG,kBAAkB,EAAE,UAAU,KAAK,MAAM,EAAE,CAAC;EACjE,CAAC;;AAGJ,SAAgB,wBAAwB;CACtC,MAAM,UAAU,eAAe;AAC/B,QAAO,YAAY,EACjB,aAAa,EAAE,IAAI,cACjB,SAA6B,SAAS,8BAA8B,GAAG,QAAQ;EAC7E,QAAQ;EACR,MAAM,KAAK,UAAU,WAAW,EAAE,MAAM,MAAM,CAAC;EAChD,CAAC,EACL,CAAC;;;;;;;;;AC3GJ,MAAa,iBAAyC,EAAE,OAAO,SAAS,YAAY;CAClF,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,CAAC,UAAU,eAAe,SAAS,CAAC,OAAO;CAEjD,MAAM,OAAO,YAAY;AACvB,QAAM,UAAU,UAAU,UAAU,MAAM;AAC1C,YAAU,KAAK;AACf,mBAAiB,UAAU,MAAM,EAAE,IAAK;;AAK1C,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf;GACE,oBAAC,QAAD;IAAM,WAAU;cAJC,WAAW,QAAQ,IAAI,OAAO,KAAK,IAAI,MAAM,QAAQ,GAAG,CAAC;IAMnE,CAAA;GACN,UACC,oBAAC,UAAD;IACE,eAAe,YAAY,CAAC,SAAS;IACrC,WAAU;IACV,OAAO,WAAW,SAAS;cAE1B,WAAW,oBAAC,QAAD,EAAQ,WAAU,eAAgB,CAAA,GAAG,oBAAC,KAAD,EAAK,WAAU,eAAgB,CAAA;IACzE,CAAA;GAEX,oBAAC,UAAD;IACE,SAAS;IACT,WAAU;IACV,OAAM;cAEL,SACC,oBAAC,OAAD,EAAO,WAAU,gCAAiC,CAAA,GAElD,oBAAC,MAAD,EAAM,WAAU,eAAgB,CAAA;IAE3B,CAAA;GACL;;;;;;;;;;;AC1BV,MAAa,sBAAmD,EAC9D,MACA,SACA,cACA,QACA,aACI;CACJ,MAAM,CAAC,MAAM,WAAW,SAAS,GAAG;CACpC,MAAM,CAAC,aAAa,kBAAkB,SAAS,GAAG;CAClD,MAAM,CAAC,SAAS,cAAc,SAAS,OAAO;CAC9C,MAAM,CAAC,aAAa,kBAAkB,SAAS,MAAM;CACrD,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,GAAG;CACxD,MAAM,CAAC,YAAY,iBAAiB,SAAS,GAAG;CAChD,MAAM,CAAC,YAAY,iBAAiB,SAAS,GAAG;CAChD,MAAM,CAAC,YAAY,iBAAiB,SAAwB,KAAK;CAEjE,MAAM,iBAAiB,yBAAyB;CAEhD,MAAM,eAAe,YAAY;AAC/B,MAAI,CAAC,KAAK,MAAM,CACd;EAGF,MAAM,QAAmC;GACvC,MAAM,KAAK,MAAM;GACjB,aAAa,YAAY,MAAM,IAAI,KAAA;GACnC,UAAU;GACV,gBAAgB;GAChB;GACA,gBAAgB,cAAc,eAAe,MAAM,IAAI,KAAA,IAAY,KAAA;GACnE,YAAY,cAAc,WAAW,MAAM,IAAI,KAAA,IAAY,KAAA;GAC3D,YAAY,WAAW,MAAM,IAAI,KAAA;GACjC;GACA;GACD;AAED,MAAI;GACF,MAAM,SAAS,MAAM,eAAe,YAAY,MAAM;AACtD,iBAAc,OAAO,WAAW,6BAA6B,OAAO,cAAc;UAC5E;;CAKV,MAAM,oBAAoB;AACxB,UAAQ,GAAG;AACX,iBAAe,GAAG;AAClB,aAAW,OAAO;AAClB,iBAAe,MAAM;AACrB,oBAAkB,GAAG;AACrB,gBAAc,GAAG;AACjB,gBAAc,GAAG;AACjB,gBAAc,KAAK;AACnB,WAAS;;AAGX,QACE,oBAAC,QAAD;EACQ;EACN,eAAe,MAAM;AACnB,OAAI,CAAC,EACH,cAAa;;YAIjB,qBAAC,eAAD;GACE,WAAW,cAAc;GACzB,WAAU;GACV,iBAAA;aAHF,CAME,oBAAC,OAAD;IAAK,WAAU;cACb,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,aAAD;KAAa,WAAU;eACpB,aAAa,oBAAoB;KACtB,CAAA,EACd,oBAAC,mBAAD,EAAA,UACG,aACG,kEACA,iFACc,CAAA,CACP,EAAA,CAAA;IACX,CAAA,EAGN,oBAAC,OAAD;IAAK,WAAU;cACZ,aAEC,qBAAC,OAAD;KAAK,WAAU;eAAf;MACE,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,oBAAC,OAAD,EAAO,WAAU,oBAAqB,CAAA,EAAA,sCAElC;;MAEN,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,oBAAC,SAAD;QAAO,WAAU;kBAAoE;QAE7E,CAAA,EACR,oBAAC,eAAD,EAAe,OAAO,YAAc,CAAA,CAChC;;MAEN,oBAAC,UAAD;OACE,SAAS;OACT,WAAU;iBACX;OAEQ,CAAA;MACL;SAGN,qBAAA,UAAA,EAAA,UAAA;KAEE,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,SAAD;OAAO,WAAU;OAAsB,SAAQ;iBAAiB;OAExD,CAAA,EACR,oBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,OAAO;OACP,WAAW,MAAM,QAAQ,EAAE,OAAO,MAAM;OACxC,aAAY;OACZ,WAAA;OACA,WAAU;OACV,CAAA,CACE;;KAGN,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,SAAD;OAAO,WAAU;OAAsB,SAAQ;iBAAiB;OAExD,CAAA,EACR,oBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,OAAO;OACP,WAAW,MAAM,eAAe,EAAE,OAAO,MAAM;OAC/C,aAAY;OACZ,WAAU;OACV,CAAA,CACE;;KAEN,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,SAAD;OAAO,WAAU;iBAAsB;OAAsB,CAAA,EAC7D,qBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,qBAAC,OAAD,EAAA,UAAA,CACE,oBAAC,OAAD;SAAK,WAAU;mBAAsB;SAAuB,CAAA,EAC5D,oBAAC,KAAD;SAAG,WAAU;mBAAuC;SAEhD,CAAA,CACA,EAAA,CAAA,EACN,oBAAC,UAAD;SACE,MAAK;SACL,eAAe,eAAe,CAAC,YAAY;SAC3C,WAAW,mHACT,cAAc,eAAe;mBAG/B,oBAAC,QAAD,EACE,WAAW,sGACT,cAAc,kBAAkB,mBAElC,CAAA;SACK,CAAA,CACL;WACL,eACC,qBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,qBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,oBAAC,SAAD;UAAO,WAAU;UAAsB,SAAQ;oBAAwB;UAE/D,CAAA,EACR,oBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,cAAa;UACb,OAAO;UACP,WAAW,MAAM,kBAAkB,EAAE,OAAO,MAAM;UAClD,aAAY;UACZ,WAAU;UACV,CAAA,CACE;YACN,qBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,oBAAC,SAAD;UAAO,WAAU;UAAsB,SAAQ;oBAAwB;UAE/D,CAAA,EACR,oBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,cAAa;UACb,OAAO;UACP,WAAW,MAAM,cAAc,EAAE,OAAO,MAAM;UAC9C,aAAY;UACZ,WAAU;UACV,CAAA,CACE;WACF;UAEJ;SACF;;KAGN,qBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,oBAAC,SAAD;QAAO,WAAU;QAAsB,SAAQ;kBAAgB;QAEvD,CAAA;OACR,oBAAC,SAAD;QACE,IAAG;QACH,MAAK;QACL,OAAO;QACP,WAAW,MAAM,cAAc,EAAE,OAAO,MAAM;QAC9C,aAAY;QACZ,WAAU;QACV,CAAA;OACF,oBAAC,KAAD;QAAG,WAAU;kBAAgC;QAEzC,CAAA;OACA;;KAGN,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,SAAD;OAAO,WAAU;OAAsB,SAAQ;iBAAoB;OAE3D,CAAA,EACR,qBAAC,UAAD;OACE,IAAG;OACH,OAAO;OACP,WAAW,MAAM,WAAW,EAAE,OAAO,MAAM;OAC3C,WAAU;iBAJZ;QAME,oBAAC,UAAD;SAAQ,OAAM;mBAAO;SAAkB,CAAA;QACvC,oBAAC,UAAD;SAAQ,OAAM;mBAAW;SAAmB,CAAA;QAC5C,oBAAC,UAAD;SAAQ,OAAM;mBAAM;SAAmB,CAAA;QAChC;SACL;;KAGL,eAAe,WACd,oBAAC,KAAD;MAAG,WAAU;gBACV,eAAe,OAAO,WAAW;MAChC,CAAA;KAIN,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;iBACX;OAEQ,CAAA,EACT,oBAAC,UAAD;OACE,SAAS;OACT,UAAU,CAAC,KAAK,MAAM,IAAI,eAAe;OACzC,WAAU;iBAET,eAAe,YAAY,cAAc;OACnC,CAAA,CACL;;KACL,EAAA,CAAA;IAED,CAAA,CACQ;;EACT,CAAA;;;;;;;;;;;ACtQb,MAAMA,qBAAqE;CACzE,SAAS;EACP,OAAO;EACP,OAAO;EACR;CACD,MAAM;EACJ,OAAO;EACP,OAAO;EACR;CACD,cAAc;EACZ,OAAO;EACP,OAAO;EACR;CACD,QAAQ;EACN,OAAO;EACP,OAAO;EACR;CACF;AAED,SAASC,oBAAkB,SAAyB;AAClD,KAAI,QAAQ,aAAa,UACvB,QAAOD,mBAAiB;AAE1B,KAAI,QAAQ,YACV,QAAOA,mBAAiB;AAE1B,KAAI,QAAQ,WACV,QAAOA,mBAAiB;AAE1B,QAAOA,mBAAiB;;AAG1B,SAAS,eAAe,KAAsB;AAC5C,KAAI,CAAC,IACH,QAAO;AAET,QAAO,IAAI,KAAK,IAAI,CAAC,eAAe,KAAA,GAAW;EAC7C,OAAO;EACP,KAAK;EACL,MAAM;EACN,MAAM;EACN,QAAQ;EACT,CAAC;;AAaJ,MAAa,sBAAmD,EAAE,SAAS,UAAU,cAAc;CACjG,MAAM,CAAC,SAAS,cAAc,SAAkB,WAAW;CAC3D,MAAM,iBAAiB,yBAAyB;CAChD,MAAM,iBAAiB,yBAAyB;CAEhD,MAAM,iBAAiBC,oBAAkB,QAAQ;CAEjD,MAAM,4BAA4B;AAChC,iBAAe,OAAO;GAAE,IAAI,QAAQ;GAAI,WAAW,CAAC,QAAQ;GAAW,CAAC;;CAG1E,MAAM,qBAAqB;AACzB,MAAI,QAAQ,mBAAmB,QAAQ,KAAK,2BAA2B,CACrE,gBAAe,OAAO,QAAQ,IAAI,EAAE,WAAW,SAAS,CAAC;;AAI7D,QACE,qBAAA,UAAA,EAAA,UAAA,CAEE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,oBAAC,cAAD,EAAA,UACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,OAAD,EAAO,WAAU,iCAAkC,CAAA;IAC/C,CAAA,EACN,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,aAAD;KAAa,WAAU;eAAa,QAAQ;KAAmB,CAAA,EAC/D,qBAAC,mBAAD;KAAmB,WAAU;eAA7B;MACE,oBAAC,QAAD;OACE,WAAW,uGAAuG,eAAe;iBAEhI,eAAe;OACX,CAAA;MACP,oBAAC,QAAD,EACE,WAAW,4BACT,QAAQ,YAAY,mBAAmB,4BAEzC,CAAA;MACF,oBAAC,QAAD,EAAA,UAAO,QAAQ,YAAY,YAAY,YAAkB,CAAA;MACvC;OAChB;MACF;MACO,CAAA,EAGf,oBAAC,OAAD;GAAK,WAAU;aACZ,CACC;IAAE,KAAK;IAAqB,OAAO;IAAY,EAC/C;IAAE,KAAK;IAAiB,OAAO;IAAQ,CACxC,CAAC,KAAK,EAAE,KAAK,YACZ,oBAAC,UAAD;IAEE,eAAe,WAAW,IAAI;IAC9B,WAAW,qEACT,YAAY,MACR,sCACA;cAGL;IACM,EATF,IASE,CACT;GACE,CAAA,CACF;KAGN,oBAAC,OAAD;EAAK,WAAU;YACZ,YAAY,aACX,oBAAC,iBAAD;GACW;GACC;GACV,iBAAiB;GACjB,UAAU;GACV,YAAY,eAAe;GAC3B,YAAY,eAAe;GAC3B,CAAA,GAEF,oBAAC,aAAD;GAAsB;GAAS,iBAAiB,WAAW,WAAW;GAAI,CAAA;EAExE,CAAA,CACL,EAAA,CAAA;;AAMP,MAAM,mBAOA,EAAE,SAAS,UAAU,iBAAiB,UAAU,YAAY,iBAChE,qBAAC,OAAD;CAAK,WAAU;WAAf;EAEE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,SAAD;IAAO,WAAU;cAAoE;IAE7E,CAAA,EACR,oBAAC,eAAD,EAAe,OAAO,6BAA6B,QAAQ,eAAiB,CAAA,CACxE;;EAGL,QAAQ,eAAe,QAAQ,kBAC9B,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,SAAD;IAAO,WAAU;cAAoE;IAE7E,CAAA,EACR,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,QAAD;MAAM,WAAU;gBAAgC;MAAuB,CAAA,EACvE,oBAAC,QAAD;MAAM,WAAU;gBAAqB,QAAQ;MAAsB,CAAA,CAC/D;QACN,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,QAAD;MAAM,WAAU;gBAAgC;MAAa,CAAA,EAC7D,oBAAC,eAAD;MAAe,OAAO,QAAQ,cAAc;MAAI,QAAA;MAAS,CAAA,CACrD;OACF;MACF;;EAIP,QAAQ,cACP,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,SAAD;IAAO,WAAU;cAAoE;IAE7E,CAAA,EACR,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,QAAD;KAAM,WAAU;eAA+B,QAAQ;KAAkB,CAAA;IACrE,CAAA,CACF;;EAIR,qBAAC,OAAD;GAAK,WAAU;aAAf;IACE,qBAAC,OAAD,EAAA,UAAA,CACE,oBAAC,QAAD;KAAM,WAAU;eAA2C;KAAc,CAAA,EACzE,oBAAC,QAAD;KAAM,WAAU;eAAqB,QAAQ;KAAsB,CAAA,CAC/D,EAAA,CAAA;IACN,qBAAC,OAAD,EAAA,UAAA,CACE,oBAAC,QAAD;KAAM,WAAU;eAA2C;KAAqB,CAAA,EAChF,oBAAC,QAAD;KAAM,WAAU;eAAWA,oBAAkB,QAAQ,CAAC;KAAa,CAAA,CAC/D,EAAA,CAAA;IACN,qBAAC,OAAD,EAAA,UAAA,CACE,oBAAC,QAAD;KAAM,WAAU;eAA2C;KAAc,CAAA,EACzE,oBAAC,QAAD;KAAM,WAAU;eAAW,eAAe,QAAQ,UAAU;KAAQ,CAAA,CAChE,EAAA,CAAA;IACN,qBAAC,OAAD;KAAK,WAAU;eAAf;MACE,oBAAC,MAAD,EAAM,WAAU,iCAAkC,CAAA;MAClD,oBAAC,QAAD;OAAM,WAAU;iBAAgC;OAAgB,CAAA;MAChE,oBAAC,QAAD;OAAM,WAAU;iBAAuB,QAAQ;OAAoB,CAAA;MAC/D;;IACN,qBAAC,OAAD;KAAK,WAAU;eAAf;MACE,oBAAC,OAAD,EAAO,WAAU,iCAAkC,CAAA;MACnD,oBAAC,QAAD;OAAM,WAAU;iBAAgC;OAAY,CAAA;MAC5D,oBAAC,QAAD;OAAM,WAAU;iBAAW,eAAe,QAAQ,gBAAgB;OAAQ,CAAA;MACtE;;IACF;;EAGL,QAAQ,UACP,oBAAC,OAAD;GAAK,WAAU;aACb,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,UAAD,EAAU,WAAU,0CAA2C,CAAA,EAC/D,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,QAAD;MAAM,WAAU;gBAAsC;MAAkB,CAAA,EACxE,qBAAC,KAAD;MACE,MAAM,gBAAgB,QAAQ;MAC9B,WAAU;gBAFZ,CAIG,YAAY,QAAQ,QACrB,oBAAC,cAAD,EAAc,WAAU,WAAY,CAAA,CAClC;QACA;OACF;;GACF,CAAA;EAIP,QAAQ,gBAAgB,QAAQ,QAAQ,gBAAgB,KAAA,KACvD,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,SAAD;IAAO,WAAU;cAAoE;IAE7E,CAAA,EACR,oBAAC,OAAD;IAAK,WAAU;cACZ,KAAK,UAAU,QAAQ,aAAa,MAAM,EAAE;IACzC,CAAA,CACF;;EAIR,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,qBAAC,UAAD;IACE,SAAS;IACT,UAAU;IACV,WAAW,mGACT,QAAQ,YACJ,2HACA;cANR,CASG,aACC,oBAAC,SAAD,EAAS,WAAU,4BAA6B,CAAA,GAC9C,QAAQ,YACV,oBAAC,YAAD,EAAY,WAAU,eAAgB,CAAA,GAEtC,oBAAC,aAAD,EAAa,WAAU,eAAgB,CAAA,EAExC,QAAQ,YAAY,YAAY,SAC1B;OAET,qBAAC,UAAD;IACE,SAAS;IACT,UAAU;IACV,WAAU;cAHZ,CAKG,aACC,oBAAC,SAAD,EAAS,WAAU,4BAA6B,CAAA,GAEhD,oBAAC,QAAD,EAAQ,WAAU,eAAgB,CAAA,EAClC,SAEK;MACL;;EACF;;AAKR,MAAM,eAGA,EAAE,SAAS,gBAAgB;CAC/B,MAAM,CAAC,MAAM,WAAW,SAAS,QAAQ,KAAK;CAC9C,MAAM,CAAC,aAAa,kBAAkB,SAAS,QAAQ,eAAe,GAAG;CACzE,MAAM,CAAC,SAAS,cAAc,SAAS,QAAQ,eAAe;CAC9D,MAAM,CAAC,aAAa,kBAAkB,SAAS,QAAQ,YAAY;CACnE,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,QAAQ,kBAAkB,GAAG;CAClF,MAAM,CAAC,YAAY,iBAAiB,SAAS,QAAQ,cAAc,GAAG;CACtE,MAAM,CAAC,YAAY,iBAAiB,SAAS,QAAQ,cAAc,GAAG;CACtE,MAAM,iBAAiB,yBAAyB;CAEhD,MAAM,gBAAgB,MAAuB;AAC3C,IAAE,gBAAgB;AAClB,MAAI,CAAC,KAAK,MAAM,CACd;EAGF,MAAM,QAAoD;GACxD,IAAI,QAAQ;GACZ,MAAM,KAAK,MAAM;GACjB,aAAa,YAAY,MAAM,IAAI,KAAA;GACnC,gBAAgB;GAChB;GACA,gBAAgB,cAAc,eAAe,MAAM,IAAI,KAAA,IAAY,KAAA;GACnE,YAAY,cAAc,WAAW,MAAM,IAAI,KAAA,IAAY,KAAA;GAC3D,YAAY,WAAW,MAAM,IAAI,KAAA;GAClC;AACD,iBAAe,OAAO,OAAO,EAAE,WAAW,CAAC;;AAG7C,QACE,qBAAC,QAAD;EAAM,UAAU;EAAc,WAAU;YAAxC;GACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,SAAD;KAAO,WAAU;KAAsB,SAAQ;eAAe;KAEtD,CAAA,EACR,oBAAC,SAAD;KACE,IAAG;KACH,MAAK;KACL,OAAO;KACP,WAAW,MAAM,QAAQ,EAAE,OAAO,MAAM;KACxC,UAAA;KACA,WAAU;KACV,CAAA,CACE;;GAEN,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,SAAD;KAAO,WAAU;KAAsB,SAAQ;eAAe;KAEtD,CAAA,EACR,oBAAC,YAAD;KACE,IAAG;KACH,MAAM;KACN,OAAO;KACP,WAAW,MAAM,eAAe,EAAE,OAAO,MAAM;KAC/C,aAAY;KACZ,WAAU;KACV,CAAA,CACE;;GAGN,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,qBAAC,OAAD,EAAA,UAAA,CACE,oBAAC,OAAD;MAAK,WAAU;gBAAsB;MAAyB,CAAA,EAC9D,oBAAC,KAAD;MAAG,WAAU;gBAAuC;MAEhD,CAAA,CACA,EAAA,CAAA,EACN,oBAAC,UAAD;MACE,MAAK;MACL,eAAe,eAAe,CAAC,YAAY;MAC3C,WAAW,mHACT,cAAc,eAAe;gBAG/B,oBAAC,QAAD,EACE,WAAW,sGACT,cAAc,kBAAkB,mBAElC,CAAA;MACK,CAAA,CACL;QACL,eACC,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,SAAD;OAAO,WAAU;OAAsB,SAAQ;iBAAsB;OAE7D,CAAA,EACR,oBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,OAAO;OACP,WAAW,MAAM,kBAAkB,EAAE,OAAO,MAAM;OAClD,aAAY;OACZ,WAAU;OACV,CAAA,CACE;SACN,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,SAAD;OAAO,WAAU;OAAsB,SAAQ;iBAAsB;OAE7D,CAAA,EACR,oBAAC,SAAD;OACE,IAAG;OACH,MAAK;OACL,OAAO;OACP,WAAW,MAAM,cAAc,EAAE,OAAO,MAAM;OAC9C,aAAY;OACZ,WAAU;OACV,CAAA,CACE;QACF;OAEJ;;GAGN,qBAAC,OAAD;IAAK,WAAU;cAAf;KACE,oBAAC,SAAD;MAAO,WAAU;MAAsB,SAAQ;gBAAc;MAErD,CAAA;KACR,oBAAC,YAAD;MACE,IAAG;MACH,MAAM;MACN,OAAO;MACP,WAAW,MAAM,cAAc,EAAE,OAAO,MAAM;MAC9C,aAAY;MACZ,WAAU;MACV,CAAA;KACF,oBAAC,KAAD;MAAG,WAAU;gBAAgC;MAEzC,CAAA;KACA;;GAEN,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,SAAD;KAAO,WAAU;KAAsB,SAAQ;eAAkB;KAEzD,CAAA,EACR,qBAAC,UAAD;KACE,IAAG;KACH,OAAO;KACP,WAAW,MAAM,WAAW,EAAE,OAAO,MAAM;KAC3C,WAAU;eAJZ;MAME,oBAAC,UAAD;OAAQ,OAAM;iBAAO;OAAkB,CAAA;MACvC,oBAAC,UAAD;OAAQ,OAAM;iBAAW;OAAmB,CAAA;MAC5C,oBAAC,UAAD;OAAQ,OAAM;iBAAM;OAAmB,CAAA;MAChC;OACL;;GAEL,eAAe,WACd,oBAAC,KAAD;IAAG,WAAU;cACV,eAAe,OAAO,WAAW;IAChC,CAAA;GAGN,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,oBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eACX;KAEQ,CAAA,EACT,oBAAC,UAAD;KACE,MAAK;KACL,UAAU,CAAC,KAAK,MAAM,IAAI,eAAe;KACzC,WAAU;eAET,eAAe,YAAY,YAAY;KACjC,CAAA,CACL;;GACD;;;;;;;;;;;ACrdX,MAAM,mBAAqE;CACzE,SAAS;EACP,OAAO;EACP,OAAO;EACR;CACD,MAAM;EACJ,OAAO;EACP,OAAO;EACR;CACD,cAAc;EACZ,OAAO;EACP,OAAO;EACR;CACD,QAAQ;EACN,OAAO;EACP,OAAO;EACR;CACF;AAED,SAAS,kBAAkB,SAAyB;AAClD,KAAI,QAAQ,aAAa,UACvB,QAAO,iBAAiB;AAE1B,KAAI,QAAQ,YACV,QAAO,iBAAiB;AAE1B,KAAI,QAAQ,WACV,QAAO,iBAAiB;AAE1B,QAAO,iBAAiB;;AAK1B,SAAS,mBAAmB,KAAsB;AAChD,KAAI,CAAC,IACH,QAAO;CAET,MAAM,OAAO,KAAK,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC,SAAS;CACjD,MAAM,OAAO,KAAK,MAAM,OAAO,IAAO;AACtC,KAAI,OAAO,EACT,QAAO;AAET,KAAI,OAAO,GACT,QAAO,GAAG,KAAK;CAEjB,MAAM,QAAQ,KAAK,MAAM,OAAO,GAAG;AACnC,KAAI,QAAQ,GACV,QAAO,GAAG,MAAM;CAElB,MAAM,OAAO,KAAK,MAAM,QAAQ,GAAG;AACnC,KAAI,OAAO,GACT,QAAO,GAAG,KAAK;AAEjB,QAAO,IAAI,KAAK,IAAI,CAAC,mBAAmB,KAAA,GAAW;EAAE,OAAO;EAAS,KAAK;EAAW,CAAC;;AAKxF,MAAM,cAIA,EAAE,SAAS,UAAU,cAAc;CACvC,MAAM,iBAAiB,kBAAkB,QAAQ;AAEjD,QACE,qBAAC,UAAD;EACE,WAAU;EACD;YAFX;GAKE,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,OAAD,EAAO,WAAU,iCAAkC,CAAA;IAC/C,CAAA;GAGN,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,QAAD;MAAM,WAAU;gBAAgC,QAAQ;MAAY,CAAA,EACpE,oBAAC,QAAD,EACE,WAAW,qCACT,QAAQ,YAAY,mBAAmB,4BAEzC,CAAA,CACE;QACN,oBAAC,OAAD;KAAK,WAAU;eACZ,WACC,qBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,oBAAC,UAAD,EAAU,WAAU,oBAAqB,CAAA,EACxC,SACI;UACL,QAAQ,cACV,oBAAC,QAAD;MAAM,WAAU;gBAA0C,QAAQ;MAAmB,CAAA,GAErF,oBAAC,QAAD;MAAM,WAAU;gBAAmC;MAAqB,CAAA;KAEtE,CAAA,CACF;;GAGN,oBAAC,QAAD;IACE,WAAW,iHAAiH,eAAe;cAE1I,eAAe;IACX,CAAA;GAGP,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,QAAD;KAAM,WAAU;KAA0B,OAAM;eAAhD,CACE,oBAAC,MAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,QAAQ,aACJ;QACP,qBAAC,QAAD;KAAM,WAAU;KAA2C,OAAM;eAAjE,CACE,oBAAC,OAAD,EAAO,WAAU,WAAY,CAAA,EAC5B,mBAAmB,QAAQ,gBAAgB,CACvC;OACH;;GAEN,oBAAC,cAAD,EAAc,WAAU,6CAA8C,CAAA;GAC/D;;;AAMb,MAAa,qBAA+C;CAC1D,MAAM,eAAe,OAAuB,KAAK;CACjD,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,CAAC,iBAAiB,sBAAsB,SAAgC,KAAK;CACnF,MAAM,CAAC,aAAa,kBAAkB,SAAS,GAAG;CAClD,MAAM,CAAC,cAAc,mBAAmB,SAAyC,MAAM;CAEvF,MAAM,EAAE,MAAM,UAAU,WAAW,UAAU,oBAAoB;CACjE,MAAM,EAAE,MAAM,kBAAkB,UAAU;CAG1C,MAAM,8BAAc,IAAI,KAAqB;AAC7C,MAAK,MAAM,QAAQ,eAAe,QAAQ,EAAE,CAC1C,aAAY,IAAI,KAAK,IAAI,KAAK,KAAK;CAIrC,MAAM,YAAY,YAAY,EAAE,EAAE,QAAQ,MAAM;AAC9C,MAAI,iBAAiB,aAAa,CAAC,EAAE,UACnC,QAAO;AAET,MAAI,iBAAiB,cAAc,EAAE,UACnC,QAAO;AAET,MAAI,aAAa;GACf,MAAM,IAAI,YAAY,aAAa;GACnC,MAAM,YAAY,EAAE,KAAK,aAAa,CAAC,SAAS,EAAE;GAClD,MAAM,YAAY,EAAE,UACf,YAAY,IAAI,EAAE,OAAO,IAAI,IAAI,aAAa,CAAC,SAAS,EAAE,GAC3D;AACJ,OAAI,CAAC,aAAa,CAAC,UACjB,QAAO;;AAGX,SAAO;GACP;CAEF,MAAM,gBAAgB,YAAY,EAAE,EAAE,QAAQ,MAAM,EAAE,UAAU,CAAC;CACjE,MAAM,iBAAiB,YAAY,EAAE,EAAE,QAAQ,MAAM,CAAC,EAAE,UAAU,CAAC;CAGnE,MAAM,cAAc,mBACd,YAAY,EAAE,EAAE,MAAM,MAAM,EAAE,OAAO,gBAAgB,GAAG,IAAI,kBAC9D;AAEJ,QACE,qBAAC,YAAD;EACE,OAAM;EACN,UAAS;EACT,MAAM;EACN,SACE,qBAAC,UAAD;GACE,eAAe,cAAc,KAAK;GAClC,WAAU;aAFZ,CAIE,oBAAC,MAAD,EAAM,WAAU,WAAY,CAAA,EAAA,cAErB;;YAXb;GAcE,qBAAC,OAAD;IAAK,KAAK;cAAV,CAEE,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,QAAD,EAAQ,WAAU,kGAAmG,CAAA,EACrH,oBAAC,SAAD;OACE,MAAK;OACL,aAAY;OACZ,OAAO;OACP,WAAW,MAAM,eAAe,EAAE,OAAO,MAAM;OAC/C,WAAU;OACV,CAAA,CACE;SAEN,oBAAC,OAAD;MAAK,WAAU;gBACZ;OACC;QAAE,KAAK;QAAgB,OAAO,QAAQ,UAAU,UAAU,EAAE;QAAI;OAChE;QAAE,KAAK;QAAoB,OAAO,YAAY,aAAa;QAAI;OAC/D;QAAE,KAAK;QAAqB,OAAO,aAAa,cAAc;QAAI;OACnE,CAAC,KAAK,EAAE,KAAK,YACZ,oBAAC,UAAD;OAEE,eAAe,gBAAgB,IAAI;OACnC,WAAW,yEACT,iBAAiB,MACb,oDACA;iBAGL;OACM,EATF,IASE,CACT;MACE,CAAA,CACF;QAGL,YACC,qBAAC,OAAD;KAAK,WAAU;eAAf,CACE,oBAAC,SAAD,EAAS,WAAU,wBAAyB,CAAA,EAAA,oBAExC;SACJ,QACF,qBAAC,OAAD;KAAK,WAAU;eAAf,CAA8I,6BACjH,MAAgB,QACvC;SACJ,SAAS,WAAW,IACtB,qBAAC,OAAD;KAAK,WAAU;eAAf;MACE,oBAAC,OAAD;OAAK,WAAU;iBACb,oBAAC,OAAD,EAAO,WAAU,iCAAkC,CAAA;OAC/C,CAAA;MACN,oBAAC,MAAD;OAAI,WAAU;iBACX,UAAU,WAAW,IAAI,oBAAoB;OAC3C,CAAA;MACL,oBAAC,KAAD;OAAG,WAAU;iBACV,UAAU,WAAW,IAClB,+EACA;OACF,CAAA;MACH,UAAU,WAAW,KACpB,qBAAC,UAAD;OACE,eAAe,cAAc,KAAK;OAClC,WAAU;iBAFZ,CAIE,oBAAC,MAAD,EAAM,WAAU,WAAY,CAAA,EAAA,iBAErB;;MAEP;SAEN,oBAAC,OAAD;KAAK,WAAU;eACb,oBAAC,OAAD;MAAK,WAAU;gBACZ,SAAS,KAAK,YACb,oBAAC,YAAD;OAEW;OACT,UAAU,QAAQ,SAAS,YAAY,IAAI,QAAQ,OAAO,GAAG,KAAA;OAC7D,eAAe,mBAAmB,QAAQ;OAC1C,EAJK,QAAQ,GAIb,CACF;MACE,CAAA;KACF,CAAA,CAEJ;;GAGN,oBAAC,QAAD;IACE,MAAM,CAAC,CAAC;IACR,eAAe,SAAS;AACtB,SAAI,CAAC,KACH,oBAAmB,KAAK;;cAI5B,oBAAC,eAAD;KACE,WAAW,aAAa;KACxB,WAAU;KACV,iBAAA;eAEC,eACC,oBAAC,oBAAD;MACE,SAAS;MACT,UAAU,YAAY,SAAS,YAAY,IAAI,YAAY,OAAO,GAAG,KAAA;MACrE,eAAe,mBAAmB,KAAK;MACvC,CAAA;KAEU,CAAA;IACT,CAAA;GAGT,oBAAC,oBAAD;IACE,MAAM;IACN,eAAe,cAAc,MAAM;IACrB;IACd,CAAA;GACS;;;;;;;;;ACpTjB,MAAa,mBAAyC;CACpD,IAAI;CACJ,MAAM;CAEN,SAAS,CACP;EACE,OAAO;EACP,MAAM;EACN,MAAM;EACN,UAAU;EACX,CACF;CAED,QAAQ,CACN;EACE,MAAM;EACN,WAAW;EACZ,CACF;CACF;;;;;;;ACTD,MAAa,0BAA2D,EACtE,YACA,UACA,QACA,aACI;CACJ,MAAM,CAAC,cAAc,mBAAmB,SAAS,MAAM;CACvD,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,EAAE,MAAM,UAAU,cAAc,oBAAoB;CAE1D,MAAM,WAAW,UAAU,MAAM,MAAM,EAAE,OAAO,WAAW;AAE3D,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf;GACE,oBAAC,SAAD;IAAO,WAAU;cAAsB;IAAuB,CAAA;GAG9D,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,UAAD;KACE,MAAK;KACL,eAAe,gBAAgB,CAAC,aAAa;KAC7C,WAAU;eAHZ,CAKE,qBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,oBAAC,OAAD,EAAO,WAAU,8CAA+C,CAAA,EAC/D,WACC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,QAAD;OAAM,WAAU;iBAAY,SAAS;OAAY,CAAA,EACjD,oBAAC,QAAD,EACE,WAAW,qCACT,SAAS,YAAY,mBAAmB,4BAE1C,CAAA,CACD,EAAA,CAAA,GAEH,oBAAC,QAAD;OAAM,WAAU;iBACb,YAAY,aAAa;OACrB,CAAA,CAEJ;SACP,oBAAC,aAAD,EAAa,WAAU,8CAA+C,CAAA,CAC/D;QAER,gBACC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,OAAD;KAAK,WAAU;KAAqB,eAAe,gBAAgB,MAAM;KAAI,CAAA,EAE7E,qBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,qBAAC,UAAD;MACE,eAAe;AACb,uBAAgB,MAAM;AACtB,qBAAc,KAAK;;MAErB,WAAU;gBALZ,CAOE,oBAAC,MAAD,EAAM,WAAU,eAAgB,CAAA,EAAA,qBAEzB;UAGP,YAAY,EAAE,EAAE,WAAW,IAC3B,oBAAC,OAAD;MAAK,WAAU;gBAAsD;MAE/D,CAAA,IAEL,YAAY,EAAE,EAAE,KAAK,YACpB,qBAAC,UAAD;MAEE,eAAe;AACb,gBAAS,QAAQ;AACjB,uBAAgB,MAAM;;MAExB,WAAW,yFACT,QAAQ,OAAO,aAAa,cAAc;gBAP9C;OAUE,oBAAC,QAAD,EACE,WAAW,qCACT,QAAQ,YAAY,mBAAmB,4BAEzC,CAAA;OACF,oBAAC,QAAD;QAAM,WAAU;kBAAY,QAAQ;QAAY,CAAA;OAChD,oBAAC,QAAD;QAAM,WAAU;kBACb,QAAQ;QACJ,CAAA;OACA;QAlBF,QAAQ,GAkBN,CACT,CAEA;OACL,EAAA,CAAA,CAED;;GAGL,YACC,qBAAC,OAAD;IAAK,WAAU;cAAf;KACE,qBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,oBAAC,QAAD;OAAM,WAAU;iBAAuB,SAAS;OAAY,CAAA,EAC5D,oBAAC,QAAD;OACE,WAAW,uGACT,SAAS,YACL,iFACA;iBAGL,SAAS,YAAY,WAAW;OAC5B,CAAA,CACH;;KACN,oBAAC,eAAD,EAAe,OAAO,6BAA6B,SAAS,eAAiB,CAAA;KAC7E,qBAAC,KAAD;MACE,MAAK;MACL,WAAU;gBAFZ,CAGC,oBACiB,oBAAC,cAAD,EAAc,WAAU,WAAY,CAAA,CAClD;;KACA;;GAGR,oBAAC,oBAAD;IACE,MAAM;IACN,eAAe,cAAc,MAAM;IAC3B;IACA;IACR,CAAA;GACE"}