@burdenoff/microfe-bigconsole 2026.804.2 → 2026.805.2
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.
- package/dist/bigconsole/AdminRoot.js +21 -16
- package/dist/bigconsole/AdminRoot.js.map +1 -1
- package/dist/bigconsole/BigConsoleRoot.js +44 -39
- package/dist/bigconsole/BigConsoleRoot.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js +115 -100
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js.map +1 -1
- package/dist/bigconsole/components/dashboard/PageTabsManager/PageTabsManager.js +100 -98
- package/dist/bigconsole/components/dashboard/PageTabsManager/PageTabsManager.js.map +1 -1
- package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +164 -161
- package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js.map +1 -1
- package/dist/bigconsole/components/datasink/DataSinkCard.js +20 -20
- package/dist/bigconsole/components/datasink/DataSinkCard.js.map +1 -1
- package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js +216 -49
- package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js.map +1 -1
- package/dist/bigconsole/components/widgets/ActionConfigDialog.js +96 -90
- package/dist/bigconsole/components/widgets/ActionConfigDialog.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetMenu.js +87 -76
- package/dist/bigconsole/components/widgets/WidgetMenu.js.map +1 -1
- package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js +120 -118
- package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js.map +1 -1
- package/dist/bigconsole/pages/DashboardBuilderPage.js +113 -104
- package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardViewPage.js +208 -199
- package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DashboardsPage.js +376 -325
- package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
- package/dist/bigconsole/pages/DataParsersPage.js +135 -125
- package/dist/bigconsole/pages/DataParsersPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkBuilderPage.js +182 -172
- package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinkViewPage.js +233 -198
- package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
- package/dist/bigconsole/pages/DataSinksPage.js +281 -268
- package/dist/bigconsole/pages/DataSinksPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserBuilderPage.js +238 -223
- package/dist/bigconsole/pages/ParserBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/ParserViewPage.js +162 -144
- package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineBuilderPage.js +212 -204
- package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelineViewPage.js +191 -183
- package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
- package/dist/bigconsole/pages/PipelinesPage.js +128 -103
- package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowViewPage.js +238 -217
- package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
- package/dist/bigconsole/pages/WorkflowsPage.js +116 -102
- package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
- package/dist/bigconsole/utils/csv.js +91 -0
- package/dist/bigconsole/utils/csv.js.map +1 -0
- package/dist/constants/permissions.js +83 -0
- package/dist/constants/permissions.js.map +1 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionConfigDialog.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/ActionConfigDialog.tsx"],"sourcesContent":["/**\n * ActionConfigDialog Component (v2.1)\n *\n * Modal dialog for creating/editing widget actions.\n *\n * v2.1 (BOFF-5659): reframed around the *outcome* — \"When clicked, do this\" —\n * instead of the abstract action Type. A button always emits\n * `widget:<name>:clicked`; the dialog now lets the user attach the behaviour\n * inline. Today the two shippable outcomes are:\n * - \"Call a webhook / my API\" → captures a URL + method; the parent pairs the\n * action with an EventWebhookMapping bound to that event (no backend change).\n * - \"Just emit an event\" → the raw emit, for callers wiring their own\n * consumers.\n * The action `name` (the event id) is auto-derived from the label and tucked\n * under Advanced so there is a single required field.\n *\n * Uses fe-libs shared UI components and semantic tokens for consistent styling.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect, useRef } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Textarea } from '@burdenoff/fe-libs/ui';\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@burdenoff/fe-libs/ui';\nimport { Checkbox } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { Zap, Plus, Pencil, ChevronDown, ChevronRight } from 'lucide-react';\nimport type {\n WidgetAction,\n ActionStyle,\n ActionConfirmation,\n CreateWidgetActionInput,\n UpdateWidgetActionInput,\n} from '../../hooks/useWidgetActions';\nimport type { HttpMethod } from '../../hooks/useEventWebhooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** What should happen when the button is clicked. */\nexport type ActionOutcome = 'WEBHOOK' | 'EMIT';\n\n/** Webhook binding drafted in the dialog and reconciled by the parent. */\nexport interface ActionWebhookDraft {\n url: string;\n method: HttpMethod;\n}\n\n/**\n * The action fields passed to the create/update mutation. The webhook binding\n * is passed as a SEPARATE onSave argument (never embedded here) so it can never\n * leak into the GraphQL input.\n */\nexport type ActionSaveData = Omit<CreateWidgetActionInput, 'widgetId'> | UpdateWidgetActionInput;\n\nexport interface ActionConfigDialogProps {\n isOpen: boolean;\n action?: WidgetAction;\n /** Existing webhook binding for the edited action (prefills the URL field). */\n initialWebhook?: ActionWebhookDraft | null;\n isLoading?: boolean;\n /** A save failure from the parent (action or webhook mutation) to surface inline. */\n saveError?: string | null;\n onClose: () => void;\n /** `webhook` is the paired binding, or `null` to remove any existing one. */\n onSave: (data: ActionSaveData, webhook: ActionWebhookDraft | null) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst OUTCOMES: { value: ActionOutcome; label: string; hint: string }[] = [\n {\n value: 'WEBHOOK',\n label: 'Call a webhook / my API',\n hint: 'Sends an HTTP request to the URL below every time the button is clicked.',\n },\n {\n value: 'EMIT',\n label: 'Just emit an event (advanced)',\n hint: 'Emits widget:<name>:clicked. Wire it up yourself in the Webhooks/Events section.',\n },\n];\n\nconst HTTP_METHODS: HttpMethod[] = ['POST', 'GET', 'PUT', 'PATCH', 'DELETE'];\n\nconst ACTION_STYLES: { value: ActionStyle; label: string; color: string }[] = [\n { value: 'PRIMARY', label: 'Primary', color: 'var(--color-action-primary-bg)' },\n { value: 'SECONDARY', label: 'Secondary', color: 'var(--color-action-secondary-bg)' },\n { value: 'SUCCESS', label: 'Success', color: 'var(--color-status-success-bg)' },\n { value: 'WARNING', label: 'Warning', color: 'var(--color-status-warning-bg)' },\n { value: 'DANGER', label: 'Danger', color: 'var(--color-action-danger-bg)' },\n];\n\nconst ICON_OPTIONS = [\n { value: '', label: 'No icon' },\n { value: 'refresh', label: 'Refresh' },\n { value: 'download', label: 'Download' },\n { value: 'upload', label: 'Upload' },\n { value: 'trash', label: 'Trash' },\n { value: 'edit', label: 'Edit' },\n { value: 'plus', label: 'Plus' },\n { value: 'check', label: 'Check' },\n { value: 'eye', label: 'View' },\n { value: 'copy', label: 'Copy' },\n { value: 'share', label: 'Share' },\n { value: 'settings', label: 'Settings' },\n { value: 'filter', label: 'Filter' },\n { value: 'search', label: 'Search' },\n];\n\n// Derive the event-id (action name) from a human label: \"Refresh Data\" -> \"refresh_data\".\nfunction slugifyName(label: string): string {\n return label\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '');\n}\n\nconst labelStyle: React.CSSProperties = {\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n marginBottom: 'var(--space-controlGap)',\n};\n\nconst hintStyle: React.CSSProperties = {\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-muted)',\n marginTop: 'var(--space-controlGap)',\n};\n\nconst selectContentStyle: React.CSSProperties = {\n zIndex: 9999,\n backgroundColor: 'var(--color-overlay-popoverBg, var(--color-bg-surface))',\n border: '1px solid var(--color-border)',\n boxShadow: 'var(--shadow-elevation-3)',\n borderRadius: 'var(--radius-button)',\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ActionConfigDialog: FC<ActionConfigDialogProps> = memo(function ActionConfigDialog({\n isOpen,\n action,\n initialWebhook,\n isLoading = false,\n saveError,\n onClose,\n onSave,\n}) {\n const isEditMode = !!action;\n const labelInputRef = useRef<HTMLInputElement>(null);\n // True once the user touches the webhook fields, so a late-arriving\n // `initialWebhook` fetch can seed the form without clobbering in-progress edits.\n const webhookTouchedRef = useRef(false);\n\n // Form state\n const [label, setLabel] = useState('');\n const [name, setName] = useState('');\n const [nameEdited, setNameEdited] = useState(false);\n const [style, setStyle] = useState<ActionStyle>('PRIMARY');\n const [icon, setIcon] = useState('');\n const [outcome, setOutcome] = useState<ActionOutcome>('WEBHOOK');\n const [webhookUrl, setWebhookUrl] = useState('');\n const [webhookMethod, setWebhookMethod] = useState<HttpMethod>('POST');\n const [disabled, setDisabled] = useState(false);\n const [hidden, setHidden] = useState(false);\n const [showAdvanced, setShowAdvanced] = useState(false);\n const [hasConfirmation, setHasConfirmation] = useState(false);\n const [confirmTitle, setConfirmTitle] = useState('Confirm Action');\n const [confirmMessage, setConfirmMessage] = useState('Are you sure you want to proceed?');\n const [confirmLabel, setConfirmLabel] = useState('Confirm');\n const [cancelLabel, setCancelLabel] = useState('Cancel');\n const [error, setError] = useState<string | null>(null);\n\n // Reset form when the dialog opens or the edited action changes. Deliberately\n // does NOT depend on `initialWebhook` — that fetch can settle after the dialog\n // is open, and re-running this would wipe the user's in-progress edits (#6).\n // The webhook fields are seeded by the separate effect below.\n useEffect(() => {\n if (!isOpen) return;\n\n webhookTouchedRef.current = false;\n setWebhookUrl('');\n setWebhookMethod('POST');\n\n if (action) {\n setLabel(action.label);\n setName(action.name);\n setNameEdited(false);\n setStyle(action.style);\n setIcon(action.icon || '');\n setDisabled(action.disabled);\n setHidden(action.hidden);\n // Default to \"emit\"; the seed effect flips to WEBHOOK if a mapping exists.\n setOutcome('EMIT');\n setHasConfirmation(!!action.confirmation);\n if (action.confirmation) {\n setConfirmTitle(action.confirmation.title);\n setConfirmMessage(action.confirmation.message);\n setConfirmLabel(action.confirmation.confirmLabel || 'Confirm');\n setCancelLabel(action.confirmation.cancelLabel || 'Cancel');\n } else {\n setConfirmTitle('Confirm Action');\n setConfirmMessage('Are you sure you want to proceed?');\n setConfirmLabel('Confirm');\n setCancelLabel('Cancel');\n }\n } else {\n setLabel('');\n setName('');\n setNameEdited(false);\n setStyle('PRIMARY');\n setIcon('');\n setOutcome('WEBHOOK');\n setDisabled(false);\n setHidden(false);\n setHasConfirmation(false);\n setConfirmTitle('Confirm Action');\n setConfirmMessage('Are you sure you want to proceed?');\n setConfirmLabel('Confirm');\n setCancelLabel('Cancel');\n }\n setShowAdvanced(false);\n setError(null);\n // Focus the label after the dialog paints — a double rAF is more reliable than\n // a magic setTimeout delay and doesn't race the open animation.\n const raf = requestAnimationFrame(() => requestAnimationFrame(() => labelInputRef.current?.focus()));\n return () => cancelAnimationFrame(raf);\n }, [isOpen, action]);\n\n // Seed the webhook fields from the (possibly late-arriving) mapping fetch,\n // unless the user has already started editing them.\n useEffect(() => {\n if (!isOpen || !initialWebhook || webhookTouchedRef.current) return;\n setOutcome('WEBHOOK');\n setWebhookUrl(initialWebhook.url);\n setWebhookMethod(initialWebhook.method);\n }, [isOpen, initialWebhook]);\n\n // Keep the auto-derived name in sync with the label until the user overrides it.\n const handleLabelChange = useCallback(\n (value: string) => {\n setLabel(value);\n if (!nameEdited) setName(slugifyName(value));\n if (error) setError(null);\n },\n [nameEdited, error]\n );\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n\n const trimmedLabel = label.trim();\n if (!trimmedLabel) {\n setError('Button label is required');\n labelInputRef.current?.focus();\n return;\n }\n\n // The action name is the event id (webhook contract). It is set once at\n // creation and never rewritten on edit — re-slugifying a stored name like\n // `refresh-v2` would silently change `widget:refresh-v2:clicked` and break\n // existing bindings/consumers (#1).\n const finalName =\n isEditMode && action ? action.name : slugifyName(nameEdited ? name : label) || slugifyName(label);\n if (!finalName) {\n setError('Enter a label using letters or numbers');\n return;\n }\n\n if (outcome === 'WEBHOOK') {\n const url = webhookUrl.trim();\n if (!url) {\n setError('Enter the URL to call when the button is clicked');\n return;\n }\n if (!/^https?:\\/\\//i.test(url)) {\n setError('Webhook URL must start with http:// or https://');\n return;\n }\n }\n\n const confirmation: ActionConfirmation | undefined = hasConfirmation\n ? {\n title: confirmTitle.trim() || 'Confirm Action',\n message: confirmMessage.trim() || 'Are you sure?',\n confirmLabel: confirmLabel.trim() || 'Confirm',\n cancelLabel: cancelLabel.trim() || 'Cancel',\n }\n : undefined;\n\n const webhook: ActionWebhookDraft | null =\n outcome === 'WEBHOOK' ? { url: webhookUrl.trim(), method: webhookMethod } : null;\n\n if (isEditMode && action) {\n const updateData: ActionSaveData = {\n id: action.id,\n name: finalName,\n label: trimmedLabel,\n // Webhook/emit outcomes must be a BUTTON so the click runs onAction and\n // emits the event. Saving a stale LINK type would render an <a> that\n // bypasses onAction, so the webhook would never fire (#3).\n type: 'BUTTON',\n style,\n icon: icon || undefined,\n confirmation: hasConfirmation ? confirmation : null,\n disabled,\n hidden,\n };\n onSave(updateData, webhook);\n } else {\n const createData: ActionSaveData = {\n name: finalName,\n label: trimmedLabel,\n // Webhook/emit outcomes must be a BUTTON so the click runs onAction and\n // emits the event. Saving a stale LINK type would render an <a> that\n // bypasses onAction, so the webhook would never fire (#3).\n type: 'BUTTON',\n style,\n icon: icon || undefined,\n confirmation,\n disabled,\n hidden,\n };\n onSave(createData, webhook);\n }\n },\n [\n label,\n name,\n nameEdited,\n outcome,\n webhookUrl,\n webhookMethod,\n style,\n icon,\n disabled,\n hidden,\n hasConfirmation,\n confirmTitle,\n confirmMessage,\n confirmLabel,\n cancelLabel,\n isEditMode,\n action,\n onSave,\n ]\n );\n\n const getStyleColor = (s: ActionStyle) =>\n ACTION_STYLES.find((as) => as.value === s)?.color || 'var(--color-action-primary-bg)';\n\n const activeOutcome = OUTCOMES.find((o) => o.value === outcome);\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && !isLoading && onClose()}>\n <DialogContent style={{ maxWidth: '500px', padding: 0 }}>\n {/* Header */}\n <DialogHeader style={{ padding: 'var(--space-pagePadding) var(--space-pagePadding) 0' }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-formGap)' }}>\n <div\n style={{\n display: 'flex',\n height: 'var(--size-controlHeight-lg)',\n width: 'var(--size-controlHeight-lg)',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 'var(--radius-card)',\n backgroundColor: 'var(--color-action-primary-bg)',\n boxShadow: 'var(--shadow-elevation-2)',\n }}\n >\n {isEditMode ? (\n <Pencil\n style={{\n height: 'var(--size-icon-lg)',\n width: 'var(--size-icon-lg)',\n color: 'var(--color-action-primary-text)',\n }}\n />\n ) : (\n <Zap\n style={{\n height: 'var(--size-icon-lg)',\n width: 'var(--size-icon-lg)',\n color: 'var(--color-action-primary-text)',\n }}\n />\n )}\n </div>\n <div>\n <DialogTitle\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-bold)',\n color: 'var(--color-text-primary)',\n margin: 0,\n }}\n >\n {isEditMode ? 'Edit Button' : 'Add Button'}\n </DialogTitle>\n <DialogDescription\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n margin: 'var(--space-controlGap) 0 0 0',\n }}\n >\n {isEditMode ? 'Update the button and what it does' : 'Add a button and choose what it does'}\n </DialogDescription>\n </div>\n </div>\n </DialogHeader>\n\n <form onSubmit={handleSubmit}>\n {/* Form Content */}\n <div style={{ padding: 'var(--space-pagePadding)', maxHeight: '60vh', overflowY: 'auto' }}>\n {/* Error Display — local validation error or a save failure from the parent. */}\n {(error || saveError) && (\n <div\n style={{\n padding: 'var(--space-controlGap)',\n backgroundColor: 'var(--color-status-error-bgSubtle)',\n border: '1px solid var(--color-status-error-border)',\n borderRadius: 'var(--radius-button)',\n marginBottom: 'var(--space-formGap)',\n }}\n >\n <p style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-status-error-text)', margin: 0 }}>\n {error || saveError}\n </p>\n </div>\n )}\n\n {/* Label Field */}\n <div style={{ marginBottom: 'var(--space-formGap)' }}>\n <label style={labelStyle}>\n Button label <span style={{ color: 'var(--color-status-error-text)' }}>*</span>\n </label>\n <Input\n ref={labelInputRef}\n value={label}\n onChange={(e) => handleLabelChange(e.target.value)}\n placeholder=\"e.g., Refresh Data, Export CSV\"\n disabled={isLoading}\n style={{ height: 'var(--size-controlHeight-md)', fontSize: 'var(--font-size-body-sm)' }}\n />\n <p style={hintStyle}>Text shown on the button</p>\n </div>\n\n {/* Style + Icon Row */}\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: '1fr 1fr',\n gap: 'var(--space-formGap)',\n marginBottom: 'var(--space-formGap)',\n }}\n >\n <div>\n <label style={labelStyle}>Style</label>\n <Select value={style} onValueChange={(v) => setStyle(v as ActionStyle)} disabled={isLoading}>\n <SelectTrigger\n style={{ height: 'var(--size-controlHeight-md)', fontSize: 'var(--font-size-body-sm)' }}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent style={selectContentStyle}>\n {ACTION_STYLES.map((s) => (\n <SelectItem key={s.value} value={s.value}>\n {s.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div>\n <label style={labelStyle}>Icon</label>\n <Select value={icon} onValueChange={setIcon} disabled={isLoading}>\n <SelectTrigger\n style={{ height: 'var(--size-controlHeight-md)', fontSize: 'var(--font-size-body-sm)' }}\n >\n <SelectValue placeholder=\"No icon\" />\n </SelectTrigger>\n <SelectContent style={{ ...selectContentStyle, maxHeight: '200px' }}>\n {ICON_OPTIONS.map((i) => (\n <SelectItem key={i.value || 'none'} value={i.value || 'none'}>\n {i.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {/* Style Preview */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n marginBottom: 'var(--space-formGap)',\n padding: 'var(--space-controlGap)',\n backgroundColor: 'var(--color-bg-sunken)',\n borderRadius: 'var(--radius-button)',\n }}\n >\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>Preview:</span>\n <button\n type=\"button\"\n style={{\n padding: 'var(--space-controlGap) var(--space-formGap)',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n borderRadius: 'var(--radius-button)',\n backgroundColor: getStyleColor(style),\n color: 'var(--color-action-primary-text)',\n border: 'none',\n cursor: 'default',\n }}\n >\n {label || 'Button'}\n </button>\n </div>\n\n {/* When clicked (outcome) */}\n <div style={{ marginBottom: 'var(--space-formGap)' }}>\n <label style={labelStyle}>When clicked</label>\n <Select\n value={outcome}\n onValueChange={(v) => {\n webhookTouchedRef.current = true;\n setOutcome(v as ActionOutcome);\n }}\n disabled={isLoading}\n >\n <SelectTrigger style={{ height: 'var(--size-controlHeight-md)', fontSize: 'var(--font-size-body-sm)' }}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent style={selectContentStyle}>\n {OUTCOMES.map((o) => (\n <SelectItem key={o.value} value={o.value}>\n {o.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {activeOutcome && <p style={hintStyle}>{activeOutcome.hint}</p>}\n </div>\n\n {/* Webhook config (only for the WEBHOOK outcome) */}\n {outcome === 'WEBHOOK' && (\n <div\n style={{\n marginBottom: 'var(--space-formGap)',\n paddingLeft: 'var(--space-formGap)',\n borderLeft: '2px solid var(--color-border)',\n display: 'flex',\n flexDirection: 'column',\n gap: 'var(--space-controlGap)',\n }}\n >\n <div>\n <label style={labelStyle}>\n URL <span style={{ color: 'var(--color-status-error-text)' }}>*</span>\n </label>\n <Input\n value={webhookUrl}\n onChange={(e) => {\n webhookTouchedRef.current = true;\n setWebhookUrl(e.target.value);\n if (error) setError(null);\n }}\n placeholder=\"https://your-service.com/hook\"\n disabled={isLoading}\n style={{ height: 'var(--size-controlHeight-md)', fontSize: 'var(--font-size-body-sm)' }}\n />\n </div>\n <div style={{ maxWidth: '160px' }}>\n <label style={labelStyle}>Method</label>\n <Select\n value={webhookMethod}\n onValueChange={(v) => {\n webhookTouchedRef.current = true;\n setWebhookMethod(v as HttpMethod);\n }}\n disabled={isLoading}\n >\n <SelectTrigger\n style={{ height: 'var(--size-controlHeight-md)', fontSize: 'var(--font-size-body-sm)' }}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent style={selectContentStyle}>\n {HTTP_METHODS.map((m) => (\n <SelectItem key={m} value={m}>\n {m}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <p style={hintStyle}>The widget's current data is sent as the request payload.</p>\n {/^http:\\/\\//i.test(webhookUrl.trim()) && (\n <p style={{ ...hintStyle, color: 'var(--color-status-warning-text)' }}>\n Warning: <code>http://</code> is unencrypted — the widget's data will be sent in plaintext. Use{' '}\n <code>https://</code> if it may contain anything sensitive.\n </p>\n )}\n </div>\n )}\n\n {/* Confirmation Section */}\n <div style={{ paddingTop: 'var(--space-formGap)', borderTop: '1px solid var(--color-border)' }}>\n <label\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n cursor: 'pointer',\n marginBottom: 'var(--space-formGap)',\n }}\n >\n <Checkbox\n checked={hasConfirmation}\n onCheckedChange={(c) => setHasConfirmation(c === true)}\n disabled={isLoading}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n Ask for confirmation first\n </span>\n </label>\n\n {hasConfirmation && (\n <div\n style={{\n paddingLeft: 'var(--space-formGap)',\n borderLeft: '2px solid var(--color-border)',\n display: 'flex',\n flexDirection: 'column',\n gap: 'var(--space-controlGap)',\n }}\n >\n <div>\n <label\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Dialog Title\n </label>\n <Input\n value={confirmTitle}\n onChange={(e) => setConfirmTitle(e.target.value)}\n disabled={isLoading}\n style={{ height: 'var(--size-controlHeight-sm)', fontSize: 'var(--font-size-body-sm)' }}\n />\n </div>\n <div>\n <label\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Message\n </label>\n <Textarea\n value={confirmMessage}\n onChange={(e) => setConfirmMessage(e.target.value)}\n rows={2}\n disabled={isLoading}\n style={{ fontSize: 'var(--font-size-body-sm)', resize: 'none' }}\n />\n </div>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 'var(--space-controlGap)' }}>\n <div>\n <label\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Confirm Button\n </label>\n <Input\n value={confirmLabel}\n onChange={(e) => setConfirmLabel(e.target.value)}\n disabled={isLoading}\n style={{ height: 'var(--size-controlHeight-sm)', fontSize: 'var(--font-size-body-sm)' }}\n />\n </div>\n <div>\n <label\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Cancel Button\n </label>\n <Input\n value={cancelLabel}\n onChange={(e) => setCancelLabel(e.target.value)}\n disabled={isLoading}\n style={{ height: 'var(--size-controlHeight-sm)', fontSize: 'var(--font-size-body-sm)' }}\n />\n </div>\n </div>\n </div>\n )}\n </div>\n\n {/* Advanced disclosure — event name + visibility */}\n <div style={{ paddingTop: 'var(--space-formGap)', borderTop: '1px solid var(--color-border)' }}>\n <button\n type=\"button\"\n onClick={() => setShowAdvanced((v) => !v)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n background: 'none',\n border: 'none',\n padding: 0,\n cursor: 'pointer',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {showAdvanced ? (\n <ChevronDown style={{ height: '14px', width: '14px' }} />\n ) : (\n <ChevronRight style={{ height: '14px', width: '14px' }} />\n )}\n Advanced\n </button>\n\n {showAdvanced && (\n <div\n style={{\n marginTop: 'var(--space-formGap)',\n display: 'flex',\n flexDirection: 'column',\n gap: 'var(--space-formGap)',\n }}\n >\n <div>\n <label style={labelStyle}>Event name (id)</label>\n <Input\n value={isEditMode && action ? action.name : name}\n onChange={(e) => {\n setName(e.target.value);\n setNameEdited(true);\n }}\n placeholder=\"auto from label\"\n // The event id is the webhook/consumer contract; it is fixed\n // once the action exists and cannot be changed on edit (#1).\n disabled={isLoading || isEditMode}\n style={{\n height: 'var(--size-controlHeight-md)',\n fontSize: 'var(--font-size-body-sm)',\n fontFamily: 'var(--font-family-mono)',\n }}\n />\n <p style={hintStyle}>\n Emits{' '}\n <code>\n widget:{isEditMode && action ? action.name : slugifyName(name || label) || '<name>'}:clicked\n </code>\n . {isEditMode ? 'Fixed once created.' : 'Auto-derived from the label.'}\n </p>\n </div>\n\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-sectionGap)' }}>\n <label\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n cursor: 'pointer',\n }}\n >\n <Checkbox\n checked={disabled}\n onCheckedChange={(c) => setDisabled(c === true)}\n disabled={isLoading}\n />\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-primary)' }}>\n Disabled\n </span>\n </label>\n <label\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n cursor: 'pointer',\n }}\n >\n <Checkbox checked={hidden} onCheckedChange={(c) => setHidden(c === true)} disabled={isLoading} />\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-primary)' }}>\n Hidden\n </span>\n </label>\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Footer */}\n <DialogFooter\n style={{\n padding: 'var(--space-formGap) var(--space-pagePadding)',\n borderTop: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-sunken)',\n display: 'flex',\n justifyContent: 'flex-end',\n gap: 'var(--space-controlGap)',\n }}\n >\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={onClose}\n disabled={isLoading}\n style={{ height: 'var(--size-controlHeight-md)' }}\n >\n Cancel\n </Button>\n <Button\n type=\"submit\"\n disabled={isLoading || !label.trim()}\n style={{ height: 'var(--size-controlHeight-md)', backgroundColor: 'var(--color-action-primary-bg)' }}\n >\n {isLoading ? (\n <span style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Spinner style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n Saving...\n </span>\n ) : (\n <span style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n {isEditMode ? (\n <Pencil style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n ) : (\n <Plus style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n )}\n {isEditMode ? 'Update Button' : 'Add Button'}\n </span>\n )}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n});\n\nexport default ActionConfigDialog;\n"],"mappings":";;;;;AAiFA,IAAM,KAAoE,CACxE;CACE,OAAO;CACP,OAAO;CACP,MAAM;CACP,EACD;CACE,OAAO;CACP,OAAO;CACP,MAAM;CACP,CACF,EAEK,KAA6B;CAAC;CAAQ;CAAO;CAAO;CAAS;CAAS,EAEtE,KAAwE;CAC5E;EAAE,OAAO;EAAW,OAAO;EAAW,OAAO;EAAkC;CAC/E;EAAE,OAAO;EAAa,OAAO;EAAa,OAAO;EAAoC;CACrF;EAAE,OAAO;EAAW,OAAO;EAAW,OAAO;EAAkC;CAC/E;EAAE,OAAO;EAAW,OAAO;EAAW,OAAO;EAAkC;CAC/E;EAAE,OAAO;EAAU,OAAO;EAAU,OAAO;EAAiC;CAC7E,EAEK,KAAe;CACnB;EAAE,OAAO;EAAI,OAAO;EAAW;CAC/B;EAAE,OAAO;EAAW,OAAO;EAAW;CACtC;EAAE,OAAO;EAAY,OAAO;EAAY;CACxC;EAAE,OAAO;EAAU,OAAO;EAAU;CACpC;EAAE,OAAO;EAAS,OAAO;EAAS;CAClC;EAAE,OAAO;EAAQ,OAAO;EAAQ;CAChC;EAAE,OAAO;EAAQ,OAAO;EAAQ;CAChC;EAAE,OAAO;EAAS,OAAO;EAAS;CAClC;EAAE,OAAO;EAAO,OAAO;EAAQ;CAC/B;EAAE,OAAO;EAAQ,OAAO;EAAQ;CAChC;EAAE,OAAO;EAAS,OAAO;EAAS;CAClC;EAAE,OAAO;EAAY,OAAO;EAAY;CACxC;EAAE,OAAO;EAAU,OAAO;EAAU;CACpC;EAAE,OAAO;EAAU,OAAO;EAAU;CACrC;AAGD,SAAS,EAAY,GAAuB;AAC1C,QAAO,EACJ,MAAM,CACN,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,YAAY,GAAG;;AAG5B,IAAM,IAAkC;CACtC,SAAS;CACT,UAAU;CACV,YAAY;CACZ,OAAO;CACP,cAAc;CACf,EAEK,IAAiC;CACrC,UAAU;CACV,OAAO;CACP,WAAW;CACZ,EAEK,IAA0C;CAC9C,QAAQ;CACR,iBAAiB;CACjB,QAAQ;CACR,WAAW;CACX,cAAc;CACf,EAMY,IAAkD,EAAK,SAA4B,EAC9F,WACA,WACA,mBACA,eAAY,IACZ,eACA,aACA,cACC;CACD,IAAM,IAAa,CAAC,CAAC,GACf,IAAgB,EAAyB,KAAK,EAG9C,IAAoB,EAAO,GAAM,EAGjC,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,CAAC,GAAM,KAAW,EAAS,GAAG,EAC9B,CAAC,GAAY,KAAiB,EAAS,GAAM,EAC7C,CAAC,GAAO,KAAY,EAAsB,UAAU,EACpD,CAAC,GAAM,KAAW,EAAS,GAAG,EAC9B,CAAC,GAAS,KAAc,EAAwB,UAAU,EAC1D,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAe,KAAoB,EAAqB,OAAO,EAChE,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,CAAC,IAAc,MAAmB,EAAS,GAAM,EACjD,CAAC,GAAiB,KAAsB,EAAS,GAAM,EACvD,CAAC,GAAc,KAAmB,EAAS,iBAAiB,EAC5D,CAAC,GAAgB,KAAqB,EAAS,oCAAoC,EACnF,CAAC,IAAc,KAAmB,EAAS,UAAU,EACrD,CAAC,IAAa,KAAkB,EAAS,SAAS,EAClD,CAAC,GAAO,KAAY,EAAwB,KAAK;AA4DvD,CAtDA,QAAgB;AACd,MAAI,CAAC,EAAQ;AA4Cb,EA1CA,EAAkB,UAAU,IAC5B,EAAc,GAAG,EACjB,EAAiB,OAAO,EAEpB,KACF,EAAS,EAAO,MAAM,EACtB,EAAQ,EAAO,KAAK,EACpB,EAAc,GAAM,EACpB,EAAS,EAAO,MAAM,EACtB,EAAQ,EAAO,QAAQ,GAAG,EAC1B,EAAY,EAAO,SAAS,EAC5B,EAAU,EAAO,OAAO,EAExB,EAAW,OAAO,EAClB,EAAmB,CAAC,CAAC,EAAO,aAAa,EACrC,EAAO,gBACT,EAAgB,EAAO,aAAa,MAAM,EAC1C,EAAkB,EAAO,aAAa,QAAQ,EAC9C,EAAgB,EAAO,aAAa,gBAAgB,UAAU,EAC9D,EAAe,EAAO,aAAa,eAAe,SAAS,KAE3D,EAAgB,iBAAiB,EACjC,EAAkB,oCAAoC,EACtD,EAAgB,UAAU,EAC1B,EAAe,SAAS,MAG1B,EAAS,GAAG,EACZ,EAAQ,GAAG,EACX,EAAc,GAAM,EACpB,EAAS,UAAU,EACnB,EAAQ,GAAG,EACX,EAAW,UAAU,EACrB,EAAY,GAAM,EAClB,EAAU,GAAM,EAChB,EAAmB,GAAM,EACzB,EAAgB,iBAAiB,EACjC,EAAkB,oCAAoC,EACtD,EAAgB,UAAU,EAC1B,EAAe,SAAS,GAE1B,GAAgB,GAAM,EACtB,EAAS,KAAK;EAGd,IAAM,IAAM,4BAA4B,4BAA4B,EAAc,SAAS,OAAO,CAAC,CAAC;AACpG,eAAa,qBAAqB,EAAI;IACrC,CAAC,GAAQ,EAAO,CAAC,EAIpB,QAAgB;AACV,GAAC,KAAU,CAAC,KAAkB,EAAkB,YACpD,EAAW,UAAU,EACrB,EAAc,EAAe,IAAI,EACjC,EAAiB,EAAe,OAAO;IACtC,CAAC,GAAQ,EAAe,CAAC;CAG5B,IAAM,KAAoB,GACvB,MAAkB;AAGjB,EAFA,EAAS,EAAM,EACV,KAAY,EAAQ,EAAY,EAAM,CAAC,EACxC,KAAO,EAAS,KAAK;IAE3B,CAAC,GAAY,EAAM,CACpB,EAEK,KAAe,GAClB,MAAuB;AACtB,IAAE,gBAAgB;EAElB,IAAM,IAAe,EAAM,MAAM;AACjC,MAAI,CAAC,GAAc;AAEjB,GADA,EAAS,2BAA2B,EACpC,EAAc,SAAS,OAAO;AAC9B;;EAOF,IAAM,IACJ,KAAc,IAAS,EAAO,OAAO,EAAY,IAAa,IAAO,EAAM,IAAI,EAAY,EAAM;AACnG,MAAI,CAAC,GAAW;AACd,KAAS,yCAAyC;AAClD;;AAGF,MAAI,MAAY,WAAW;GACzB,IAAM,IAAM,EAAW,MAAM;AAC7B,OAAI,CAAC,GAAK;AACR,MAAS,mDAAmD;AAC5D;;AAEF,OAAI,CAAC,gBAAgB,KAAK,EAAI,EAAE;AAC9B,MAAS,kDAAkD;AAC3D;;;EAIJ,IAAM,IAA+C,IACjD;GACE,OAAO,EAAa,MAAM,IAAI;GAC9B,SAAS,EAAe,MAAM,IAAI;GAClC,cAAc,GAAa,MAAM,IAAI;GACrC,aAAa,GAAY,MAAM,IAAI;GACpC,GACD,KAAA,GAEE,IACJ,MAAY,YAAY;GAAE,KAAK,EAAW,MAAM;GAAE,QAAQ;GAAe,GAAG;AAE9E,EAeE,GAfE,KAAc,IACmB;GACjC,IAAI,EAAO;GACX,MAAM;GACN,OAAO;GAIP,MAAM;GACN;GACA,MAAM,KAAQ,KAAA;GACd,cAAc,IAAkB,IAAe;GAC/C;GACA;GACD,GAGkC;GACjC,MAAM;GACN,OAAO;GAIP,MAAM;GACN;GACA,MAAM,KAAQ,KAAA;GACd;GACA;GACA;GACD,EAdkB,EAeQ;IAG/B;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF,EAEK,MAAiB,MACrB,GAAc,MAAM,MAAO,EAAG,UAAU,EAAE,EAAE,SAAS,kCAEjD,KAAgB,GAAS,MAAM,MAAM,EAAE,UAAU,EAAQ;AAE/D,QACE,kBAAC,IAAD;EAAQ,MAAM;EAAQ,eAAe,MAAS,CAAC,KAAQ,CAAC,KAAa,IAAS;YAC5E,kBAAC,IAAD;GAAe,OAAO;IAAE,UAAU;IAAS,SAAS;IAAG;aAAvD,CAEE,kBAAC,IAAD;IAAc,OAAO,EAAE,SAAS,uDAAuD;cACrF,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,YAAY;MAAU,KAAK;MAAwB;eAAlF,CACE,kBAAC,OAAD;MACE,OAAO;OACL,SAAS;OACT,QAAQ;OACR,OAAO;OACP,YAAY;OACZ,gBAAgB;OAChB,cAAc;OACd,iBAAiB;OACjB,WAAW;OACZ;gBAGC,EADD,IACE,KAQA,IARD,EACE,OAAO;OACL,QAAQ;OACR,OAAO;OACP,OAAO;OACR,EACD,CAQA;MAEA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,IAAD;MACE,OAAO;OACL,UAAU;OACV,YAAY;OACZ,OAAO;OACP,QAAQ;OACT;gBAEA,IAAa,gBAAgB;MAClB,CAAA,EACd,kBAAC,IAAD;MACE,OAAO;OACL,UAAU;OACV,OAAO;OACP,QAAQ;OACT;gBAEA,IAAa,uCAAuC;MACnC,CAAA,CAChB,EAAA,CAAA,CACF;;IACO,CAAA,EAEf,kBAAC,QAAD;IAAM,UAAU;cAAhB,CAEE,kBAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAA4B,WAAW;MAAQ,WAAW;MAAQ;eAAzF;OAEI,KAAS,OACT,kBAAC,OAAD;OACE,OAAO;QACL,SAAS;QACT,iBAAiB;QACjB,QAAQ;QACR,cAAc;QACd,cAAc;QACf;iBAED,kBAAC,KAAD;QAAG,OAAO;SAAE,UAAU;SAA4B,OAAO;SAAkC,QAAQ;SAAG;kBACnG,KAAS;QACR,CAAA;OACA,CAAA;MAIR,kBAAC,OAAD;OAAK,OAAO,EAAE,cAAc,wBAAwB;iBAApD;QACE,kBAAC,SAAD;SAAO,OAAO;mBAAd,CAA0B,iBACX,kBAAC,QAAD;UAAM,OAAO,EAAE,OAAO,kCAAkC;oBAAE;UAAQ,CAAA,CACzE;;QACR,kBAAC,GAAD;SACE,KAAK;SACL,OAAO;SACP,WAAW,MAAM,GAAkB,EAAE,OAAO,MAAM;SAClD,aAAY;SACZ,UAAU;SACV,OAAO;UAAE,QAAQ;UAAgC,UAAU;UAA4B;SACvF,CAAA;QACF,kBAAC,KAAD;SAAG,OAAO;mBAAW;SAA4B,CAAA;QAC7C;;MAGN,kBAAC,OAAD;OACE,OAAO;QACL,SAAS;QACT,qBAAqB;QACrB,KAAK;QACL,cAAc;QACf;iBANH,CAQE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,OAAO;kBAAY;QAAa,CAAA,EACvC,kBAAC,GAAD;QAAQ,OAAO;QAAO,gBAAgB,MAAM,EAAS,EAAiB;QAAE,UAAU;kBAAlF,CACE,kBAAC,GAAD;SACE,OAAO;UAAE,QAAQ;UAAgC,UAAU;UAA4B;mBAEvF,kBAAC,GAAD,EAAe,CAAA;SACD,CAAA,EAChB,kBAAC,GAAD;SAAe,OAAO;mBACnB,GAAc,KAAK,MAClB,kBAAC,GAAD;UAA0B,OAAO,EAAE;oBAChC,EAAE;UACQ,EAFI,EAAE,MAEN,CACb;SACY,CAAA,CACT;UACL,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,OAAO;kBAAY;QAAY,CAAA,EACtC,kBAAC,GAAD;QAAQ,OAAO;QAAM,eAAe;QAAS,UAAU;kBAAvD,CACE,kBAAC,GAAD;SACE,OAAO;UAAE,QAAQ;UAAgC,UAAU;UAA4B;mBAEvF,kBAAC,GAAD,EAAa,aAAY,WAAY,CAAA;SACvB,CAAA,EAChB,kBAAC,GAAD;SAAe,OAAO;UAAE,GAAG;UAAoB,WAAW;UAAS;mBAChE,GAAa,KAAK,MACjB,kBAAC,GAAD;UAAoC,OAAO,EAAE,SAAS;oBACnD,EAAE;UACQ,EAFI,EAAE,SAAS,OAEf,CACb;SACY,CAAA,CACT;UACL,EAAA,CAAA,CACF;;MAGN,kBAAC,OAAD;OACE,OAAO;QACL,SAAS;QACT,YAAY;QACZ,KAAK;QACL,cAAc;QACd,SAAS;QACT,iBAAiB;QACjB,cAAc;QACf;iBATH,CAWE,kBAAC,QAAD;QAAM,OAAO;SAAE,UAAU;SAA4B,OAAO;SAA2B;kBAAE;QAAe,CAAA,EACxG,kBAAC,UAAD;QACE,MAAK;QACL,OAAO;SACL,SAAS;SACT,UAAU;SACV,YAAY;SACZ,cAAc;SACd,iBAAiB,GAAc,EAAM;SACrC,OAAO;SACP,QAAQ;SACR,QAAQ;SACT;kBAEA,KAAS;QACH,CAAA,CACL;;MAGN,kBAAC,OAAD;OAAK,OAAO,EAAE,cAAc,wBAAwB;iBAApD;QACE,kBAAC,SAAD;SAAO,OAAO;mBAAY;SAAoB,CAAA;QAC9C,kBAAC,GAAD;SACE,OAAO;SACP,gBAAgB,MAAM;AAEpB,UADA,EAAkB,UAAU,IAC5B,EAAW,EAAmB;;SAEhC,UAAU;mBANZ,CAQE,kBAAC,GAAD;UAAe,OAAO;WAAE,QAAQ;WAAgC,UAAU;WAA4B;oBACpG,kBAAC,GAAD,EAAe,CAAA;UACD,CAAA,EAChB,kBAAC,GAAD;UAAe,OAAO;oBACnB,GAAS,KAAK,MACb,kBAAC,GAAD;WAA0B,OAAO,EAAE;qBAChC,EAAE;WACQ,EAFI,EAAE,MAEN,CACb;UACY,CAAA,CACT;;QACR,MAAiB,kBAAC,KAAD;SAAG,OAAO;mBAAY,GAAc;SAAS,CAAA;QAC3D;;MAGL,MAAY,aACX,kBAAC,OAAD;OACE,OAAO;QACL,cAAc;QACd,aAAa;QACb,YAAY;QACZ,SAAS;QACT,eAAe;QACf,KAAK;QACN;iBARH;QAUE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,OAAO;mBAAd,CAA0B,QACpB,kBAAC,QAAD;UAAM,OAAO,EAAE,OAAO,kCAAkC;oBAAE;UAAQ,CAAA,CAChE;YACR,kBAAC,GAAD;SACE,OAAO;SACP,WAAW,MAAM;AAGf,UAFA,EAAkB,UAAU,IAC5B,EAAc,EAAE,OAAO,MAAM,EACzB,KAAO,EAAS,KAAK;;SAE3B,aAAY;SACZ,UAAU;SACV,OAAO;UAAE,QAAQ;UAAgC,UAAU;UAA4B;SACvF,CAAA,CACE,EAAA,CAAA;QACN,kBAAC,OAAD;SAAK,OAAO,EAAE,UAAU,SAAS;mBAAjC,CACE,kBAAC,SAAD;UAAO,OAAO;oBAAY;UAAc,CAAA,EACxC,kBAAC,GAAD;UACE,OAAO;UACP,gBAAgB,MAAM;AAEpB,WADA,EAAkB,UAAU,IAC5B,EAAiB,EAAgB;;UAEnC,UAAU;oBANZ,CAQE,kBAAC,GAAD;WACE,OAAO;YAAE,QAAQ;YAAgC,UAAU;YAA4B;qBAEvF,kBAAC,GAAD,EAAe,CAAA;WACD,CAAA,EAChB,kBAAC,GAAD;WAAe,OAAO;qBACnB,GAAa,KAAK,MACjB,kBAAC,GAAD;YAAoB,OAAO;sBACxB;YACU,EAFI,EAEJ,CACb;WACY,CAAA,CACT;YACL;;QACN,kBAAC,KAAD;SAAG,OAAO;mBAAW;SAAkE,CAAA;QACtF,cAAc,KAAK,EAAW,MAAM,CAAC,IACpC,kBAAC,KAAD;SAAG,OAAO;UAAE,GAAG;UAAW,OAAO;UAAoC;mBAArE;UAAuE;UAC5D,kBAAC,QAAD,EAAA,UAAM,WAAc,CAAA;;UAAwE;UACrG,kBAAC,QAAD,EAAA,UAAM,YAAe,CAAA;;UACnB;;QAEF;;MAIR,kBAAC,OAAD;OAAK,OAAO;QAAE,YAAY;QAAwB,WAAW;QAAiC;iBAA9F,CACE,kBAAC,SAAD;QACE,OAAO;SACL,SAAS;SACT,YAAY;SACZ,KAAK;SACL,QAAQ;SACR,cAAc;SACf;kBAPH,CASE,kBAAC,GAAD;SACE,SAAS;SACT,kBAAkB,MAAM,EAAmB,MAAM,GAAK;SACtD,UAAU;SACV,CAAA,EACF,kBAAC,QAAD;SACE,OAAO;UACL,UAAU;UACV,YAAY;UACZ,OAAO;UACR;mBACF;SAEM,CAAA,CACD;WAEP,KACC,kBAAC,OAAD;QACE,OAAO;SACL,aAAa;SACb,YAAY;SACZ,SAAS;SACT,eAAe;SACf,KAAK;SACN;kBAPH;SASE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;UACE,OAAO;WACL,SAAS;WACT,UAAU;WACV,YAAY;WACZ,OAAO;WACP,cAAc;WACf;oBACF;UAEO,CAAA,EACR,kBAAC,GAAD;UACE,OAAO;UACP,WAAW,MAAM,EAAgB,EAAE,OAAO,MAAM;UAChD,UAAU;UACV,OAAO;WAAE,QAAQ;WAAgC,UAAU;WAA4B;UACvF,CAAA,CACE,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;UACE,OAAO;WACL,SAAS;WACT,UAAU;WACV,YAAY;WACZ,OAAO;WACP,cAAc;WACf;oBACF;UAEO,CAAA,EACR,kBAAC,IAAD;UACE,OAAO;UACP,WAAW,MAAM,EAAkB,EAAE,OAAO,MAAM;UAClD,MAAM;UACN,UAAU;UACV,OAAO;WAAE,UAAU;WAA4B,QAAQ;WAAQ;UAC/D,CAAA,CACE,EAAA,CAAA;SACN,kBAAC,OAAD;UAAK,OAAO;WAAE,SAAS;WAAQ,qBAAqB;WAAW,KAAK;WAA2B;oBAA/F,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;WACE,OAAO;YACL,SAAS;YACT,UAAU;YACV,YAAY;YACZ,OAAO;YACP,cAAc;YACf;qBACF;WAEO,CAAA,EACR,kBAAC,GAAD;WACE,OAAO;WACP,WAAW,MAAM,EAAgB,EAAE,OAAO,MAAM;WAChD,UAAU;WACV,OAAO;YAAE,QAAQ;YAAgC,UAAU;YAA4B;WACvF,CAAA,CACE,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;WACE,OAAO;YACL,SAAS;YACT,UAAU;YACV,YAAY;YACZ,OAAO;YACP,cAAc;YACf;qBACF;WAEO,CAAA,EACR,kBAAC,GAAD;WACE,OAAO;WACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;WAC/C,UAAU;WACV,OAAO;YAAE,QAAQ;YAAgC,UAAU;YAA4B;WACvF,CAAA,CACE,EAAA,CAAA,CACF;;SACF;UAEJ;;MAGN,kBAAC,OAAD;OAAK,OAAO;QAAE,YAAY;QAAwB,WAAW;QAAiC;iBAA9F,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,IAAiB,MAAM,CAAC,EAAE;QACzC,OAAO;SACL,SAAS;SACT,YAAY;SACZ,KAAK;SACL,YAAY;SACZ,QAAQ;SACR,SAAS;SACT,QAAQ;SACR,UAAU;SACV,YAAY;SACZ,OAAO;SACR;kBAdH,CAiBI,EADD,KACE,KAEA,IAFD,EAAa,OAAO;SAAE,QAAQ;SAAQ,OAAO;SAAQ,EAAI,CAEC,EAC1D,WAEK;WAER,MACC,kBAAC,OAAD;QACE,OAAO;SACL,WAAW;SACX,SAAS;SACT,eAAe;SACf,KAAK;SACN;kBANH,CAQE,kBAAC,OAAD,EAAA,UAAA;SACE,kBAAC,SAAD;UAAO,OAAO;oBAAY;UAAuB,CAAA;SACjD,kBAAC,GAAD;UACE,OAAO,KAAc,IAAS,EAAO,OAAO;UAC5C,WAAW,MAAM;AAEf,WADA,EAAQ,EAAE,OAAO,MAAM,EACvB,EAAc,GAAK;;UAErB,aAAY;UAGZ,UAAU,KAAa;UACvB,OAAO;WACL,QAAQ;WACR,UAAU;WACV,YAAY;WACb;UACD,CAAA;SACF,kBAAC,KAAD;UAAG,OAAO;oBAAV;WAAqB;WACb;WACN,kBAAC,QAAD,EAAA,UAAA;YAAM;YACI,KAAc,IAAS,EAAO,OAAO,EAAY,KAAQ,EAAM,IAAI;YAAS;YAC/E,EAAA,CAAA;;WACJ,IAAa,wBAAwB;WACtC;;SACA,EAAA,CAAA,EAEN,kBAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAQ,YAAY;UAAU,KAAK;UAA2B;mBAArF,CACE,kBAAC,SAAD;UACE,OAAO;WACL,SAAS;WACT,YAAY;WACZ,KAAK;WACL,QAAQ;WACT;oBANH,CAQE,kBAAC,GAAD;WACE,SAAS;WACT,kBAAkB,MAAM,EAAY,MAAM,GAAK;WAC/C,UAAU;WACV,CAAA,EACF,kBAAC,QAAD;WAAM,OAAO;YAAE,UAAU;YAA4B,OAAO;YAA6B;qBAAE;WAEpF,CAAA,CACD;aACR,kBAAC,SAAD;UACE,OAAO;WACL,SAAS;WACT,YAAY;WACZ,KAAK;WACL,QAAQ;WACT;oBANH,CAQE,kBAAC,GAAD;WAAU,SAAS;WAAQ,kBAAkB,MAAM,EAAU,MAAM,GAAK;WAAE,UAAU;WAAa,CAAA,EACjG,kBAAC,QAAD;WAAM,OAAO;YAAE,UAAU;YAA4B,OAAO;YAA6B;qBAAE;WAEpF,CAAA,CACD;YACJ;WACF;UAEJ;;MACF;QAGN,kBAAC,IAAD;KACE,OAAO;MACL,SAAS;MACT,WAAW;MACX,iBAAiB;MACjB,SAAS;MACT,gBAAgB;MAChB,KAAK;MACN;eARH,CAUE,kBAAC,IAAD;MACE,MAAK;MACL,SAAQ;MACR,SAAS;MACT,UAAU;MACV,OAAO,EAAE,QAAQ,gCAAgC;gBAClD;MAEQ,CAAA,EACT,kBAAC,IAAD;MACE,MAAK;MACL,UAAU,KAAa,CAAC,EAAM,MAAM;MACpC,OAAO;OAAE,QAAQ;OAAgC,iBAAiB;OAAkC;gBAEnG,IACC,kBAAC,QAAD;OAAM,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,KAAK;QAAwB;iBAAnF,CACE,kBAAC,IAAD,EAAS,OAAO;QAAE,QAAQ;QAAuB,OAAO;QAAuB,EAAI,CAAA,EAAA,YAE9E;WAEP,kBAAC,QAAD;OAAM,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,KAAK;QAAwB;iBAAnF,CAEI,EADD,IACE,KAEA,IAFD,EAAQ,OAAO;QAAE,QAAQ;QAAuB,OAAO;QAAuB,EAAI,CAEF,EAEjF,IAAa,kBAAkB,aAC3B;;MAEF,CAAA,CACI;OACV;MACO;;EACT,CAAA;EAEX"}
|
|
1
|
+
{"version":3,"file":"ActionConfigDialog.js","names":[],"sources":["../../../../src/bigconsole/components/widgets/ActionConfigDialog.tsx"],"sourcesContent":["/**\n * ActionConfigDialog Component (v2.1)\n *\n * Modal dialog for creating/editing widget actions.\n *\n * v2.1 (BOFF-5659): reframed around the *outcome* — \"When clicked, do this\" —\n * instead of the abstract action Type. A button always emits\n * `widget:<name>:clicked`; the dialog now lets the user attach the behaviour\n * inline. Today the two shippable outcomes are:\n * - \"Call a webhook / my API\" → captures a URL + method; the parent pairs the\n * action with an EventWebhookMapping bound to that event (no backend change).\n * - \"Just emit an event\" → the raw emit, for callers wiring their own\n * consumers.\n * The action `name` (the event id) is auto-derived from the label and tucked\n * under Advanced so there is a single required field.\n *\n * Uses fe-libs shared UI components and semantic tokens for consistent styling.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect, useRef } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Textarea } from '@burdenoff/fe-libs/ui';\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@burdenoff/fe-libs/ui';\nimport { Checkbox } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { Zap, Plus, Pencil, ChevronDown, ChevronRight } from 'lucide-react';\nimport { BIGCONSOLE_PERMISSIONS } from '../../../constants/permissions';\nimport type {\n WidgetAction,\n ActionStyle,\n ActionConfirmation,\n CreateWidgetActionInput,\n UpdateWidgetActionInput,\n} from '../../hooks/useWidgetActions';\nimport type { HttpMethod } from '../../hooks/useEventWebhooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** What should happen when the button is clicked. */\nexport type ActionOutcome = 'WEBHOOK' | 'EMIT';\n\n/** Webhook binding drafted in the dialog and reconciled by the parent. */\nexport interface ActionWebhookDraft {\n url: string;\n method: HttpMethod;\n}\n\n/**\n * The action fields passed to the create/update mutation. The webhook binding\n * is passed as a SEPARATE onSave argument (never embedded here) so it can never\n * leak into the GraphQL input.\n */\nexport type ActionSaveData = Omit<CreateWidgetActionInput, 'widgetId'> | UpdateWidgetActionInput;\n\nexport interface ActionConfigDialogProps {\n isOpen: boolean;\n action?: WidgetAction;\n /** Existing webhook binding for the edited action (prefills the URL field). */\n initialWebhook?: ActionWebhookDraft | null;\n isLoading?: boolean;\n /** A save failure from the parent (action or webhook mutation) to surface inline. */\n saveError?: string | null;\n onClose: () => void;\n /** `webhook` is the paired binding, or `null` to remove any existing one. */\n onSave: (data: ActionSaveData, webhook: ActionWebhookDraft | null) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst OUTCOMES: { value: ActionOutcome; label: string; hint: string }[] = [\n {\n value: 'WEBHOOK',\n label: 'Call a webhook / my API',\n hint: 'Sends an HTTP request to the URL below every time the button is clicked.',\n },\n {\n value: 'EMIT',\n label: 'Just emit an event (advanced)',\n hint: 'Emits widget:<name>:clicked. Wire it up yourself in the Webhooks/Events section.',\n },\n];\n\nconst HTTP_METHODS: HttpMethod[] = ['POST', 'GET', 'PUT', 'PATCH', 'DELETE'];\n\nconst ACTION_STYLES: { value: ActionStyle; label: string; color: string }[] = [\n { value: 'PRIMARY', label: 'Primary', color: 'var(--color-action-primary-bg)' },\n { value: 'SECONDARY', label: 'Secondary', color: 'var(--color-action-secondary-bg)' },\n { value: 'SUCCESS', label: 'Success', color: 'var(--color-status-success-bg)' },\n { value: 'WARNING', label: 'Warning', color: 'var(--color-status-warning-bg)' },\n { value: 'DANGER', label: 'Danger', color: 'var(--color-action-danger-bg)' },\n];\n\nconst ICON_OPTIONS = [\n { value: '', label: 'No icon' },\n { value: 'refresh', label: 'Refresh' },\n { value: 'download', label: 'Download' },\n { value: 'upload', label: 'Upload' },\n { value: 'trash', label: 'Trash' },\n { value: 'edit', label: 'Edit' },\n { value: 'plus', label: 'Plus' },\n { value: 'check', label: 'Check' },\n { value: 'eye', label: 'View' },\n { value: 'copy', label: 'Copy' },\n { value: 'share', label: 'Share' },\n { value: 'settings', label: 'Settings' },\n { value: 'filter', label: 'Filter' },\n { value: 'search', label: 'Search' },\n];\n\n// Derive the event-id (action name) from a human label: \"Refresh Data\" -> \"refresh_data\".\nfunction slugifyName(label: string): string {\n return label\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '');\n}\n\nconst labelStyle: React.CSSProperties = {\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n marginBottom: 'var(--space-controlGap)',\n};\n\nconst hintStyle: React.CSSProperties = {\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-muted)',\n marginTop: 'var(--space-controlGap)',\n};\n\nconst selectContentStyle: React.CSSProperties = {\n zIndex: 9999,\n backgroundColor: 'var(--color-overlay-popoverBg, var(--color-bg-surface))',\n border: '1px solid var(--color-border)',\n boxShadow: 'var(--shadow-elevation-3)',\n borderRadius: 'var(--radius-button)',\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ActionConfigDialog: FC<ActionConfigDialogProps> = memo(function ActionConfigDialog({\n isOpen,\n action,\n initialWebhook,\n isLoading = false,\n saveError,\n onClose,\n onSave,\n}) {\n const isEditMode = !!action;\n const labelInputRef = useRef<HTMLInputElement>(null);\n // True once the user touches the webhook fields, so a late-arriving\n // `initialWebhook` fetch can seed the form without clobbering in-progress edits.\n const webhookTouchedRef = useRef(false);\n\n // Form state\n const [label, setLabel] = useState('');\n const [name, setName] = useState('');\n const [nameEdited, setNameEdited] = useState(false);\n const [style, setStyle] = useState<ActionStyle>('PRIMARY');\n const [icon, setIcon] = useState('');\n const [outcome, setOutcome] = useState<ActionOutcome>('WEBHOOK');\n const [webhookUrl, setWebhookUrl] = useState('');\n const [webhookMethod, setWebhookMethod] = useState<HttpMethod>('POST');\n const [disabled, setDisabled] = useState(false);\n const [hidden, setHidden] = useState(false);\n const [showAdvanced, setShowAdvanced] = useState(false);\n const [hasConfirmation, setHasConfirmation] = useState(false);\n const [confirmTitle, setConfirmTitle] = useState('Confirm Action');\n const [confirmMessage, setConfirmMessage] = useState('Are you sure you want to proceed?');\n const [confirmLabel, setConfirmLabel] = useState('Confirm');\n const [cancelLabel, setCancelLabel] = useState('Cancel');\n const [error, setError] = useState<string | null>(null);\n\n // Reset form when the dialog opens or the edited action changes. Deliberately\n // does NOT depend on `initialWebhook` — that fetch can settle after the dialog\n // is open, and re-running this would wipe the user's in-progress edits (#6).\n // The webhook fields are seeded by the separate effect below.\n useEffect(() => {\n if (!isOpen) return;\n\n webhookTouchedRef.current = false;\n setWebhookUrl('');\n setWebhookMethod('POST');\n\n if (action) {\n setLabel(action.label);\n setName(action.name);\n setNameEdited(false);\n setStyle(action.style);\n setIcon(action.icon || '');\n setDisabled(action.disabled);\n setHidden(action.hidden);\n // Default to \"emit\"; the seed effect flips to WEBHOOK if a mapping exists.\n setOutcome('EMIT');\n setHasConfirmation(!!action.confirmation);\n if (action.confirmation) {\n setConfirmTitle(action.confirmation.title);\n setConfirmMessage(action.confirmation.message);\n setConfirmLabel(action.confirmation.confirmLabel || 'Confirm');\n setCancelLabel(action.confirmation.cancelLabel || 'Cancel');\n } else {\n setConfirmTitle('Confirm Action');\n setConfirmMessage('Are you sure you want to proceed?');\n setConfirmLabel('Confirm');\n setCancelLabel('Cancel');\n }\n } else {\n setLabel('');\n setName('');\n setNameEdited(false);\n setStyle('PRIMARY');\n setIcon('');\n setOutcome('WEBHOOK');\n setDisabled(false);\n setHidden(false);\n setHasConfirmation(false);\n setConfirmTitle('Confirm Action');\n setConfirmMessage('Are you sure you want to proceed?');\n setConfirmLabel('Confirm');\n setCancelLabel('Cancel');\n }\n setShowAdvanced(false);\n setError(null);\n // Focus the label after the dialog paints — a double rAF is more reliable than\n // a magic setTimeout delay and doesn't race the open animation.\n const raf = requestAnimationFrame(() => requestAnimationFrame(() => labelInputRef.current?.focus()));\n return () => cancelAnimationFrame(raf);\n }, [isOpen, action]);\n\n // Seed the webhook fields from the (possibly late-arriving) mapping fetch,\n // unless the user has already started editing them.\n useEffect(() => {\n if (!isOpen || !initialWebhook || webhookTouchedRef.current) return;\n setOutcome('WEBHOOK');\n setWebhookUrl(initialWebhook.url);\n setWebhookMethod(initialWebhook.method);\n }, [isOpen, initialWebhook]);\n\n // Keep the auto-derived name in sync with the label until the user overrides it.\n const handleLabelChange = useCallback(\n (value: string) => {\n setLabel(value);\n if (!nameEdited) setName(slugifyName(value));\n if (error) setError(null);\n },\n [nameEdited, error]\n );\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n\n const trimmedLabel = label.trim();\n if (!trimmedLabel) {\n setError('Button label is required');\n labelInputRef.current?.focus();\n return;\n }\n\n // The action name is the event id (webhook contract). It is set once at\n // creation and never rewritten on edit — re-slugifying a stored name like\n // `refresh-v2` would silently change `widget:refresh-v2:clicked` and break\n // existing bindings/consumers (#1).\n const finalName =\n isEditMode && action ? action.name : slugifyName(nameEdited ? name : label) || slugifyName(label);\n if (!finalName) {\n setError('Enter a label using letters or numbers');\n return;\n }\n\n if (outcome === 'WEBHOOK') {\n const url = webhookUrl.trim();\n if (!url) {\n setError('Enter the URL to call when the button is clicked');\n return;\n }\n if (!/^https?:\\/\\//i.test(url)) {\n setError('Webhook URL must start with http:// or https://');\n return;\n }\n }\n\n const confirmation: ActionConfirmation | undefined = hasConfirmation\n ? {\n title: confirmTitle.trim() || 'Confirm Action',\n message: confirmMessage.trim() || 'Are you sure?',\n confirmLabel: confirmLabel.trim() || 'Confirm',\n cancelLabel: cancelLabel.trim() || 'Cancel',\n }\n : undefined;\n\n const webhook: ActionWebhookDraft | null =\n outcome === 'WEBHOOK' ? { url: webhookUrl.trim(), method: webhookMethod } : null;\n\n if (isEditMode && action) {\n const updateData: ActionSaveData = {\n id: action.id,\n name: finalName,\n label: trimmedLabel,\n // Webhook/emit outcomes must be a BUTTON so the click runs onAction and\n // emits the event. Saving a stale LINK type would render an <a> that\n // bypasses onAction, so the webhook would never fire (#3).\n type: 'BUTTON',\n style,\n icon: icon || undefined,\n confirmation: hasConfirmation ? confirmation : null,\n disabled,\n hidden,\n };\n onSave(updateData, webhook);\n } else {\n const createData: ActionSaveData = {\n name: finalName,\n label: trimmedLabel,\n // Webhook/emit outcomes must be a BUTTON so the click runs onAction and\n // emits the event. Saving a stale LINK type would render an <a> that\n // bypasses onAction, so the webhook would never fire (#3).\n type: 'BUTTON',\n style,\n icon: icon || undefined,\n confirmation,\n disabled,\n hidden,\n };\n onSave(createData, webhook);\n }\n },\n [\n label,\n name,\n nameEdited,\n outcome,\n webhookUrl,\n webhookMethod,\n style,\n icon,\n disabled,\n hidden,\n hasConfirmation,\n confirmTitle,\n confirmMessage,\n confirmLabel,\n cancelLabel,\n isEditMode,\n action,\n onSave,\n ]\n );\n\n const getStyleColor = (s: ActionStyle) =>\n ACTION_STYLES.find((as) => as.value === s)?.color || 'var(--color-action-primary-bg)';\n\n const activeOutcome = OUTCOMES.find((o) => o.value === outcome);\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && !isLoading && onClose()}>\n <DialogContent style={{ maxWidth: '500px', padding: 0 }}>\n {/* Header */}\n <DialogHeader style={{ padding: 'var(--space-pagePadding) var(--space-pagePadding) 0' }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-formGap)' }}>\n <div\n style={{\n display: 'flex',\n height: 'var(--size-controlHeight-lg)',\n width: 'var(--size-controlHeight-lg)',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: 'var(--radius-card)',\n backgroundColor: 'var(--color-action-primary-bg)',\n boxShadow: 'var(--shadow-elevation-2)',\n }}\n >\n {isEditMode ? (\n <Pencil\n style={{\n height: 'var(--size-icon-lg)',\n width: 'var(--size-icon-lg)',\n color: 'var(--color-action-primary-text)',\n }}\n />\n ) : (\n <Zap\n style={{\n height: 'var(--size-icon-lg)',\n width: 'var(--size-icon-lg)',\n color: 'var(--color-action-primary-text)',\n }}\n />\n )}\n </div>\n <div>\n <DialogTitle\n style={{\n fontSize: 'var(--font-size-heading-h4)',\n fontWeight: 'var(--font-weight-bold)',\n color: 'var(--color-text-primary)',\n margin: 0,\n }}\n >\n {isEditMode ? 'Edit Button' : 'Add Button'}\n </DialogTitle>\n <DialogDescription\n style={{\n fontSize: 'var(--font-size-body-sm)',\n color: 'var(--color-text-secondary)',\n margin: 'var(--space-controlGap) 0 0 0',\n }}\n >\n {isEditMode ? 'Update the button and what it does' : 'Add a button and choose what it does'}\n </DialogDescription>\n </div>\n </div>\n </DialogHeader>\n\n <form onSubmit={handleSubmit}>\n {/* Form Content */}\n <div style={{ padding: 'var(--space-pagePadding)', maxHeight: '60vh', overflowY: 'auto' }}>\n {/* Error Display — local validation error or a save failure from the parent. */}\n {(error || saveError) && (\n <div\n style={{\n padding: 'var(--space-controlGap)',\n backgroundColor: 'var(--color-status-error-bgSubtle)',\n border: '1px solid var(--color-status-error-border)',\n borderRadius: 'var(--radius-button)',\n marginBottom: 'var(--space-formGap)',\n }}\n >\n <p style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-status-error-text)', margin: 0 }}>\n {error || saveError}\n </p>\n </div>\n )}\n\n {/* Label Field */}\n <div style={{ marginBottom: 'var(--space-formGap)' }}>\n <label style={labelStyle}>\n Button label <span style={{ color: 'var(--color-status-error-text)' }}>*</span>\n </label>\n <Input\n ref={labelInputRef}\n value={label}\n onChange={(e) => handleLabelChange(e.target.value)}\n placeholder=\"e.g., Refresh Data, Export CSV\"\n disabled={isLoading}\n style={{ height: 'var(--size-controlHeight-md)', fontSize: 'var(--font-size-body-sm)' }}\n />\n <p style={hintStyle}>Text shown on the button</p>\n </div>\n\n {/* Style + Icon Row */}\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: '1fr 1fr',\n gap: 'var(--space-formGap)',\n marginBottom: 'var(--space-formGap)',\n }}\n >\n <div>\n <label style={labelStyle}>Style</label>\n <Select value={style} onValueChange={(v) => setStyle(v as ActionStyle)} disabled={isLoading}>\n <SelectTrigger\n style={{ height: 'var(--size-controlHeight-md)', fontSize: 'var(--font-size-body-sm)' }}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent style={selectContentStyle}>\n {ACTION_STYLES.map((s) => (\n <SelectItem key={s.value} value={s.value}>\n {s.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <div>\n <label style={labelStyle}>Icon</label>\n <Select value={icon} onValueChange={setIcon} disabled={isLoading}>\n <SelectTrigger\n style={{ height: 'var(--size-controlHeight-md)', fontSize: 'var(--font-size-body-sm)' }}\n >\n <SelectValue placeholder=\"No icon\" />\n </SelectTrigger>\n <SelectContent style={{ ...selectContentStyle, maxHeight: '200px' }}>\n {ICON_OPTIONS.map((i) => (\n <SelectItem key={i.value || 'none'} value={i.value || 'none'}>\n {i.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n\n {/* Style Preview */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n marginBottom: 'var(--space-formGap)',\n padding: 'var(--space-controlGap)',\n backgroundColor: 'var(--color-bg-sunken)',\n borderRadius: 'var(--radius-button)',\n }}\n >\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-muted)' }}>Preview:</span>\n <button\n type=\"button\"\n style={{\n padding: 'var(--space-controlGap) var(--space-formGap)',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n borderRadius: 'var(--radius-button)',\n backgroundColor: getStyleColor(style),\n color: 'var(--color-action-primary-text)',\n border: 'none',\n cursor: 'default',\n }}\n >\n {label || 'Button'}\n </button>\n </div>\n\n {/* When clicked (outcome) */}\n <div style={{ marginBottom: 'var(--space-formGap)' }}>\n <label style={labelStyle}>When clicked</label>\n <Select\n value={outcome}\n onValueChange={(v) => {\n webhookTouchedRef.current = true;\n setOutcome(v as ActionOutcome);\n }}\n disabled={isLoading}\n >\n <SelectTrigger style={{ height: 'var(--size-controlHeight-md)', fontSize: 'var(--font-size-body-sm)' }}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent style={selectContentStyle}>\n {OUTCOMES.map((o) => (\n <SelectItem key={o.value} value={o.value}>\n {o.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {activeOutcome && <p style={hintStyle}>{activeOutcome.hint}</p>}\n </div>\n\n {/* Webhook config (only for the WEBHOOK outcome) */}\n {outcome === 'WEBHOOK' && (\n <div\n style={{\n marginBottom: 'var(--space-formGap)',\n paddingLeft: 'var(--space-formGap)',\n borderLeft: '2px solid var(--color-border)',\n display: 'flex',\n flexDirection: 'column',\n gap: 'var(--space-controlGap)',\n }}\n >\n <div>\n <label style={labelStyle}>\n URL <span style={{ color: 'var(--color-status-error-text)' }}>*</span>\n </label>\n <Input\n value={webhookUrl}\n onChange={(e) => {\n webhookTouchedRef.current = true;\n setWebhookUrl(e.target.value);\n if (error) setError(null);\n }}\n placeholder=\"https://your-service.com/hook\"\n disabled={isLoading}\n style={{ height: 'var(--size-controlHeight-md)', fontSize: 'var(--font-size-body-sm)' }}\n />\n </div>\n <div style={{ maxWidth: '160px' }}>\n <label style={labelStyle}>Method</label>\n <Select\n value={webhookMethod}\n onValueChange={(v) => {\n webhookTouchedRef.current = true;\n setWebhookMethod(v as HttpMethod);\n }}\n disabled={isLoading}\n >\n <SelectTrigger\n style={{ height: 'var(--size-controlHeight-md)', fontSize: 'var(--font-size-body-sm)' }}\n >\n <SelectValue />\n </SelectTrigger>\n <SelectContent style={selectContentStyle}>\n {HTTP_METHODS.map((m) => (\n <SelectItem key={m} value={m}>\n {m}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n <p style={hintStyle}>The widget's current data is sent as the request payload.</p>\n {/^http:\\/\\//i.test(webhookUrl.trim()) && (\n <p style={{ ...hintStyle, color: 'var(--color-status-warning-text)' }}>\n Warning: <code>http://</code> is unencrypted — the widget's data will be sent in plaintext. Use{' '}\n <code>https://</code> if it may contain anything sensitive.\n </p>\n )}\n </div>\n )}\n\n {/* Confirmation Section */}\n <div style={{ paddingTop: 'var(--space-formGap)', borderTop: '1px solid var(--color-border)' }}>\n <label\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n cursor: 'pointer',\n marginBottom: 'var(--space-formGap)',\n }}\n >\n <Checkbox\n checked={hasConfirmation}\n onCheckedChange={(c) => setHasConfirmation(c === true)}\n disabled={isLoading}\n />\n <span\n style={{\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n Ask for confirmation first\n </span>\n </label>\n\n {hasConfirmation && (\n <div\n style={{\n paddingLeft: 'var(--space-formGap)',\n borderLeft: '2px solid var(--color-border)',\n display: 'flex',\n flexDirection: 'column',\n gap: 'var(--space-controlGap)',\n }}\n >\n <div>\n <label\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Dialog Title\n </label>\n <Input\n value={confirmTitle}\n onChange={(e) => setConfirmTitle(e.target.value)}\n disabled={isLoading}\n style={{ height: 'var(--size-controlHeight-sm)', fontSize: 'var(--font-size-body-sm)' }}\n />\n </div>\n <div>\n <label\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Message\n </label>\n <Textarea\n value={confirmMessage}\n onChange={(e) => setConfirmMessage(e.target.value)}\n rows={2}\n disabled={isLoading}\n style={{ fontSize: 'var(--font-size-body-sm)', resize: 'none' }}\n />\n </div>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 'var(--space-controlGap)' }}>\n <div>\n <label\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Confirm Button\n </label>\n <Input\n value={confirmLabel}\n onChange={(e) => setConfirmLabel(e.target.value)}\n disabled={isLoading}\n style={{ height: 'var(--size-controlHeight-sm)', fontSize: 'var(--font-size-body-sm)' }}\n />\n </div>\n <div>\n <label\n style={{\n display: 'block',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-medium)',\n color: 'var(--color-text-secondary)',\n marginBottom: 'var(--space-controlGap)',\n }}\n >\n Cancel Button\n </label>\n <Input\n value={cancelLabel}\n onChange={(e) => setCancelLabel(e.target.value)}\n disabled={isLoading}\n style={{ height: 'var(--size-controlHeight-sm)', fontSize: 'var(--font-size-body-sm)' }}\n />\n </div>\n </div>\n </div>\n )}\n </div>\n\n {/* Advanced disclosure — event name + visibility */}\n <div style={{ paddingTop: 'var(--space-formGap)', borderTop: '1px solid var(--color-border)' }}>\n <button\n type=\"button\"\n onClick={() => setShowAdvanced((v) => !v)}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n background: 'none',\n border: 'none',\n padding: 0,\n cursor: 'pointer',\n fontSize: 'var(--font-size-body-sm)',\n fontWeight: 'var(--font-weight-semibold)',\n color: 'var(--color-text-primary)',\n }}\n >\n {showAdvanced ? (\n <ChevronDown style={{ height: '14px', width: '14px' }} />\n ) : (\n <ChevronRight style={{ height: '14px', width: '14px' }} />\n )}\n Advanced\n </button>\n\n {showAdvanced && (\n <div\n style={{\n marginTop: 'var(--space-formGap)',\n display: 'flex',\n flexDirection: 'column',\n gap: 'var(--space-formGap)',\n }}\n >\n <div>\n <label style={labelStyle}>Event name (id)</label>\n <Input\n value={isEditMode && action ? action.name : name}\n onChange={(e) => {\n setName(e.target.value);\n setNameEdited(true);\n }}\n placeholder=\"auto from label\"\n // The event id is the webhook/consumer contract; it is fixed\n // once the action exists and cannot be changed on edit (#1).\n disabled={isLoading || isEditMode}\n style={{\n height: 'var(--size-controlHeight-md)',\n fontSize: 'var(--font-size-body-sm)',\n fontFamily: 'var(--font-family-mono)',\n }}\n />\n <p style={hintStyle}>\n Emits{' '}\n <code>\n widget:{isEditMode && action ? action.name : slugifyName(name || label) || '<name>'}:clicked\n </code>\n . {isEditMode ? 'Fixed once created.' : 'Auto-derived from the label.'}\n </p>\n </div>\n\n <div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-sectionGap)' }}>\n <label\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n cursor: 'pointer',\n }}\n >\n <Checkbox\n checked={disabled}\n onCheckedChange={(c) => setDisabled(c === true)}\n disabled={isLoading}\n />\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-primary)' }}>\n Disabled\n </span>\n </label>\n <label\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 'var(--space-controlGap)',\n cursor: 'pointer',\n }}\n >\n <Checkbox checked={hidden} onCheckedChange={(c) => setHidden(c === true)} disabled={isLoading} />\n <span style={{ fontSize: 'var(--font-size-body-sm)', color: 'var(--color-text-primary)' }}>\n Hidden\n </span>\n </label>\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Footer */}\n <DialogFooter\n style={{\n padding: 'var(--space-formGap) var(--space-pagePadding)',\n borderTop: '1px solid var(--color-border)',\n backgroundColor: 'var(--color-bg-sunken)',\n display: 'flex',\n justifyContent: 'flex-end',\n gap: 'var(--space-controlGap)',\n }}\n >\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={onClose}\n disabled={isLoading}\n style={{ height: 'var(--size-controlHeight-md)' }}\n >\n Cancel\n </Button>\n {/*\n Gate the save on the matching widget-action permission — create for\n a new button, update for an edit. `disable` keeps the button visible\n (so the reason is discoverable) but non-submittable when denied.\n */}\n <PermissionButton\n permission={\n isEditMode ? BIGCONSOLE_PERMISSIONS.WIDGET_ACTION_UPDATE : BIGCONSOLE_PERMISSIONS.WIDGET_ACTION_CREATE\n }\n deniedBehavior=\"disable\"\n >\n <Button\n type=\"submit\"\n disabled={isLoading || !label.trim()}\n style={{ height: 'var(--size-controlHeight-md)', backgroundColor: 'var(--color-action-primary-bg)' }}\n >\n {isLoading ? (\n <span style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n <Spinner style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n Saving...\n </span>\n ) : (\n <span style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-iconGap)' }}>\n {isEditMode ? (\n <Pencil style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n ) : (\n <Plus style={{ height: 'var(--size-icon-sm)', width: 'var(--size-icon-sm)' }} />\n )}\n {isEditMode ? 'Update Button' : 'Add Button'}\n </span>\n )}\n </Button>\n </PermissionButton>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n});\n\nexport default ActionConfigDialog;\n"],"mappings":"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"}
|
|
@@ -2,51 +2,53 @@ import { useWidgetStore as e } from "../../store/widgetStore.js";
|
|
|
2
2
|
import "../../store/index.js";
|
|
3
3
|
import t from "../../hooks/useWidgetOperations.js";
|
|
4
4
|
import "../../hooks/index.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { BIGCONSOLE_PERMISSIONS as n } from "../../../constants/permissions.js";
|
|
6
|
+
import { memo as r, useCallback as i, useEffect as a, useRef as o, useState as s } from "react";
|
|
7
|
+
import { usePermissions as c } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
8
|
+
import { Fragment as l, jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
9
|
+
import { createPortal as f } from "react-dom";
|
|
8
10
|
//#region src/bigconsole/components/widgets/WidgetMenu.tsx
|
|
9
|
-
var
|
|
10
|
-
let [
|
|
11
|
+
var p = r(function({ widget: r, isEditMode: p, onRefresh: m, onExportCSV: h, onExportPNG: g }) {
|
|
12
|
+
let [_, v] = s(!1), [y, b] = s({
|
|
11
13
|
top: 0,
|
|
12
14
|
left: 0
|
|
13
|
-
}),
|
|
14
|
-
|
|
15
|
+
}), x = o(null), S = o(null), { hasPermission: C } = c(), w = C(n.WIDGET_UPDATE), T = C(n.WIDGET_CREATE), E = C(n.WIDGET_DELETE), D = e((e) => e.selectWidget), O = e((e) => e.copyWidget), k = e((e) => e.cutWidget), A = e((e) => e.removeWidget), j = e((e) => e.openConfigPanel), { duplicateWidget: M, deleteWidget: N, refreshWidgetData: P } = t(r.pageId, r.dashboardId, { skipFetch: !0 });
|
|
16
|
+
a(() => {
|
|
15
17
|
let e = (e) => {
|
|
16
|
-
|
|
18
|
+
x.current && !x.current.contains(e.target) && S.current && !S.current.contains(e.target) && v(!1);
|
|
17
19
|
};
|
|
18
|
-
return
|
|
20
|
+
return _ && document.addEventListener("mousedown", e), () => {
|
|
19
21
|
document.removeEventListener("mousedown", e);
|
|
20
22
|
};
|
|
21
|
-
}, [
|
|
23
|
+
}, [_]), a(() => {
|
|
22
24
|
let e = (e) => {
|
|
23
|
-
e.key === "Escape" && (
|
|
25
|
+
e.key === "Escape" && (v(!1), S.current?.focus());
|
|
24
26
|
};
|
|
25
|
-
return
|
|
27
|
+
return _ && document.addEventListener("keydown", e), () => {
|
|
26
28
|
document.removeEventListener("keydown", e);
|
|
27
29
|
};
|
|
28
|
-
}, [
|
|
29
|
-
let
|
|
30
|
-
e.stopPropagation(),
|
|
31
|
-
if (!e &&
|
|
32
|
-
let e =
|
|
33
|
-
n + 160 > window.innerWidth - 10 && (n = window.innerWidth - 160 - 10), n < 10 && (n = 10), t + 320 > window.innerHeight - 10 && (t = e.top - 320 - 4, t < 10 && (t = 10)),
|
|
30
|
+
}, [_]);
|
|
31
|
+
let F = i((e) => {
|
|
32
|
+
e.stopPropagation(), v((e) => {
|
|
33
|
+
if (!e && S.current) {
|
|
34
|
+
let e = S.current.getBoundingClientRect(), t = e.bottom + 4, n = e.right - 160;
|
|
35
|
+
n + 160 > window.innerWidth - 10 && (n = window.innerWidth - 160 - 10), n < 10 && (n = 10), t + 320 > window.innerHeight - 10 && (t = e.top - 320 - 4, t < 10 && (t = 10)), b({
|
|
34
36
|
top: t + window.scrollY,
|
|
35
37
|
left: n + window.scrollX
|
|
36
38
|
});
|
|
37
39
|
}
|
|
38
40
|
return !e;
|
|
39
41
|
});
|
|
40
|
-
}, []),
|
|
42
|
+
}, []), I = [
|
|
41
43
|
{
|
|
42
44
|
id: "edit",
|
|
43
45
|
label: "Edit Widget",
|
|
44
|
-
icon: /* @__PURE__ */
|
|
46
|
+
icon: /* @__PURE__ */ u("svg", {
|
|
45
47
|
fill: "none",
|
|
46
48
|
stroke: "currentColor",
|
|
47
49
|
viewBox: "0 0 24 24",
|
|
48
50
|
className: "w-4 h-4",
|
|
49
|
-
children: /* @__PURE__ */
|
|
51
|
+
children: /* @__PURE__ */ u("path", {
|
|
50
52
|
strokeLinecap: "round",
|
|
51
53
|
strokeLinejoin: "round",
|
|
52
54
|
strokeWidth: 2,
|
|
@@ -54,19 +56,20 @@ var d = n(function({ widget: n, isEditMode: d, onRefresh: f, onExportCSV: p, onE
|
|
|
54
56
|
})
|
|
55
57
|
}),
|
|
56
58
|
action: () => {
|
|
57
|
-
|
|
59
|
+
D(r.id), j(), v(!1);
|
|
58
60
|
},
|
|
59
|
-
disabled: !
|
|
61
|
+
disabled: !p,
|
|
62
|
+
permitted: w
|
|
60
63
|
},
|
|
61
64
|
{
|
|
62
65
|
id: "refresh",
|
|
63
66
|
label: "Refresh Data",
|
|
64
|
-
icon: /* @__PURE__ */
|
|
67
|
+
icon: /* @__PURE__ */ u("svg", {
|
|
65
68
|
fill: "none",
|
|
66
69
|
stroke: "currentColor",
|
|
67
70
|
viewBox: "0 0 24 24",
|
|
68
71
|
className: "w-4 h-4",
|
|
69
|
-
children: /* @__PURE__ */
|
|
72
|
+
children: /* @__PURE__ */ u("path", {
|
|
70
73
|
strokeLinecap: "round",
|
|
71
74
|
strokeLinejoin: "round",
|
|
72
75
|
strokeWidth: 2,
|
|
@@ -74,19 +77,19 @@ var d = n(function({ widget: n, isEditMode: d, onRefresh: f, onExportCSV: p, onE
|
|
|
74
77
|
})
|
|
75
78
|
}),
|
|
76
79
|
action: async () => {
|
|
77
|
-
|
|
80
|
+
v(!1), m ? m() : await P(r.id);
|
|
78
81
|
},
|
|
79
82
|
disabled: !1
|
|
80
83
|
},
|
|
81
84
|
{
|
|
82
85
|
id: "exportCSV",
|
|
83
86
|
label: "Export CSV",
|
|
84
|
-
icon: /* @__PURE__ */
|
|
87
|
+
icon: /* @__PURE__ */ u("svg", {
|
|
85
88
|
fill: "none",
|
|
86
89
|
stroke: "currentColor",
|
|
87
90
|
viewBox: "0 0 24 24",
|
|
88
91
|
className: "w-4 h-4",
|
|
89
|
-
children: /* @__PURE__ */
|
|
92
|
+
children: /* @__PURE__ */ u("path", {
|
|
90
93
|
strokeLinecap: "round",
|
|
91
94
|
strokeLinejoin: "round",
|
|
92
95
|
strokeWidth: 2,
|
|
@@ -94,19 +97,19 @@ var d = n(function({ widget: n, isEditMode: d, onRefresh: f, onExportCSV: p, onE
|
|
|
94
97
|
})
|
|
95
98
|
}),
|
|
96
99
|
action: () => {
|
|
97
|
-
|
|
100
|
+
v(!1), h?.();
|
|
98
101
|
},
|
|
99
|
-
disabled: !
|
|
102
|
+
disabled: !h
|
|
100
103
|
},
|
|
101
104
|
{
|
|
102
105
|
id: "exportPNG",
|
|
103
106
|
label: "Export PNG",
|
|
104
|
-
icon: /* @__PURE__ */
|
|
107
|
+
icon: /* @__PURE__ */ u("svg", {
|
|
105
108
|
fill: "none",
|
|
106
109
|
stroke: "currentColor",
|
|
107
110
|
viewBox: "0 0 24 24",
|
|
108
111
|
className: "w-4 h-4",
|
|
109
|
-
children: /* @__PURE__ */
|
|
112
|
+
children: /* @__PURE__ */ u("path", {
|
|
110
113
|
strokeLinecap: "round",
|
|
111
114
|
strokeLinejoin: "round",
|
|
112
115
|
strokeWidth: 2,
|
|
@@ -114,26 +117,26 @@ var d = n(function({ widget: n, isEditMode: d, onRefresh: f, onExportCSV: p, onE
|
|
|
114
117
|
})
|
|
115
118
|
}),
|
|
116
119
|
action: () => {
|
|
117
|
-
|
|
120
|
+
v(!1), g?.();
|
|
118
121
|
},
|
|
119
|
-
disabled: !
|
|
122
|
+
disabled: !g
|
|
120
123
|
},
|
|
121
124
|
{
|
|
122
125
|
id: "divider1",
|
|
123
126
|
label: "",
|
|
124
|
-
icon: /* @__PURE__ */
|
|
127
|
+
icon: /* @__PURE__ */ u(l, {}),
|
|
125
128
|
action: () => {},
|
|
126
129
|
divider: !0
|
|
127
130
|
},
|
|
128
131
|
{
|
|
129
132
|
id: "copy",
|
|
130
133
|
label: "Copy",
|
|
131
|
-
icon: /* @__PURE__ */
|
|
134
|
+
icon: /* @__PURE__ */ u("svg", {
|
|
132
135
|
fill: "none",
|
|
133
136
|
stroke: "currentColor",
|
|
134
137
|
viewBox: "0 0 24 24",
|
|
135
138
|
className: "w-4 h-4",
|
|
136
|
-
children: /* @__PURE__ */
|
|
139
|
+
children: /* @__PURE__ */ u("path", {
|
|
137
140
|
strokeLinecap: "round",
|
|
138
141
|
strokeLinejoin: "round",
|
|
139
142
|
strokeWidth: 2,
|
|
@@ -141,19 +144,20 @@ var d = n(function({ widget: n, isEditMode: d, onRefresh: f, onExportCSV: p, onE
|
|
|
141
144
|
})
|
|
142
145
|
}),
|
|
143
146
|
action: () => {
|
|
144
|
-
|
|
147
|
+
O(r.id), v(!1);
|
|
145
148
|
},
|
|
146
|
-
disabled: !
|
|
149
|
+
disabled: !p,
|
|
150
|
+
permitted: T
|
|
147
151
|
},
|
|
148
152
|
{
|
|
149
153
|
id: "cut",
|
|
150
154
|
label: "Cut",
|
|
151
|
-
icon: /* @__PURE__ */
|
|
155
|
+
icon: /* @__PURE__ */ u("svg", {
|
|
152
156
|
fill: "none",
|
|
153
157
|
stroke: "currentColor",
|
|
154
158
|
viewBox: "0 0 24 24",
|
|
155
159
|
className: "w-4 h-4",
|
|
156
|
-
children: /* @__PURE__ */
|
|
160
|
+
children: /* @__PURE__ */ u("path", {
|
|
157
161
|
strokeLinecap: "round",
|
|
158
162
|
strokeLinejoin: "round",
|
|
159
163
|
strokeWidth: 2,
|
|
@@ -161,19 +165,20 @@ var d = n(function({ widget: n, isEditMode: d, onRefresh: f, onExportCSV: p, onE
|
|
|
161
165
|
})
|
|
162
166
|
}),
|
|
163
167
|
action: () => {
|
|
164
|
-
|
|
168
|
+
k(r.id), v(!1);
|
|
165
169
|
},
|
|
166
|
-
disabled: !
|
|
170
|
+
disabled: !p,
|
|
171
|
+
permitted: T && w
|
|
167
172
|
},
|
|
168
173
|
{
|
|
169
174
|
id: "duplicate",
|
|
170
175
|
label: "Duplicate",
|
|
171
|
-
icon: /* @__PURE__ */
|
|
176
|
+
icon: /* @__PURE__ */ u("svg", {
|
|
172
177
|
fill: "none",
|
|
173
178
|
stroke: "currentColor",
|
|
174
179
|
viewBox: "0 0 24 24",
|
|
175
180
|
className: "w-4 h-4",
|
|
176
|
-
children: /* @__PURE__ */
|
|
181
|
+
children: /* @__PURE__ */ u("path", {
|
|
177
182
|
strokeLinecap: "round",
|
|
178
183
|
strokeLinejoin: "round",
|
|
179
184
|
strokeWidth: 2,
|
|
@@ -181,33 +186,34 @@ var d = n(function({ widget: n, isEditMode: d, onRefresh: f, onExportCSV: p, onE
|
|
|
181
186
|
})
|
|
182
187
|
}),
|
|
183
188
|
action: async () => {
|
|
184
|
-
|
|
189
|
+
v(!1);
|
|
185
190
|
let e = {
|
|
186
|
-
x: (
|
|
187
|
-
y: (
|
|
188
|
-
width:
|
|
189
|
-
height:
|
|
191
|
+
x: (r.positionX ?? 0) + 1,
|
|
192
|
+
y: (r.positionY ?? 0) + 1,
|
|
193
|
+
width: r.positionWidth ?? 4,
|
|
194
|
+
height: r.positionHeight ?? 4
|
|
190
195
|
};
|
|
191
|
-
await
|
|
196
|
+
await M(r.id, e, `${r.title} (Copy)`);
|
|
192
197
|
},
|
|
193
|
-
disabled: !
|
|
198
|
+
disabled: !p,
|
|
199
|
+
permitted: T
|
|
194
200
|
},
|
|
195
201
|
{
|
|
196
202
|
id: "divider2",
|
|
197
203
|
label: "",
|
|
198
|
-
icon: /* @__PURE__ */
|
|
204
|
+
icon: /* @__PURE__ */ u(l, {}),
|
|
199
205
|
action: () => {},
|
|
200
206
|
divider: !0
|
|
201
207
|
},
|
|
202
208
|
{
|
|
203
209
|
id: "delete",
|
|
204
210
|
label: "Delete",
|
|
205
|
-
icon: /* @__PURE__ */
|
|
211
|
+
icon: /* @__PURE__ */ u("svg", {
|
|
206
212
|
fill: "none",
|
|
207
213
|
stroke: "currentColor",
|
|
208
214
|
viewBox: "0 0 24 24",
|
|
209
215
|
className: "w-4 h-4",
|
|
210
|
-
children: /* @__PURE__ */
|
|
216
|
+
children: /* @__PURE__ */ u("path", {
|
|
211
217
|
strokeLinecap: "round",
|
|
212
218
|
strokeLinejoin: "round",
|
|
213
219
|
strokeWidth: 2,
|
|
@@ -215,63 +221,68 @@ var d = n(function({ widget: n, isEditMode: d, onRefresh: f, onExportCSV: p, onE
|
|
|
215
221
|
})
|
|
216
222
|
}),
|
|
217
223
|
action: async () => {
|
|
218
|
-
window.confirm("Are you sure you want to delete this widget?") ? (
|
|
224
|
+
window.confirm("Are you sure you want to delete this widget?") ? (v(!1), await N(r.id) || A(r.id)) : v(!1);
|
|
219
225
|
},
|
|
220
|
-
disabled: !
|
|
221
|
-
destructive: !0
|
|
226
|
+
disabled: !p,
|
|
227
|
+
destructive: !0,
|
|
228
|
+
permitted: E
|
|
222
229
|
}
|
|
223
|
-
]
|
|
224
|
-
|
|
230
|
+
].filter((e) => e.permitted !== !1), L = I.filter((e, t) => {
|
|
231
|
+
if (!e.divider) return !0;
|
|
232
|
+
let n = I[t - 1], r = I[t + 1];
|
|
233
|
+
return n !== void 0 && r !== void 0 && !n.divider && !r.divider;
|
|
234
|
+
});
|
|
235
|
+
return /* @__PURE__ */ d("div", {
|
|
225
236
|
className: "relative",
|
|
226
|
-
children: [/* @__PURE__ */
|
|
227
|
-
ref:
|
|
237
|
+
children: [/* @__PURE__ */ u("button", {
|
|
238
|
+
ref: S,
|
|
228
239
|
type: "button",
|
|
229
|
-
onClick:
|
|
240
|
+
onClick: F,
|
|
230
241
|
className: `
|
|
231
242
|
p-1.5 rounded-md
|
|
232
243
|
text-text-secondary
|
|
233
244
|
hover:text-text-primary
|
|
234
245
|
hover:bg-bg-sunken
|
|
235
246
|
transition-colors
|
|
236
|
-
${
|
|
247
|
+
${_ ? "bg-bg-sunken" : ""}
|
|
237
248
|
`,
|
|
238
249
|
title: "Widget options",
|
|
239
250
|
"aria-haspopup": "menu",
|
|
240
|
-
"aria-expanded":
|
|
241
|
-
children: /* @__PURE__ */
|
|
251
|
+
"aria-expanded": _,
|
|
252
|
+
children: /* @__PURE__ */ d("svg", {
|
|
242
253
|
className: "w-4 h-4",
|
|
243
254
|
fill: "currentColor",
|
|
244
255
|
viewBox: "0 0 24 24",
|
|
245
256
|
children: [
|
|
246
|
-
/* @__PURE__ */
|
|
257
|
+
/* @__PURE__ */ u("circle", {
|
|
247
258
|
cx: "12",
|
|
248
259
|
cy: "5",
|
|
249
260
|
r: "2"
|
|
250
261
|
}),
|
|
251
|
-
/* @__PURE__ */
|
|
262
|
+
/* @__PURE__ */ u("circle", {
|
|
252
263
|
cx: "12",
|
|
253
264
|
cy: "12",
|
|
254
265
|
r: "2"
|
|
255
266
|
}),
|
|
256
|
-
/* @__PURE__ */
|
|
267
|
+
/* @__PURE__ */ u("circle", {
|
|
257
268
|
cx: "12",
|
|
258
269
|
cy: "19",
|
|
259
270
|
r: "2"
|
|
260
271
|
})
|
|
261
272
|
]
|
|
262
273
|
})
|
|
263
|
-
}),
|
|
264
|
-
ref:
|
|
274
|
+
}), _ && f(/* @__PURE__ */ u("div", {
|
|
275
|
+
ref: x,
|
|
265
276
|
style: {
|
|
266
277
|
position: "fixed",
|
|
267
|
-
top:
|
|
268
|
-
left:
|
|
278
|
+
top: y.top,
|
|
279
|
+
left: y.left,
|
|
269
280
|
zIndex: 99999
|
|
270
281
|
},
|
|
271
282
|
className: "\n min-w-[160px]\n py-1\n bg-bg-surface\n border border-border-default\n rounded-lg shadow-lg\n ",
|
|
272
283
|
role: "menu",
|
|
273
284
|
"aria-orientation": "vertical",
|
|
274
|
-
children:
|
|
285
|
+
children: L.map((e) => e.divider ? /* @__PURE__ */ u("div", { className: "my-1 border-t border-border-default" }, e.id) : /* @__PURE__ */ d("button", {
|
|
275
286
|
type: "button",
|
|
276
287
|
onClick: (t) => {
|
|
277
288
|
t.stopPropagation(), e.action();
|
|
@@ -285,12 +296,12 @@ var d = n(function({ widget: n, isEditMode: d, onRefresh: f, onExportCSV: p, onE
|
|
|
285
296
|
${e.destructive ? "text-status-error-text hover:text-status-error-text" : "text-text-primary"}
|
|
286
297
|
`,
|
|
287
298
|
role: "menuitem",
|
|
288
|
-
children: [e.icon, /* @__PURE__ */
|
|
299
|
+
children: [e.icon, /* @__PURE__ */ u("span", { children: e.label })]
|
|
289
300
|
}, e.id))
|
|
290
301
|
}), document.body)]
|
|
291
302
|
});
|
|
292
303
|
});
|
|
293
304
|
//#endregion
|
|
294
|
-
export {
|
|
305
|
+
export { p as default };
|
|
295
306
|
|
|
296
307
|
//# sourceMappingURL=WidgetMenu.js.map
|