@burdenoff/microfe-bigconsole 2026.717.3 → 2026.717.4

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.
@@ -222,9 +222,9 @@ var E = i(function() {
222
222
  children: /* @__PURE__ */ w("div", {
223
223
  className: "text-center",
224
224
  children: [
225
- /* @__PURE__ */ C(c, { className: "w-12 h-12 text-state-error mx-auto mb-spacing-md" }),
225
+ /* @__PURE__ */ C(c, { className: "w-12 h-12 text-state-error mx-auto mb-4" }),
226
226
  /* @__PURE__ */ C("p", {
227
- className: "text-state-error text-body-sm font-medium mb-spacing-sm",
227
+ className: "text-state-error text-body-sm font-medium mb-2",
228
228
  children: "Failed to load DataSink"
229
229
  }),
230
230
  /* @__PURE__ */ C("p", {
@@ -239,13 +239,13 @@ var E = i(function() {
239
239
  className: "max-w-3xl mx-auto p-card-padding",
240
240
  children: [
241
241
  /* @__PURE__ */ w("div", {
242
- className: "mb-spacing-lg",
242
+ className: "mb-6",
243
243
  children: [
244
244
  /* @__PURE__ */ w(p, {
245
245
  onClick: $,
246
246
  variant: "ghost",
247
247
  size: "sm",
248
- className: "mb-spacing-md",
248
+ className: "mb-4",
249
249
  children: [/* @__PURE__ */ C(l, { className: "w-4 h-4 mr-2" }), "Back to DataSinks"]
250
250
  }),
251
251
  /* @__PURE__ */ C("h1", {
@@ -253,13 +253,13 @@ var E = i(function() {
253
253
  children: D ? "Edit DataSink" : "Create DataSink"
254
254
  }),
255
255
  /* @__PURE__ */ C("p", {
256
- className: "text-body-sm text-text-secondary mt-spacing-xs",
256
+ className: "text-body-sm text-text-secondary mt-1",
257
257
  children: D ? "Update the DataSink configuration." : "Create a new DataSink to store and cache data. Use the unique key to connect workflows."
258
258
  })
259
259
  ]
260
260
  }),
261
261
  I && /* @__PURE__ */ w("div", {
262
- className: "mb-spacing-lg p-4 bg-state-error/10 border border-state-error/30 rounded-lg flex items-start gap-3",
262
+ className: "mb-6 p-4 bg-state-error/10 border border-state-error/30 rounded-lg flex items-start gap-3",
263
263
  children: [/* @__PURE__ */ C(c, { className: "w-5 h-5 text-state-error flex-shrink-0 mt-0.5" }), /* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("p", {
264
264
  className: "text-sm font-medium text-state-error",
265
265
  children: "Failed to save DataSink"
@@ -269,12 +269,12 @@ var E = i(function() {
269
269
  })] })]
270
270
  }),
271
271
  /* @__PURE__ */ C(ue, {
272
- className: "mb-spacing-lg",
272
+ className: "mb-6",
273
273
  children: "A data sink is the named, structured store your dashboards read from. Give it a unique key, configure how long data is cached and how it refreshes, then point a workflow at that key to push data in. Get the basics right here and everything downstream connects to it."
274
274
  }),
275
275
  /* @__PURE__ */ w("form", {
276
276
  onSubmit: we,
277
- className: "space-y-spacing-lg",
277
+ className: "space-y-6",
278
278
  children: [
279
279
  /* @__PURE__ */ w(le, {
280
280
  treatment: "glass",
@@ -298,7 +298,7 @@ var E = i(function() {
298
298
  className: "space-y-5",
299
299
  children: [
300
300
  /* @__PURE__ */ w("div", {
301
- className: "space-y-spacing-sm",
301
+ className: "space-y-2",
302
302
  children: [
303
303
  /* @__PURE__ */ w(b, {
304
304
  htmlFor: "name",
@@ -322,7 +322,7 @@ var E = i(function() {
322
322
  ]
323
323
  }),
324
324
  /* @__PURE__ */ w("div", {
325
- className: "space-y-spacing-sm",
325
+ className: "space-y-2",
326
326
  children: [
327
327
  /* @__PURE__ */ w(b, {
328
328
  htmlFor: "key",
@@ -361,7 +361,7 @@ var E = i(function() {
361
361
  ]
362
362
  }),
363
363
  /* @__PURE__ */ w("div", {
364
- className: "space-y-spacing-sm",
364
+ className: "space-y-2",
365
365
  children: [/* @__PURE__ */ C(b, {
366
366
  htmlFor: "description",
367
367
  children: "Description"
@@ -395,7 +395,7 @@ var E = i(function() {
395
395
  }), /* @__PURE__ */ w(h, {
396
396
  className: "space-y-5",
397
397
  children: [/* @__PURE__ */ w("div", {
398
- className: "space-y-spacing-sm",
398
+ className: "space-y-2",
399
399
  children: [
400
400
  /* @__PURE__ */ C(b, { children: "Cache Duration" }),
401
401
  /* @__PURE__ */ C(r, {
@@ -437,7 +437,7 @@ var E = i(function() {
437
437
  })]
438
438
  })
439
439
  }), /* @__PURE__ */ C(h, { children: /* @__PURE__ */ w("div", {
440
- className: "space-y-spacing-sm",
440
+ className: "space-y-2",
441
441
  children: [
442
442
  /* @__PURE__ */ C(x, {
443
443
  value: O.data,
@@ -496,7 +496,7 @@ var E = i(function() {
496
496
  description: "Connect a workflow to this sink's key to start pushing data."
497
497
  }) })] }),
498
498
  /* @__PURE__ */ w("div", {
499
- className: "flex items-center justify-end gap-spacing-md pt-spacing-md",
499
+ className: "flex items-center justify-end gap-4 pt-4",
500
500
  children: [/* @__PURE__ */ C(p, {
501
501
  type: "button",
502
502
  onClick: $,
@@ -512,7 +512,7 @@ var E = i(function() {
512
512
  ]
513
513
  }),
514
514
  !D && /* @__PURE__ */ w("div", {
515
- className: "mt-spacing-lg p-4 bg-bg-sunken rounded-lg border border-border-default",
515
+ className: "mt-6 p-4 bg-bg-sunken rounded-lg border border-border-default",
516
516
  children: [/* @__PURE__ */ C("h3", {
517
517
  className: "text-sm font-medium text-text-primary mb-2",
518
518
  children: "What's Next?"
@@ -1 +1 @@
1
- {"version":3,"file":"DataSinkBuilderPage.js","names":[],"sources":["../../../src/bigconsole/pages/DataSinkBuilderPage.tsx"],"sourcesContent":["/**\n * DataSink Builder Page\n *\n * Simplified page for creating and editing DataSinks.\n * DataSinks are now standalone - workflows push data to them via unique keys.\n * Required fields: name, key\n */\n\nimport { memo, useState, useMemo, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeCopyText, nativeToast } from '../../utils/nativeBridge';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { CacheTTLSelector } from '../components/datasink/CacheTTLSelector';\nimport { RefreshTypeSelector } from '../components/datasink/RefreshTypeSelector';\nimport type { CreateDataSinkInput, UpdateDataSinkInput, DataSinkRefreshType } from '../types';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Textarea } from '@burdenoff/fe-libs/ui';\nimport { Label } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\n// Lucide icons\nimport { ArrowLeft, Save, Loader2, AlertCircle, Database, Settings, Eye, Copy, Check } from 'lucide-react';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface RefreshConfig {\n type: 'manual' | 'scheduled';\n schedule?: {\n cron: string;\n timezone: string;\n };\n}\n\ninterface FormState {\n name: string;\n key: string;\n description: string;\n ttl: number;\n refresh: RefreshConfig;\n data: string; // JSON string for data payload\n}\n\ninterface FormErrors {\n name?: string;\n key?: string;\n data?: string;\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\n/**\n * Generate a key from name (slug format)\n */\nfunction generateKey(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .substring(0, 64);\n}\n\n/**\n * Validate form data\n */\nfunction validateForm(form: FormState): FormErrors {\n const errors: FormErrors = {};\n\n if (!form.name.trim()) {\n errors.name = 'Name is required';\n } else if (form.name.length < 2) {\n errors.name = 'Name must be at least 2 characters';\n } else if (form.name.length > 100) {\n errors.name = 'Name must be less than 100 characters';\n }\n\n if (!form.key.trim()) {\n errors.key = 'Key is required';\n } else if (!/^[a-z0-9_]+$/.test(form.key)) {\n errors.key = 'Key can only contain lowercase letters, numbers, and underscores';\n } else if (form.key.length > 64) {\n errors.key = 'Key must be less than 64 characters';\n }\n\n // Validate JSON data if provided\n if (form.data && form.data.trim()) {\n try {\n JSON.parse(form.data);\n } catch {\n errors.data = 'Invalid JSON format';\n }\n }\n\n return errors;\n}\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const DataSinkBuilderPage = memo(function DataSinkBuilderPage() {\n const { dataSinkId } = useParams<{ dataSinkId?: string }>();\n const navigate = useAppNavigate();\n\n const isEdit = dataSinkId !== 'new' && !!dataSinkId;\n\n // Form state\n const [form, setForm] = useState<FormState>({\n name: '',\n key: '',\n description: '',\n ttl: 300, // 5 minutes default\n refresh: {\n type: 'manual',\n },\n data: '{}', // Default empty JSON object\n });\n const [errors, setErrors] = useState<FormErrors>({});\n const [keyManuallyEdited, setKeyManuallyEdited] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [keyCopied, setKeyCopied] = useState(false);\n\n // Data preview state\n const [isLoadingPreview, setIsLoadingPreview] = useState(false);\n const [previewData, setPreviewData] = useState<unknown[] | null>(null);\n const [previewError, setPreviewError] = useState<string | null>(null);\n\n // Operations\n const { loading, error, dataSinks, fetchDataSink, createDataSink, updateDataSink, getDataSinkData } =\n useDataSinkOperations({\n skipFetch: true,\n });\n\n // Get existing data sink if editing\n const existingDataSink = isEdit ? dataSinks.find((ds) => ds.id === dataSinkId) : null;\n\n // Load existing data sink for editing\n useEffect(() => {\n if (isEdit && dataSinkId && !existingDataSink) {\n fetchDataSink(dataSinkId);\n }\n }, [isEdit, dataSinkId, existingDataSink, fetchDataSink]);\n\n // Track if form has been initialized\n const [formInitialized, setFormInitialized] = useState(false);\n\n // Populate form with existing data when editing (only once)\n useEffect(() => {\n if (existingDataSink && !formInitialized) {\n // Format existing data as pretty JSON string\n let dataString = '';\n if (existingDataSink.data) {\n try {\n // Check if data is already a string (JSON string from API)\n if (typeof existingDataSink.data === 'string') {\n // Parse and re-stringify to ensure proper formatting\n const parsed = JSON.parse(existingDataSink.data);\n dataString = JSON.stringify(parsed, null, 2);\n } else {\n // It's an object, just stringify it\n dataString = JSON.stringify(existingDataSink.data, null, 2);\n }\n } catch {\n // If parsing fails, use as-is if string, otherwise empty\n dataString = typeof existingDataSink.data === 'string' ? existingDataSink.data : '';\n }\n }\n\n setForm({\n name: existingDataSink.name || '',\n key: existingDataSink.key || '',\n description: existingDataSink.description || '',\n ttl: existingDataSink.ttl || 300,\n refresh: existingDataSink.refresh\n ? {\n type: existingDataSink.refresh.type === 'scheduled' ? 'scheduled' : 'manual',\n schedule: existingDataSink.refresh.schedule\n ? {\n cron: existingDataSink.refresh.schedule.cron,\n timezone: existingDataSink.refresh.schedule.timezone,\n }\n : undefined,\n }\n : { type: 'manual' },\n data: dataString,\n });\n setKeyManuallyEdited(true); // Don't auto-generate key when editing\n setFormInitialized(true); // Mark as initialized\n setErrors({}); // Clear any errors\n }\n }, [existingDataSink, formInitialized]);\n\n // Auto-generate key from name\n const handleNameChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setForm((prev) => ({\n ...prev,\n name,\n key: keyManuallyEdited ? prev.key : generateKey(name),\n }));\n setErrors((prev) => ({ ...prev, name: undefined }));\n },\n [keyManuallyEdited]\n );\n\n // Handle key change\n const handleKeyChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const key = e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '');\n setForm((prev) => ({ ...prev, key }));\n setKeyManuallyEdited(true);\n setErrors((prev) => ({ ...prev, key: undefined }));\n }, []);\n\n // Handle description change\n const handleDescriptionChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setForm((prev) => ({ ...prev, description: e.target.value }));\n }, []);\n\n // Handle TTL change\n const handleTTLChange = useCallback((ttl: number | null) => {\n setForm((prev) => ({ ...prev, ttl: ttl || 0 }));\n }, []);\n\n // Handle refresh config change\n const handleRefreshChange = useCallback((config: RefreshConfig) => {\n setForm((prev) => ({ ...prev, refresh: config }));\n }, []);\n\n // Handle data change\n const handleDataChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setForm((prev) => ({ ...prev, data: e.target.value }));\n setErrors((prev) => ({ ...prev, data: undefined }));\n }, []);\n\n // Format JSON data\n const handleFormatData = useCallback(() => {\n try {\n const parsed = JSON.parse(form.data);\n setForm((prev) => ({ ...prev, data: JSON.stringify(parsed, null, 2) }));\n setErrors((prev) => ({ ...prev, data: undefined }));\n } catch {\n setErrors((prev) => ({ ...prev, data: 'Invalid JSON format' }));\n }\n }, [form.data]);\n\n // Copy key to clipboard\n const handleCopyKey = useCallback(async () => {\n if (form.key) {\n await nativeCopyText(form.key);\n setKeyCopied(true);\n if (isNative()) void nativeToast('Key copied');\n setTimeout(() => setKeyCopied(false), 2000);\n }\n }, [form.key]);\n\n // Handle back navigation\n const handleBack = useCallback(() => {\n navigate('/datasinks');\n }, [navigate]);\n\n // Load data preview\n const handleShowData = useCallback(async () => {\n if (!isEdit || !existingDataSink) return;\n\n setIsLoadingPreview(true);\n setPreviewError(null);\n\n try {\n const result = await getDataSinkData({ key: existingDataSink.key });\n if (result?.raw) {\n // Get first 20 items if it's an array, otherwise wrap in array\n const data = Array.isArray(result.raw) ? result.raw.slice(0, 20) : [result.raw];\n setPreviewData(data);\n } else {\n setPreviewData([]);\n }\n } catch (err) {\n setPreviewError(err instanceof Error ? err.message : 'Failed to load data');\n } finally {\n setIsLoadingPreview(false);\n }\n }, [isEdit, existingDataSink, getDataSinkData]);\n\n // Handle form submission\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n setSubmitError(null);\n\n // Validate form\n const validationErrors = validateForm(form);\n if (Object.keys(validationErrors).length > 0) {\n setErrors(validationErrors);\n return;\n }\n\n setIsSaving(true);\n try {\n // Parse data JSON\n let parsedData: Record<string, unknown> = {};\n if (form.data && form.data.trim()) {\n try {\n parsedData = JSON.parse(form.data) as Record<string, unknown>;\n } catch {\n setErrors((prev) => ({ ...prev, data: 'Invalid JSON format' }));\n setIsSaving(false);\n return;\n }\n }\n\n if (isEdit && existingDataSink) {\n // Update existing\n const input: UpdateDataSinkInput = {\n id: existingDataSink.id,\n version: existingDataSink.version,\n name: form.name,\n description: form.description || undefined,\n ttl: form.ttl,\n refresh: {\n type: form.refresh.type as DataSinkRefreshType,\n schedule: form.refresh.schedule,\n },\n data: parsedData, // Include data in update\n };\n const result = await updateDataSink(input);\n if (!result) {\n throw new Error('Failed to update DataSink');\n }\n } else {\n // Create new - simplified flow (standalone DataSinks)\n const input: CreateDataSinkInput = {\n key: form.key,\n name: form.name,\n description: form.description || undefined,\n ttl: form.ttl,\n refresh: {\n type: form.refresh.type as DataSinkRefreshType,\n schedule: form.refresh.schedule,\n },\n data: parsedData, // Include data in create\n };\n const result = await createDataSink(input);\n if (!result) {\n throw new Error('Failed to create DataSink');\n }\n }\n navigate('/datasinks');\n } catch (err) {\n setSubmitError(err instanceof Error ? err.message : 'Failed to save DataSink');\n } finally {\n setIsSaving(false);\n }\n },\n [form, isEdit, existingDataSink, createDataSink, updateDataSink, navigate]\n );\n\n // Check if form is valid\n const isFormValid = useMemo(() => {\n const nameValid = form.name.trim().length >= 2;\n const keyValid = form.key.trim().length > 0;\n\n // Check JSON validity if data is provided\n let dataValid = true;\n if (form.data && form.data.trim()) {\n try {\n JSON.parse(form.data);\n } catch {\n dataValid = false;\n }\n }\n\n return nameValid && keyValid && dataValid;\n }, [form.name, form.key, form.data]);\n\n // Loading state\n if (loading && isEdit) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <Loader2 className=\"w-8 h-8 text-text-secondary animate-spin\" />\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-card-padding\">\n <div className=\"text-center\">\n <AlertCircle className=\"w-12 h-12 text-state-error mx-auto mb-spacing-md\" />\n <p className=\"text-state-error text-body-sm font-medium mb-spacing-sm\">Failed to load DataSink</p>\n <p className=\"text-text-tertiary text-body-xs\">{error.message}</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"min-h-screen bg-bg-canvas\">\n <div className=\"max-w-3xl mx-auto p-card-padding\">\n {/* Header */}\n <div className=\"mb-spacing-lg\">\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\" className=\"mb-spacing-md\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Back to DataSinks\n </Button>\n <h1 className=\"text-heading-h1 font-bold text-text-primary\">\n {isEdit ? 'Edit DataSink' : 'Create DataSink'}\n </h1>\n <p className=\"text-body-sm text-text-secondary mt-spacing-xs\">\n {isEdit\n ? 'Update the DataSink configuration.'\n : 'Create a new DataSink to store and cache data. Use the unique key to connect workflows.'}\n </p>\n </div>\n\n {/* Error Alert */}\n {submitError && (\n <div className=\"mb-spacing-lg p-4 bg-state-error/10 border border-state-error/30 rounded-lg flex items-start gap-3\">\n <AlertCircle className=\"w-5 h-5 text-state-error flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-state-error\">Failed to save DataSink</p>\n <p className=\"text-xs text-state-error/80 mt-1\">{submitError}</p>\n </div>\n </div>\n )}\n\n <PagePurpose className=\"mb-spacing-lg\">\n A data sink is the named, structured store your dashboards read from. Give it a unique key, configure how long\n data is cached and how it refreshes, then point a workflow at that key to push data in. Get the basics right\n here and everything downstream connects to it.\n </PagePurpose>\n\n {/* Form */}\n <form onSubmit={handleSubmit} className=\"space-y-spacing-lg\">\n {/* Basic Information — primary emphasis zone */}\n <GlassCard treatment=\"glass\" glow>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Database className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Basic Information</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Essential details for your DataSink</p>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {/* Name field */}\n <div className=\"space-y-spacing-sm\">\n <Label htmlFor=\"name\">\n Name <span className=\"text-state-error\">*</span>\n </Label>\n <Input\n id=\"name\"\n type=\"text\"\n value={form.name}\n onChange={handleNameChange}\n placeholder=\"e.g., Sales Metrics\"\n className={errors.name ? 'border-state-error' : ''}\n />\n {errors.name && <p className=\"text-body-xs text-state-error\">{errors.name}</p>}\n </div>\n\n {/* Key field */}\n <div className=\"space-y-spacing-sm\">\n <Label htmlFor=\"key\">\n Unique Key <span className=\"text-state-error\">*</span>\n </Label>\n <div className=\"flex gap-2\">\n <Input\n id=\"key\"\n type=\"text\"\n value={form.key}\n onChange={handleKeyChange}\n placeholder=\"sales_metrics\"\n disabled={isEdit}\n className={`font-mono flex-1 ${errors.key ? 'border-state-error' : ''}`}\n />\n {form.key && (\n <Button type=\"button\" onClick={handleCopyKey} variant=\"outline\" size=\"sm\" className=\"px-3\">\n {keyCopied ? <Check className=\"w-4 h-4 text-state-success\" /> : <Copy className=\"w-4 h-4\" />}\n </Button>\n )}\n </div>\n {errors.key && <p className=\"text-body-xs text-state-error\">{errors.key}</p>}\n <p className=\"text-body-xs text-text-tertiary\">\n {isEdit\n ? 'Key cannot be changed. Use this key in your workflows to push data.'\n : 'Auto-generated from name. This key will be used by workflows to push data.'}\n </p>\n </div>\n\n {/* Description field */}\n <div className=\"space-y-spacing-sm\">\n <Label htmlFor=\"description\">Description</Label>\n <Textarea\n id=\"description\"\n value={form.description}\n onChange={handleDescriptionChange}\n placeholder=\"Describe what data this sink stores...\"\n rows={2}\n className=\"resize-none\"\n />\n </div>\n </CardContent>\n </GlassCard>\n\n {/* Configuration */}\n <Card>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Settings className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Configuration</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Cache and refresh settings</p>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {/* Cache TTL */}\n <div className=\"space-y-spacing-sm\">\n <Label>Cache Duration</Label>\n <CacheTTLSelector value={form.ttl} onChange={handleTTLChange} />\n <p className=\"text-body-xs text-text-tertiary\">\n How long cached data remains valid before requiring refresh.\n </p>\n </div>\n\n {/* Refresh Type */}\n <RefreshTypeSelector value={form.refresh} onChange={handleRefreshChange} />\n </CardContent>\n </Card>\n\n {/* Data Payload */}\n <Card>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Database className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Data Payload</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {isEdit ? 'Edit the JSON data stored in this DataSink' : 'Optional initial data (JSON format)'}\n </p>\n </div>\n </div>\n <Button type=\"button\" onClick={handleFormatData} variant=\"outline\" size=\"sm\">\n Format JSON\n </Button>\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-spacing-sm\">\n <Textarea\n value={form.data}\n onChange={handleDataChange}\n placeholder='{\"key\": \"value\"}'\n rows={10}\n className={`font-mono text-xs resize-y ${errors.data ? 'border-state-error' : ''}`}\n />\n {errors.data && <p className=\"text-body-xs text-state-error\">{errors.data}</p>}\n <p className=\"text-body-xs text-text-tertiary\">\n Enter valid JSON data. This data will be stored and can be accessed by widgets via parsers.\n </p>\n </div>\n </CardContent>\n </Card>\n\n {/* Data Preview - Only show for existing DataSinks */}\n {isEdit && existingDataSink && (\n <Card>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Eye className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Data Preview</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Preview the first 20 records</p>\n </div>\n </div>\n <Button\n type=\"button\"\n onClick={handleShowData}\n variant=\"outline\"\n size=\"sm\"\n disabled={isLoadingPreview}\n >\n {isLoadingPreview ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n Loading...\n </>\n ) : (\n <>\n <Eye className=\"w-4 h-4 mr-2\" />\n Show Data\n </>\n )}\n </Button>\n </div>\n </CardHeader>\n {(previewData || previewError) && (\n <CardContent>\n {previewError ? (\n <div className=\"p-4 bg-state-error/10 border border-state-error/30 rounded-md\">\n <p className=\"text-sm text-state-error\">{previewError}</p>\n </div>\n ) : previewData && previewData.length > 0 ? (\n <pre className=\"p-4 bg-bg-sunken rounded-md overflow-auto max-h-[300px] text-xs font-mono\">\n {JSON.stringify(previewData, null, 2)}\n </pre>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No data available yet\"\n description=\"Connect a workflow to this sink's key to start pushing data.\"\n />\n )}\n </CardContent>\n )}\n </Card>\n )}\n\n {/* Submit */}\n <div className=\"flex items-center justify-end gap-spacing-md pt-spacing-md\">\n <Button type=\"button\" onClick={handleBack} variant=\"secondary\">\n Cancel\n </Button>\n <Button type=\"submit\" disabled={!isFormValid || isSaving} variant=\"default\">\n {isSaving ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n Saving...\n </>\n ) : (\n <>\n <Save className=\"w-4 h-4 mr-2\" />\n {isEdit ? 'Save Changes' : 'Create DataSink'}\n </>\n )}\n </Button>\n </div>\n </form>\n\n {/* Info Box for New DataSinks */}\n {!isEdit && (\n <div className=\"mt-spacing-lg p-4 bg-bg-sunken rounded-lg border border-border-default\">\n <h3 className=\"text-sm font-medium text-text-primary mb-2\">What&apos;s Next?</h3>\n <ol className=\"list-decimal list-inside text-xs text-text-secondary space-y-1\">\n <li>Create this DataSink to get your unique key</li>\n <li>\n Go to <strong>Workflows</strong> and create a FluidGrids workflow\n </li>\n <li>Use the unique key in your workflow to push data to this DataSink</li>\n <li>\n Create <strong>Parsers</strong> to transform the data using JQ or JSONata\n </li>\n </ol>\n </div>\n )}\n </div>\n </div>\n );\n});\n\nexport default DataSinkBuilderPage;\n"],"mappings":";;;;;;;;;;;;AA8DA,SAAS,GAAY,GAAsB;AACzC,QAAO,EACJ,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,YAAY,GAAG,CACvB,UAAU,GAAG,GAAG;;AAMrB,SAAS,EAAa,GAA6B;CACjD,IAAM,IAAqB,EAAE;AAmB7B,KAjBK,EAAK,KAAK,MAAM,GAEV,EAAK,KAAK,SAAS,IAC5B,EAAO,OAAO,uCACL,EAAK,KAAK,SAAS,QAC5B,EAAO,OAAO,2CAJd,EAAO,OAAO,oBAOX,EAAK,IAAI,MAAM,GAER,eAAe,KAAK,EAAK,IAAI,GAE9B,EAAK,IAAI,SAAS,OAC3B,EAAO,MAAM,yCAFb,EAAO,MAAM,qEAFb,EAAO,MAAM,mBAQX,EAAK,QAAQ,EAAK,KAAK,MAAM,CAC/B,KAAI;AACF,OAAK,MAAM,EAAK,KAAK;SACf;AACN,IAAO,OAAO;;AAIlB,QAAO;;AAOT,IAAa,IAAsB,EAAK,WAA+B;CACrE,IAAM,EAAE,kBAAe,IAAoC,EACrD,IAAW,GAAgB,EAE3B,IAAS,MAAe,SAAS,CAAC,CAAC,GAGnC,CAAC,GAAM,KAAW,EAAoB;EAC1C,MAAM;EACN,KAAK;EACL,aAAa;EACb,KAAK;EACL,SAAS,EACP,MAAM,UACP;EACD,MAAM;EACP,CAAC,EACI,CAAC,GAAQ,KAAa,EAAqB,EAAE,CAAC,EAC9C,CAAC,GAAmB,KAAwB,EAAS,GAAM,EAC3D,CAAC,GAAU,KAAe,EAAS,GAAM,EACzC,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,GAAW,KAAgB,EAAS,GAAM,EAG3C,CAAC,GAAkB,KAAuB,EAAS,GAAM,EACzD,CAAC,GAAa,KAAkB,EAA2B,KAAK,EAChE,CAAC,GAAc,KAAmB,EAAwB,KAAK,EAG/D,EAAE,aAAS,UAAO,eAAW,kBAAe,mBAAgB,mBAAgB,uBAChF,EAAsB,EACpB,WAAW,IACZ,CAAC,EAGE,IAAmB,IAAS,GAAU,MAAM,MAAO,EAAG,OAAO,EAAW,GAAG;AAGjF,SAAgB;AACd,EAAI,KAAU,KAAc,CAAC,KAC3B,EAAc,EAAW;IAE1B;EAAC;EAAQ;EAAY;EAAkB;EAAc,CAAC;CAGzD,IAAM,CAAC,GAAiB,MAAsB,EAAS,GAAM;AAG7D,SAAgB;AACd,MAAI,KAAoB,CAAC,GAAiB;GAExC,IAAI,IAAa;AACjB,OAAI,EAAiB,KACnB,KAAI;AAEF,QAAI,OAAO,EAAiB,QAAS,UAAU;KAE7C,IAAM,IAAS,KAAK,MAAM,EAAiB,KAAK;AAChD,SAAa,KAAK,UAAU,GAAQ,MAAM,EAAE;UAG5C,KAAa,KAAK,UAAU,EAAiB,MAAM,MAAM,EAAE;WAEvD;AAEN,QAAa,OAAO,EAAiB,QAAS,WAAW,EAAiB,OAAO;;AAwBrF,GApBA,EAAQ;IACN,MAAM,EAAiB,QAAQ;IAC/B,KAAK,EAAiB,OAAO;IAC7B,aAAa,EAAiB,eAAe;IAC7C,KAAK,EAAiB,OAAO;IAC7B,SAAS,EAAiB,UACtB;KACE,MAAM,EAAiB,QAAQ,SAAS,cAAc,cAAc;KACpE,UAAU,EAAiB,QAAQ,WAC/B;MACE,MAAM,EAAiB,QAAQ,SAAS;MACxC,UAAU,EAAiB,QAAQ,SAAS;MAC7C,GACD,KAAA;KACL,GACD,EAAE,MAAM,UAAU;IACtB,MAAM;IACP,CAAC,EACF,EAAqB,GAAK,EAC1B,GAAmB,GAAK,EACxB,EAAU,EAAE,CAAC;;IAEd,CAAC,GAAkB,EAAgB,CAAC;CAGvC,IAAM,KAAmB,GACtB,MAA2C;EAC1C,IAAM,IAAO,EAAE,OAAO;AAMtB,EALA,GAAS,OAAU;GACjB,GAAG;GACH;GACA,KAAK,IAAoB,EAAK,MAAM,GAAY,EAAK;GACtD,EAAE,EACH,GAAW,OAAU;GAAE,GAAG;GAAM,MAAM,KAAA;GAAW,EAAE;IAErD,CAAC,EAAkB,CACpB,EAGK,KAAkB,GAAa,MAA2C;EAC9E,IAAM,IAAM,EAAE,OAAO,MAAM,aAAa,CAAC,QAAQ,eAAe,GAAG;AAGnE,EAFA,GAAS,OAAU;GAAE,GAAG;GAAM;GAAK,EAAE,EACrC,EAAqB,GAAK,EAC1B,GAAW,OAAU;GAAE,GAAG;GAAM,KAAK,KAAA;GAAW,EAAE;IACjD,EAAE,CAAC,EAGA,KAA0B,GAAa,MAA8C;AACzF,KAAS,OAAU;GAAE,GAAG;GAAM,aAAa,EAAE,OAAO;GAAO,EAAE;IAC5D,EAAE,CAAC,EAGA,KAAkB,GAAa,MAAuB;AAC1D,KAAS,OAAU;GAAE,GAAG;GAAM,KAAK,KAAO;GAAG,EAAE;IAC9C,EAAE,CAAC,EAGA,KAAsB,GAAa,MAA0B;AACjE,KAAS,OAAU;GAAE,GAAG;GAAM,SAAS;GAAQ,EAAE;IAChD,EAAE,CAAC,EAGA,KAAmB,GAAa,MAA8C;AAElF,EADA,GAAS,OAAU;GAAE,GAAG;GAAM,MAAM,EAAE,OAAO;GAAO,EAAE,EACtD,GAAW,OAAU;GAAE,GAAG;GAAM,MAAM,KAAA;GAAW,EAAE;IAClD,EAAE,CAAC,EAGA,KAAmB,QAAkB;AACzC,MAAI;GACF,IAAM,IAAS,KAAK,MAAM,EAAK,KAAK;AAEpC,GADA,GAAS,OAAU;IAAE,GAAG;IAAM,MAAM,KAAK,UAAU,GAAQ,MAAM,EAAE;IAAE,EAAE,EACvE,GAAW,OAAU;IAAE,GAAG;IAAM,MAAM,KAAA;IAAW,EAAE;UAC7C;AACN,MAAW,OAAU;IAAE,GAAG;IAAM,MAAM;IAAuB,EAAE;;IAEhE,CAAC,EAAK,KAAK,CAAC,EAGT,KAAgB,EAAY,YAAY;AAC5C,EAAI,EAAK,QACP,MAAM,GAAe,EAAK,IAAI,EAC9B,EAAa,GAAK,EACd,GAAU,IAAO,GAAY,aAAa,EAC9C,iBAAiB,EAAa,GAAM,EAAE,IAAK;IAE5C,CAAC,EAAK,IAAI,CAAC,EAGR,IAAa,QAAkB;AACnC,IAAS,aAAa;IACrB,CAAC,EAAS,CAAC,EAGR,KAAiB,EAAY,YAAY;AACzC,SAAC,KAAU,CAAC,IAGhB;GADA,EAAoB,GAAK,EACzB,EAAgB,KAAK;AAErB,OAAI;IACF,IAAM,IAAS,MAAM,EAAgB,EAAE,KAAK,EAAiB,KAAK,CAAC;AACnE,IAAI,GAAQ,MAGV,EADa,MAAM,QAAQ,EAAO,IAAI,GAAG,EAAO,IAAI,MAAM,GAAG,GAAG,GAAG,CAAC,EAAO,IAAI,CAC3D,GAEpB,EAAe,EAAE,CAAC;YAEb,GAAK;AACZ,MAAgB,aAAe,QAAQ,EAAI,UAAU,sBAAsB;aACnE;AACR,MAAoB,GAAM;;;IAE3B;EAAC;EAAQ;EAAkB;EAAgB,CAAC,EAGzC,KAAe,EACnB,OAAO,MAAuB;AAE5B,EADA,EAAE,gBAAgB,EAClB,EAAe,KAAK;EAGpB,IAAM,IAAmB,EAAa,EAAK;AAC3C,MAAI,OAAO,KAAK,EAAiB,CAAC,SAAS,GAAG;AAC5C,KAAU,EAAiB;AAC3B;;AAGF,IAAY,GAAK;AACjB,MAAI;GAEF,IAAI,IAAsC,EAAE;AAC5C,OAAI,EAAK,QAAQ,EAAK,KAAK,MAAM,CAC/B,KAAI;AACF,QAAa,KAAK,MAAM,EAAK,KAAK;WAC5B;AAEN,IADA,GAAW,OAAU;KAAE,GAAG;KAAM,MAAM;KAAuB,EAAE,EAC/D,EAAY,GAAM;AAClB;;AAIJ,OAAI,KAAU;QAeR,CADW,MAAM,EAZc;KACjC,IAAI,EAAiB;KACrB,SAAS,EAAiB;KAC1B,MAAM,EAAK;KACX,aAAa,EAAK,eAAe,KAAA;KACjC,KAAK,EAAK;KACV,SAAS;MACP,MAAM,EAAK,QAAQ;MACnB,UAAU,EAAK,QAAQ;MACxB;KACD,MAAM;KACP,CACyC,CAExC,OAAU,MAAM,4BAA4B;cAgB1C,CADW,MAAM,EAXc;IACjC,KAAK,EAAK;IACV,MAAM,EAAK;IACX,aAAa,EAAK,eAAe,KAAA;IACjC,KAAK,EAAK;IACV,SAAS;KACP,MAAM,EAAK,QAAQ;KACnB,UAAU,EAAK,QAAQ;KACxB;IACD,MAAM;IACP,CACyC,CAExC,OAAU,MAAM,4BAA4B;AAGhD,KAAS,aAAa;WACf,GAAK;AACZ,KAAe,aAAe,QAAQ,EAAI,UAAU,0BAA0B;YACtE;AACR,KAAY,GAAM;;IAGtB;EAAC;EAAM;EAAQ;EAAkB;EAAgB;EAAgB;EAAS,CAC3E,EAGK,KAAc,SAAc;EAChC,IAAM,IAAY,EAAK,KAAK,MAAM,CAAC,UAAU,GACvC,IAAW,EAAK,IAAI,MAAM,CAAC,SAAS,GAGtC,IAAY;AAChB,MAAI,EAAK,QAAQ,EAAK,KAAK,MAAM,CAC/B,KAAI;AACF,QAAK,MAAM,EAAK,KAAK;UACf;AACN,OAAY;;AAIhB,SAAO,KAAa,KAAY;IAC/B;EAAC,EAAK;EAAM,EAAK;EAAK,EAAK;EAAK,CAAC;AAwBpC,QArBI,MAAW,IAEX,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,4CAA6C,CAAA;EAC5D,CAAA,GAKN,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD,EAAa,WAAU,oDAAqD,CAAA;IAC5E,kBAAC,KAAD;KAAG,WAAU;eAA0D;KAA2B,CAAA;IAClG,kBAAC,KAAD;KAAG,WAAU;eAAmC,EAAM;KAAY,CAAA;IAC9D;;EACF,CAAA,GAKR,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAQ,SAAS;OAAY,SAAQ;OAAQ,MAAK;OAAK,WAAU;iBAAjE,CACE,kBAAC,GAAD,EAAW,WAAU,gBAAiB,CAAA,EAAA,oBAE/B;;MACT,kBAAC,MAAD;OAAI,WAAU;iBACX,IAAS,kBAAkB;OACzB,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;iBACV,IACG,uCACA;OACF,CAAA;MACA;;IAGL,KACC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAa,WAAU,iDAAkD,CAAA,EACzE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAuC;MAA2B,CAAA,EAC/E,kBAAC,KAAD;MAAG,WAAU;gBAAoC;MAAgB,CAAA,CAC7D,EAAA,CAAA,CACF;;IAGR,kBAAC,IAAD;KAAa,WAAU;eAAgB;KAIzB,CAAA;IAGd,kBAAC,QAAD;KAAM,UAAU;KAAc,WAAU;eAAxC;MAEE,kBAAC,IAAD;OAAW,WAAU;OAAQ,MAAA;iBAA7B,CACE,kBAAC,GAAD;QAAY,WAAU;kBACpB,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,GAAD,EAAU,WAAU,kCAAmC,CAAA;UACnD,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,GAAD;UAAW,WAAU;oBAAY;UAA6B,CAAA,EAC9D,kBAAC,KAAD;UAAG,WAAU;oBAAqC;UAAuC,CAAA,CACrF,EAAA,CAAA,CACF;;QACK,CAAA,EACb,kBAAC,GAAD;QAAa,WAAU;kBAAvB;SAEE,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,GAAD;YAAO,SAAQ;sBAAf,CAAsB,SACf,kBAAC,QAAD;aAAM,WAAU;uBAAmB;aAAQ,CAAA,CAC1C;;WACR,kBAAC,GAAD;YACE,IAAG;YACH,MAAK;YACL,OAAO,EAAK;YACZ,UAAU;YACV,aAAY;YACZ,WAAW,EAAO,OAAO,uBAAuB;YAChD,CAAA;WACD,EAAO,QAAQ,kBAAC,KAAD;YAAG,WAAU;sBAAiC,EAAO;YAAS,CAAA;WAC1E;;SAGN,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,GAAD;YAAO,SAAQ;sBAAf,CAAqB,eACR,kBAAC,QAAD;aAAM,WAAU;uBAAmB;aAAQ,CAAA,CAChD;;WACR,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,GAAD;aACE,IAAG;aACH,MAAK;aACL,OAAO,EAAK;aACZ,UAAU;aACV,aAAY;aACZ,UAAU;aACV,WAAW,oBAAoB,EAAO,MAAM,uBAAuB;aACnE,CAAA,EACD,EAAK,OACJ,kBAAC,GAAD;aAAQ,MAAK;aAAS,SAAS;aAAe,SAAQ;aAAU,MAAK;aAAK,WAAU;uBACjF,IAAY,kBAAC,IAAD,EAAO,WAAU,8BAA+B,CAAA,GAAG,kBAAC,IAAD,EAAM,WAAU,WAAY,CAAA;aACrF,CAAA,CAEP;;WACL,EAAO,OAAO,kBAAC,KAAD;YAAG,WAAU;sBAAiC,EAAO;YAAQ,CAAA;WAC5E,kBAAC,KAAD;YAAG,WAAU;sBACV,IACG,wEACA;YACF,CAAA;WACA;;SAGN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD;WAAO,SAAQ;qBAAc;WAAmB,CAAA,EAChD,kBAAC,GAAD;WACE,IAAG;WACH,OAAO,EAAK;WACZ,UAAU;WACV,aAAY;WACZ,MAAM;WACN,WAAU;WACV,CAAA,CACE;;SACM;UACJ;;MAGZ,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;OAAY,WAAU;iBACpB,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,IAAD,EAAU,WAAU,kCAAmC,CAAA;SACnD,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,GAAD;SAAW,WAAU;mBAAY;SAAyB,CAAA,EAC1D,kBAAC,KAAD;SAAG,WAAU;mBAAqC;SAA8B,CAAA,CAC5E,EAAA,CAAA,CACF;;OACK,CAAA,EACb,kBAAC,GAAD;OAAa,WAAU;iBAAvB,CAEE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD,EAAA,UAAO,kBAAsB,CAAA;SAC7B,kBAAC,GAAD;UAAkB,OAAO,EAAK;UAAK,UAAU;UAAmB,CAAA;SAChE,kBAAC,KAAD;UAAG,WAAU;oBAAkC;UAE3C,CAAA;SACA;WAGN,kBAAC,IAAD;QAAqB,OAAO,EAAK;QAAS,UAAU;QAAuB,CAAA,CAC/D;SACT,EAAA,CAAA;MAGP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;OAAY,WAAU;iBACpB,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,GAAD,EAAU,WAAU,kCAAmC,CAAA;UACnD,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,GAAD;UAAW,WAAU;oBAAY;UAAwB,CAAA,EACzD,kBAAC,KAAD;UAAG,WAAU;oBACV,IAAS,+CAA+C;UACvD,CAAA,CACA,EAAA,CAAA,CACF;YACN,kBAAC,GAAD;SAAQ,MAAK;SAAS,SAAS;SAAkB,SAAQ;SAAU,MAAK;mBAAK;SAEpE,CAAA,CACL;;OACK,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD;SACE,OAAO,EAAK;SACZ,UAAU;SACV,aAAY;SACZ,MAAM;SACN,WAAW,8BAA8B,EAAO,OAAO,uBAAuB;SAC9E,CAAA;QACD,EAAO,QAAQ,kBAAC,KAAD;SAAG,WAAU;mBAAiC,EAAO;SAAS,CAAA;QAC9E,kBAAC,KAAD;SAAG,WAAU;mBAAkC;SAE3C,CAAA;QACA;UACM,CAAA,CACT,EAAA,CAAA;MAGN,KAAU,KACT,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;OAAY,WAAU;iBACpB,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,GAAD,EAAK,WAAU,kCAAmC,CAAA;UAC9C,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,GAAD;UAAW,WAAU;oBAAY;UAAwB,CAAA,EACzD,kBAAC,KAAD;UAAG,WAAU;oBAAqC;UAAgC,CAAA,CAC9E,EAAA,CAAA,CACF;YACN,kBAAC,GAAD;SACE,MAAK;SACL,SAAS;SACT,SAAQ;SACR,MAAK;SACL,UAAU;mBAET,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,EAAA,aAEhD,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAK,WAAU,gBAAiB,CAAA,EAAA,YAE/B,EAAA,CAAA;SAEE,CAAA,CACL;;OACK,CAAA,GACX,KAAe,MACf,kBAAC,GAAD,EAAA,UACG,IACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,KAAD;QAAG,WAAU;kBAA4B;QAAiB,CAAA;OACtD,CAAA,GACJ,KAAe,EAAY,SAAS,IACtC,kBAAC,OAAD;OAAK,WAAU;iBACZ,KAAK,UAAU,GAAa,MAAM,EAAE;OACjC,CAAA,GAEN,kBAAC,GAAD;OACE,cAAa;OACb,OAAM;OACN,aAAY;OACZ,CAAA,EAEQ,CAAA,CAEX,EAAA,CAAA;MAIT,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAQ,MAAK;QAAS,SAAS;QAAY,SAAQ;kBAAY;QAEtD,CAAA,EACT,kBAAC,GAAD;QAAQ,MAAK;QAAS,UAAU,CAAC,MAAe;QAAU,SAAQ;kBAC/D,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAS,WAAU,6BAA8B,CAAA,EAAA,YAEhD,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,IAAD,EAAM,WAAU,gBAAiB,CAAA,EAChC,IAAS,iBAAiB,kBAC1B,EAAA,CAAA;QAEE,CAAA,CACL;;MACD;;IAGN,CAAC,KACA,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA6C;MAAsB,CAAA,EACjF,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACE,kBAAC,MAAD,EAAA,UAAI,+CAAgD,CAAA;OACpD,kBAAC,MAAD,EAAA,UAAA;QAAI;QACI,kBAAC,UAAD,EAAA,UAAQ,aAAkB,CAAA;;QAC7B,EAAA,CAAA;OACL,kBAAC,MAAD,EAAA,UAAI,qEAAsE,CAAA;OAC1E,kBAAC,MAAD,EAAA,UAAA;QAAI;QACK,kBAAC,UAAD,EAAA,UAAQ,WAAgB,CAAA;;QAC5B,EAAA,CAAA;OACF;QACD;;IAEJ;;EACF,CAAA;EAER"}
1
+ {"version":3,"file":"DataSinkBuilderPage.js","names":[],"sources":["../../../src/bigconsole/pages/DataSinkBuilderPage.tsx"],"sourcesContent":["/**\n * DataSink Builder Page\n *\n * Simplified page for creating and editing DataSinks.\n * DataSinks are now standalone - workflows push data to them via unique keys.\n * Required fields: name, key\n */\n\nimport { memo, useState, useMemo, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeCopyText, nativeToast } from '../../utils/nativeBridge';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { CacheTTLSelector } from '../components/datasink/CacheTTLSelector';\nimport { RefreshTypeSelector } from '../components/datasink/RefreshTypeSelector';\nimport type { CreateDataSinkInput, UpdateDataSinkInput, DataSinkRefreshType } from '../types';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Textarea } from '@burdenoff/fe-libs/ui';\nimport { Label } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\n// Lucide icons\nimport { ArrowLeft, Save, Loader2, AlertCircle, Database, Settings, Eye, Copy, Check } from 'lucide-react';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface RefreshConfig {\n type: 'manual' | 'scheduled';\n schedule?: {\n cron: string;\n timezone: string;\n };\n}\n\ninterface FormState {\n name: string;\n key: string;\n description: string;\n ttl: number;\n refresh: RefreshConfig;\n data: string; // JSON string for data payload\n}\n\ninterface FormErrors {\n name?: string;\n key?: string;\n data?: string;\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\n/**\n * Generate a key from name (slug format)\n */\nfunction generateKey(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .substring(0, 64);\n}\n\n/**\n * Validate form data\n */\nfunction validateForm(form: FormState): FormErrors {\n const errors: FormErrors = {};\n\n if (!form.name.trim()) {\n errors.name = 'Name is required';\n } else if (form.name.length < 2) {\n errors.name = 'Name must be at least 2 characters';\n } else if (form.name.length > 100) {\n errors.name = 'Name must be less than 100 characters';\n }\n\n if (!form.key.trim()) {\n errors.key = 'Key is required';\n } else if (!/^[a-z0-9_]+$/.test(form.key)) {\n errors.key = 'Key can only contain lowercase letters, numbers, and underscores';\n } else if (form.key.length > 64) {\n errors.key = 'Key must be less than 64 characters';\n }\n\n // Validate JSON data if provided\n if (form.data && form.data.trim()) {\n try {\n JSON.parse(form.data);\n } catch {\n errors.data = 'Invalid JSON format';\n }\n }\n\n return errors;\n}\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const DataSinkBuilderPage = memo(function DataSinkBuilderPage() {\n const { dataSinkId } = useParams<{ dataSinkId?: string }>();\n const navigate = useAppNavigate();\n\n const isEdit = dataSinkId !== 'new' && !!dataSinkId;\n\n // Form state\n const [form, setForm] = useState<FormState>({\n name: '',\n key: '',\n description: '',\n ttl: 300, // 5 minutes default\n refresh: {\n type: 'manual',\n },\n data: '{}', // Default empty JSON object\n });\n const [errors, setErrors] = useState<FormErrors>({});\n const [keyManuallyEdited, setKeyManuallyEdited] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [keyCopied, setKeyCopied] = useState(false);\n\n // Data preview state\n const [isLoadingPreview, setIsLoadingPreview] = useState(false);\n const [previewData, setPreviewData] = useState<unknown[] | null>(null);\n const [previewError, setPreviewError] = useState<string | null>(null);\n\n // Operations\n const { loading, error, dataSinks, fetchDataSink, createDataSink, updateDataSink, getDataSinkData } =\n useDataSinkOperations({\n skipFetch: true,\n });\n\n // Get existing data sink if editing\n const existingDataSink = isEdit ? dataSinks.find((ds) => ds.id === dataSinkId) : null;\n\n // Load existing data sink for editing\n useEffect(() => {\n if (isEdit && dataSinkId && !existingDataSink) {\n fetchDataSink(dataSinkId);\n }\n }, [isEdit, dataSinkId, existingDataSink, fetchDataSink]);\n\n // Track if form has been initialized\n const [formInitialized, setFormInitialized] = useState(false);\n\n // Populate form with existing data when editing (only once)\n useEffect(() => {\n if (existingDataSink && !formInitialized) {\n // Format existing data as pretty JSON string\n let dataString = '';\n if (existingDataSink.data) {\n try {\n // Check if data is already a string (JSON string from API)\n if (typeof existingDataSink.data === 'string') {\n // Parse and re-stringify to ensure proper formatting\n const parsed = JSON.parse(existingDataSink.data);\n dataString = JSON.stringify(parsed, null, 2);\n } else {\n // It's an object, just stringify it\n dataString = JSON.stringify(existingDataSink.data, null, 2);\n }\n } catch {\n // If parsing fails, use as-is if string, otherwise empty\n dataString = typeof existingDataSink.data === 'string' ? existingDataSink.data : '';\n }\n }\n\n setForm({\n name: existingDataSink.name || '',\n key: existingDataSink.key || '',\n description: existingDataSink.description || '',\n ttl: existingDataSink.ttl || 300,\n refresh: existingDataSink.refresh\n ? {\n type: existingDataSink.refresh.type === 'scheduled' ? 'scheduled' : 'manual',\n schedule: existingDataSink.refresh.schedule\n ? {\n cron: existingDataSink.refresh.schedule.cron,\n timezone: existingDataSink.refresh.schedule.timezone,\n }\n : undefined,\n }\n : { type: 'manual' },\n data: dataString,\n });\n setKeyManuallyEdited(true); // Don't auto-generate key when editing\n setFormInitialized(true); // Mark as initialized\n setErrors({}); // Clear any errors\n }\n }, [existingDataSink, formInitialized]);\n\n // Auto-generate key from name\n const handleNameChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setForm((prev) => ({\n ...prev,\n name,\n key: keyManuallyEdited ? prev.key : generateKey(name),\n }));\n setErrors((prev) => ({ ...prev, name: undefined }));\n },\n [keyManuallyEdited]\n );\n\n // Handle key change\n const handleKeyChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const key = e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '');\n setForm((prev) => ({ ...prev, key }));\n setKeyManuallyEdited(true);\n setErrors((prev) => ({ ...prev, key: undefined }));\n }, []);\n\n // Handle description change\n const handleDescriptionChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setForm((prev) => ({ ...prev, description: e.target.value }));\n }, []);\n\n // Handle TTL change\n const handleTTLChange = useCallback((ttl: number | null) => {\n setForm((prev) => ({ ...prev, ttl: ttl || 0 }));\n }, []);\n\n // Handle refresh config change\n const handleRefreshChange = useCallback((config: RefreshConfig) => {\n setForm((prev) => ({ ...prev, refresh: config }));\n }, []);\n\n // Handle data change\n const handleDataChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setForm((prev) => ({ ...prev, data: e.target.value }));\n setErrors((prev) => ({ ...prev, data: undefined }));\n }, []);\n\n // Format JSON data\n const handleFormatData = useCallback(() => {\n try {\n const parsed = JSON.parse(form.data);\n setForm((prev) => ({ ...prev, data: JSON.stringify(parsed, null, 2) }));\n setErrors((prev) => ({ ...prev, data: undefined }));\n } catch {\n setErrors((prev) => ({ ...prev, data: 'Invalid JSON format' }));\n }\n }, [form.data]);\n\n // Copy key to clipboard\n const handleCopyKey = useCallback(async () => {\n if (form.key) {\n await nativeCopyText(form.key);\n setKeyCopied(true);\n if (isNative()) void nativeToast('Key copied');\n setTimeout(() => setKeyCopied(false), 2000);\n }\n }, [form.key]);\n\n // Handle back navigation\n const handleBack = useCallback(() => {\n navigate('/datasinks');\n }, [navigate]);\n\n // Load data preview\n const handleShowData = useCallback(async () => {\n if (!isEdit || !existingDataSink) return;\n\n setIsLoadingPreview(true);\n setPreviewError(null);\n\n try {\n const result = await getDataSinkData({ key: existingDataSink.key });\n if (result?.raw) {\n // Get first 20 items if it's an array, otherwise wrap in array\n const data = Array.isArray(result.raw) ? result.raw.slice(0, 20) : [result.raw];\n setPreviewData(data);\n } else {\n setPreviewData([]);\n }\n } catch (err) {\n setPreviewError(err instanceof Error ? err.message : 'Failed to load data');\n } finally {\n setIsLoadingPreview(false);\n }\n }, [isEdit, existingDataSink, getDataSinkData]);\n\n // Handle form submission\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n setSubmitError(null);\n\n // Validate form\n const validationErrors = validateForm(form);\n if (Object.keys(validationErrors).length > 0) {\n setErrors(validationErrors);\n return;\n }\n\n setIsSaving(true);\n try {\n // Parse data JSON\n let parsedData: Record<string, unknown> = {};\n if (form.data && form.data.trim()) {\n try {\n parsedData = JSON.parse(form.data) as Record<string, unknown>;\n } catch {\n setErrors((prev) => ({ ...prev, data: 'Invalid JSON format' }));\n setIsSaving(false);\n return;\n }\n }\n\n if (isEdit && existingDataSink) {\n // Update existing\n const input: UpdateDataSinkInput = {\n id: existingDataSink.id,\n version: existingDataSink.version,\n name: form.name,\n description: form.description || undefined,\n ttl: form.ttl,\n refresh: {\n type: form.refresh.type as DataSinkRefreshType,\n schedule: form.refresh.schedule,\n },\n data: parsedData, // Include data in update\n };\n const result = await updateDataSink(input);\n if (!result) {\n throw new Error('Failed to update DataSink');\n }\n } else {\n // Create new - simplified flow (standalone DataSinks)\n const input: CreateDataSinkInput = {\n key: form.key,\n name: form.name,\n description: form.description || undefined,\n ttl: form.ttl,\n refresh: {\n type: form.refresh.type as DataSinkRefreshType,\n schedule: form.refresh.schedule,\n },\n data: parsedData, // Include data in create\n };\n const result = await createDataSink(input);\n if (!result) {\n throw new Error('Failed to create DataSink');\n }\n }\n navigate('/datasinks');\n } catch (err) {\n setSubmitError(err instanceof Error ? err.message : 'Failed to save DataSink');\n } finally {\n setIsSaving(false);\n }\n },\n [form, isEdit, existingDataSink, createDataSink, updateDataSink, navigate]\n );\n\n // Check if form is valid\n const isFormValid = useMemo(() => {\n const nameValid = form.name.trim().length >= 2;\n const keyValid = form.key.trim().length > 0;\n\n // Check JSON validity if data is provided\n let dataValid = true;\n if (form.data && form.data.trim()) {\n try {\n JSON.parse(form.data);\n } catch {\n dataValid = false;\n }\n }\n\n return nameValid && keyValid && dataValid;\n }, [form.name, form.key, form.data]);\n\n // Loading state\n if (loading && isEdit) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <Loader2 className=\"w-8 h-8 text-text-secondary animate-spin\" />\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-card-padding\">\n <div className=\"text-center\">\n <AlertCircle className=\"w-12 h-12 text-state-error mx-auto mb-4\" />\n <p className=\"text-state-error text-body-sm font-medium mb-2\">Failed to load DataSink</p>\n <p className=\"text-text-tertiary text-body-xs\">{error.message}</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"min-h-screen bg-bg-canvas\">\n <div className=\"max-w-3xl mx-auto p-card-padding\">\n {/* Header */}\n <div className=\"mb-6\">\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\" className=\"mb-4\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Back to DataSinks\n </Button>\n <h1 className=\"text-heading-h1 font-bold text-text-primary\">\n {isEdit ? 'Edit DataSink' : 'Create DataSink'}\n </h1>\n <p className=\"text-body-sm text-text-secondary mt-1\">\n {isEdit\n ? 'Update the DataSink configuration.'\n : 'Create a new DataSink to store and cache data. Use the unique key to connect workflows.'}\n </p>\n </div>\n\n {/* Error Alert */}\n {submitError && (\n <div className=\"mb-6 p-4 bg-state-error/10 border border-state-error/30 rounded-lg flex items-start gap-3\">\n <AlertCircle className=\"w-5 h-5 text-state-error flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-state-error\">Failed to save DataSink</p>\n <p className=\"text-xs text-state-error/80 mt-1\">{submitError}</p>\n </div>\n </div>\n )}\n\n <PagePurpose className=\"mb-6\">\n A data sink is the named, structured store your dashboards read from. Give it a unique key, configure how long\n data is cached and how it refreshes, then point a workflow at that key to push data in. Get the basics right\n here and everything downstream connects to it.\n </PagePurpose>\n\n {/* Form */}\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Basic Information — primary emphasis zone */}\n <GlassCard treatment=\"glass\" glow>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Database className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Basic Information</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Essential details for your DataSink</p>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {/* Name field */}\n <div className=\"space-y-2\">\n <Label htmlFor=\"name\">\n Name <span className=\"text-state-error\">*</span>\n </Label>\n <Input\n id=\"name\"\n type=\"text\"\n value={form.name}\n onChange={handleNameChange}\n placeholder=\"e.g., Sales Metrics\"\n className={errors.name ? 'border-state-error' : ''}\n />\n {errors.name && <p className=\"text-body-xs text-state-error\">{errors.name}</p>}\n </div>\n\n {/* Key field */}\n <div className=\"space-y-2\">\n <Label htmlFor=\"key\">\n Unique Key <span className=\"text-state-error\">*</span>\n </Label>\n <div className=\"flex gap-2\">\n <Input\n id=\"key\"\n type=\"text\"\n value={form.key}\n onChange={handleKeyChange}\n placeholder=\"sales_metrics\"\n disabled={isEdit}\n className={`font-mono flex-1 ${errors.key ? 'border-state-error' : ''}`}\n />\n {form.key && (\n <Button type=\"button\" onClick={handleCopyKey} variant=\"outline\" size=\"sm\" className=\"px-3\">\n {keyCopied ? <Check className=\"w-4 h-4 text-state-success\" /> : <Copy className=\"w-4 h-4\" />}\n </Button>\n )}\n </div>\n {errors.key && <p className=\"text-body-xs text-state-error\">{errors.key}</p>}\n <p className=\"text-body-xs text-text-tertiary\">\n {isEdit\n ? 'Key cannot be changed. Use this key in your workflows to push data.'\n : 'Auto-generated from name. This key will be used by workflows to push data.'}\n </p>\n </div>\n\n {/* Description field */}\n <div className=\"space-y-2\">\n <Label htmlFor=\"description\">Description</Label>\n <Textarea\n id=\"description\"\n value={form.description}\n onChange={handleDescriptionChange}\n placeholder=\"Describe what data this sink stores...\"\n rows={2}\n className=\"resize-none\"\n />\n </div>\n </CardContent>\n </GlassCard>\n\n {/* Configuration */}\n <Card>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Settings className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Configuration</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Cache and refresh settings</p>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {/* Cache TTL */}\n <div className=\"space-y-2\">\n <Label>Cache Duration</Label>\n <CacheTTLSelector value={form.ttl} onChange={handleTTLChange} />\n <p className=\"text-body-xs text-text-tertiary\">\n How long cached data remains valid before requiring refresh.\n </p>\n </div>\n\n {/* Refresh Type */}\n <RefreshTypeSelector value={form.refresh} onChange={handleRefreshChange} />\n </CardContent>\n </Card>\n\n {/* Data Payload */}\n <Card>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Database className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Data Payload</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {isEdit ? 'Edit the JSON data stored in this DataSink' : 'Optional initial data (JSON format)'}\n </p>\n </div>\n </div>\n <Button type=\"button\" onClick={handleFormatData} variant=\"outline\" size=\"sm\">\n Format JSON\n </Button>\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n <Textarea\n value={form.data}\n onChange={handleDataChange}\n placeholder='{\"key\": \"value\"}'\n rows={10}\n className={`font-mono text-xs resize-y ${errors.data ? 'border-state-error' : ''}`}\n />\n {errors.data && <p className=\"text-body-xs text-state-error\">{errors.data}</p>}\n <p className=\"text-body-xs text-text-tertiary\">\n Enter valid JSON data. This data will be stored and can be accessed by widgets via parsers.\n </p>\n </div>\n </CardContent>\n </Card>\n\n {/* Data Preview - Only show for existing DataSinks */}\n {isEdit && existingDataSink && (\n <Card>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Eye className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Data Preview</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Preview the first 20 records</p>\n </div>\n </div>\n <Button\n type=\"button\"\n onClick={handleShowData}\n variant=\"outline\"\n size=\"sm\"\n disabled={isLoadingPreview}\n >\n {isLoadingPreview ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n Loading...\n </>\n ) : (\n <>\n <Eye className=\"w-4 h-4 mr-2\" />\n Show Data\n </>\n )}\n </Button>\n </div>\n </CardHeader>\n {(previewData || previewError) && (\n <CardContent>\n {previewError ? (\n <div className=\"p-4 bg-state-error/10 border border-state-error/30 rounded-md\">\n <p className=\"text-sm text-state-error\">{previewError}</p>\n </div>\n ) : previewData && previewData.length > 0 ? (\n <pre className=\"p-4 bg-bg-sunken rounded-md overflow-auto max-h-[300px] text-xs font-mono\">\n {JSON.stringify(previewData, null, 2)}\n </pre>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No data available yet\"\n description=\"Connect a workflow to this sink's key to start pushing data.\"\n />\n )}\n </CardContent>\n )}\n </Card>\n )}\n\n {/* Submit */}\n <div className=\"flex items-center justify-end gap-4 pt-4\">\n <Button type=\"button\" onClick={handleBack} variant=\"secondary\">\n Cancel\n </Button>\n <Button type=\"submit\" disabled={!isFormValid || isSaving} variant=\"default\">\n {isSaving ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n Saving...\n </>\n ) : (\n <>\n <Save className=\"w-4 h-4 mr-2\" />\n {isEdit ? 'Save Changes' : 'Create DataSink'}\n </>\n )}\n </Button>\n </div>\n </form>\n\n {/* Info Box for New DataSinks */}\n {!isEdit && (\n <div className=\"mt-6 p-4 bg-bg-sunken rounded-lg border border-border-default\">\n <h3 className=\"text-sm font-medium text-text-primary mb-2\">What&apos;s Next?</h3>\n <ol className=\"list-decimal list-inside text-xs text-text-secondary space-y-1\">\n <li>Create this DataSink to get your unique key</li>\n <li>\n Go to <strong>Workflows</strong> and create a FluidGrids workflow\n </li>\n <li>Use the unique key in your workflow to push data to this DataSink</li>\n <li>\n Create <strong>Parsers</strong> to transform the data using JQ or JSONata\n </li>\n </ol>\n </div>\n )}\n </div>\n </div>\n );\n});\n\nexport default DataSinkBuilderPage;\n"],"mappings":"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"}
@@ -102,13 +102,13 @@ var T = s(function() {
102
102
  children: /* @__PURE__ */ C("div", {
103
103
  className: "text-center",
104
104
  children: [
105
- /* @__PURE__ */ S(ue, { className: "w-12 h-12 text-state-error mx-auto mb-spacing-md" }),
105
+ /* @__PURE__ */ S(ue, { className: "w-12 h-12 text-state-error mx-auto mb-4" }),
106
106
  /* @__PURE__ */ S("p", {
107
- className: "text-state-error text-body-sm font-medium mb-spacing-sm",
107
+ className: "text-state-error text-body-sm font-medium mb-2",
108
108
  children: "Failed to load DataSink"
109
109
  }),
110
110
  /* @__PURE__ */ S("p", {
111
- className: "text-text-tertiary text-body-xs mb-spacing-lg",
111
+ className: "text-text-tertiary text-body-xs mb-6",
112
112
  children: W.message
113
113
  }),
114
114
  /* @__PURE__ */ C(g, {
@@ -120,10 +120,10 @@ var T = s(function() {
120
120
  ]
121
121
  })
122
122
  }) : U ? /* @__PURE__ */ C("div", {
123
- className: "p-card-padding space-y-spacing-lg",
123
+ className: "p-card-padding space-y-6",
124
124
  children: [
125
125
  /* @__PURE__ */ C("div", {
126
- className: "flex flex-col sm:flex-row sm:items-start sm:justify-between gap-spacing-md",
126
+ className: "flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4",
127
127
  children: [/* @__PURE__ */ C("div", {
128
128
  className: "flex-1",
129
129
  children: [
@@ -131,27 +131,27 @@ var T = s(function() {
131
131
  onClick: Y,
132
132
  variant: "ghost",
133
133
  size: "sm",
134
- className: "mb-spacing-md",
134
+ className: "mb-4",
135
135
  children: [/* @__PURE__ */ S(u, { className: "w-4 h-4 mr-2" }), "Back to DataSinks"]
136
136
  }),
137
137
  /* @__PURE__ */ C("div", {
138
- className: "flex items-center gap-spacing-sm flex-wrap",
138
+ className: "flex items-center gap-2 flex-wrap",
139
139
  children: [/* @__PURE__ */ S("h1", {
140
140
  className: "text-heading-h1 font-bold text-text-primary",
141
141
  children: U.name || "Untitled"
142
142
  }), /* @__PURE__ */ S(r, { status: U.status })]
143
143
  }),
144
144
  /* @__PURE__ */ S("p", {
145
- className: "text-body-sm text-text-tertiary font-mono mt-spacing-xs",
145
+ className: "text-body-sm text-text-tertiary font-mono mt-1",
146
146
  children: U.key
147
147
  }),
148
148
  U.description && /* @__PURE__ */ S("p", {
149
- className: "text-body-sm text-text-secondary mt-spacing-sm",
149
+ className: "text-body-sm text-text-secondary mt-2",
150
150
  children: U.description
151
151
  })
152
152
  ]
153
153
  }), /* @__PURE__ */ C("div", {
154
- className: "flex items-center gap-spacing-sm flex-wrap",
154
+ className: "flex items-center gap-2 flex-wrap",
155
155
  children: [
156
156
  /* @__PURE__ */ C(g, {
157
157
  onClick: X,
@@ -213,13 +213,13 @@ var T = s(function() {
213
213
  ]
214
214
  }),
215
215
  /* @__PURE__ */ C("div", {
216
- className: "grid gap-spacing-lg md:grid-cols-2",
216
+ className: "grid gap-6 md:grid-cols-2",
217
217
  children: [
218
218
  /* @__PURE__ */ C(ge, {
219
219
  treatment: "glass",
220
220
  glow: !0,
221
221
  children: [/* @__PURE__ */ S(y, { children: /* @__PURE__ */ S(b, { children: "Details" }) }), /* @__PURE__ */ S(v, { children: /* @__PURE__ */ C("dl", {
222
- className: "space-y-spacing-md text-body-sm",
222
+ className: "space-y-4 text-body-sm",
223
223
  children: [
224
224
  /* @__PURE__ */ C("div", {
225
225
  className: "flex justify-between items-center",
@@ -309,7 +309,7 @@ var T = s(function() {
309
309
  /* @__PURE__ */ C(y, {
310
310
  className: "flex flex-row items-center justify-between space-y-0",
311
311
  children: [/* @__PURE__ */ C(b, {
312
- className: "flex items-center gap-spacing-sm",
312
+ className: "flex items-center gap-2",
313
313
  children: [/* @__PURE__ */ S(d, { className: "w-4 h-4 text-text-secondary" }), "Data Preview"]
314
314
  }), /* @__PURE__ */ C("div", {
315
315
  className: "flex items-center gap-2",
@@ -341,12 +341,12 @@ var T = s(function() {
341
341
  data: P,
342
342
  maxHeight: "400px"
343
343
  }), H !== null && /* @__PURE__ */ C("div", {
344
- className: "flex items-center justify-between gap-spacing-md mt-spacing-md pt-spacing-md border-t border-border-subtle",
344
+ className: "flex items-center justify-between gap-4 mt-4 pt-4 border-t border-border-subtle",
345
345
  children: [/* @__PURE__ */ C("div", {
346
- className: "flex items-center gap-spacing-sm text-body-sm text-text-secondary",
346
+ className: "flex items-center gap-2 text-body-sm text-text-secondary",
347
347
  children: [/* @__PURE__ */ S("span", { children: "Rows per page" }), /* @__PURE__ */ S("select", {
348
348
  "aria-label": "Rows per page",
349
- className: "bg-bg-canvas border border-border-default rounded px-spacing-sm py-spacing-2xs text-text-primary",
349
+ className: "bg-bg-canvas border border-border-default rounded px-2 py-0.5 text-text-primary",
350
350
  value: z,
351
351
  onChange: (e) => Oe(Number(e.target.value)),
352
352
  children: [
@@ -359,7 +359,7 @@ var T = s(function() {
359
359
  }, e))
360
360
  })]
361
361
  }), /* @__PURE__ */ C("div", {
362
- className: "flex items-center gap-spacing-md text-body-sm text-text-secondary",
362
+ className: "flex items-center gap-4 text-body-sm text-text-secondary",
363
363
  children: [
364
364
  /* @__PURE__ */ S("span", { children: H === 0 ? "0 rows" : `${B + 1}–${Math.min(B + z, H)} of ${H}` }),
365
365
  /* @__PURE__ */ S(g, {
@@ -380,8 +380,8 @@ var T = s(function() {
380
380
  })]
381
381
  })] }),
382
382
  !E && /* @__PURE__ */ C(v, {
383
- className: "text-center p-spacing-xl",
384
- children: [/* @__PURE__ */ S(d, { className: "w-10 h-10 text-text-tertiary mx-auto mb-spacing-md" }), /* @__PURE__ */ S("p", {
383
+ className: "text-center p-8",
384
+ children: [/* @__PURE__ */ S(d, { className: "w-10 h-10 text-text-tertiary mx-auto mb-4" }), /* @__PURE__ */ S("p", {
385
385
  className: "text-body-sm text-text-secondary",
386
386
  children: "Click \"Show Data\" to preview the cached data."
387
387
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"DataSinkViewPage.js","names":[],"sources":["../../../src/bigconsole/pages/DataSinkViewPage.tsx"],"sourcesContent":["/**\n * DataSink View Page\n *\n * Page for viewing DataSink details, data preview, and actions.\n */\n\nimport { memo, useState, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeConfirm, nativeImpact, nativeNotify } from '../../utils/nativeBridge';\nimport { formatDistanceToNow } from 'date-fns';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { useDataSinkSubscription } from '../hooks/useDataSinkSubscription';\nimport { useDataSinkStore } from '../store/dataSinkStore';\nimport { DataSinkStatusBadge } from '../components/datasink/DataSinkStatusBadge';\nimport { DataSinkDataViewer } from '../components/datasink/DataSinkDataViewer';\nimport { DataSinkTableViewer } from '../components/datasink/DataSinkTableViewer';\nimport { LinkedWorkflowsPanel } from '../components/datasink/LinkedWorkflowsPanel';\nimport { LinkedParsersPanel } from '../components/datasink/LinkedParsersPanel';\nimport { WorkflowStatusPanel } from '../components/datasink/WorkflowStatusPanel';\nimport { ExportDataSinkDialog } from '../components/datasink/ExportDataSinkDialog';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { GlassCard, IllustratedEmptyState, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\n\n// Lucide icons\nimport {\n ArrowLeft,\n Edit,\n RefreshCw,\n Trash2,\n Database,\n Loader2,\n AlertCircle,\n Eye,\n EyeOff,\n Download,\n Grid3X3,\n Code,\n} from 'lucide-react';\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const DataSinkViewPage = memo(function DataSinkViewPage() {\n const { dataSinkId } = useParams<{ dataSinkId: string; consoleId: string }>();\n const navigate = useAppNavigate();\n\n // Local state\n const [showData, setShowData] = useState(false);\n const [isRefreshing, setIsRefreshing] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);\n const [dataPreview, setDataPreview] = useState<Record<string, unknown> | null>(null);\n const [dataLoading, setDataLoading] = useState(false);\n const [dataViewMode, setDataViewMode] = useState<'table' | 'json'>('table');\n // v3.0: server-side pagination for array payloads (avoids loading millions of rows).\n const [pageSize, setPageSize] = useState(100);\n const [pageOffset, setPageOffset] = useState(0);\n const [dataTotalCount, setDataTotalCount] = useState<number | null>(null);\n const [dataHasMore, setDataHasMore] = useState(false);\n\n // Store\n const dataSink = useDataSinkStore((state) => (dataSinkId ? state.dataSinks.get(dataSinkId) : undefined));\n\n // Operations\n const { loading, error, fetchDataSink, getDataSinkData, refreshDataSink, deleteDataSink } = useDataSinkOperations({\n skipFetch: true,\n });\n\n // Subscribe to updates\n useDataSinkSubscription({\n dataSinkId,\n enabled: !!dataSinkId,\n });\n\n // Load DataSink on mount - always fetch full data to get workflowLinks\n useEffect(() => {\n if (dataSinkId) {\n fetchDataSink(dataSinkId);\n }\n }, [dataSinkId, fetchDataSink]);\n\n // Handle back navigation\n const handleBack = useCallback(() => {\n navigate(`/datasinks`);\n }, [navigate]);\n\n // Handle edit\n const handleEdit = useCallback(() => {\n navigate(`/datasinks/${dataSinkId}/edit`);\n }, [navigate, dataSinkId]);\n\n // Handle refresh\n const handleRefresh = useCallback(async () => {\n if (!dataSinkId) return;\n setIsRefreshing(true);\n try {\n await refreshDataSink({ id: dataSinkId });\n } catch (err) {\n } finally {\n setIsRefreshing(false);\n }\n }, [dataSinkId, refreshDataSink]);\n\n // Handle delete\n const handleDelete = useCallback(async () => {\n if (!dataSinkId) return;\n const native = await nativeConfirm({\n title: 'Delete DataSink?',\n message: 'Are you sure you want to delete this DataSink?',\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const ok = native === null ? window.confirm('Are you sure you want to delete this DataSink?') : native;\n if (!ok) return;\n nativeImpact('medium');\n setIsDeleting(true);\n try {\n await deleteDataSink(dataSinkId);\n if (isNative()) void nativeNotify('success');\n navigate(`/datasinks`);\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n }\n }, [dataSinkId, deleteDataSink, navigate]);\n\n // Load one page of the sink's data (v3.0: server-side pagination).\n const loadDataPage = useCallback(\n async (offset: number, size: number) => {\n if (!dataSinkId) return;\n setDataLoading(true);\n try {\n const result = await getDataSinkData({ id: dataSinkId }, { limit: size, offset });\n if (result) {\n setDataPreview((result.raw as Record<string, unknown>) || {});\n // totalCount is non-null only for array payloads being paginated.\n setDataTotalCount(typeof result.totalCount === 'number' ? result.totalCount : null);\n setDataHasMore(Boolean(result.hasMore));\n setPageOffset(offset);\n }\n } catch {\n // handled by the hook's error state\n } finally {\n setDataLoading(false);\n }\n },\n [dataSinkId, getDataSinkData]\n );\n\n // Handle show data (toggles the preview; loads the first page).\n const handleShowData = useCallback(async () => {\n if (!dataSinkId) return;\n if (showData) {\n setShowData(false);\n return;\n }\n await loadDataPage(0, pageSize);\n setShowData(true);\n }, [dataSinkId, showData, loadDataPage, pageSize]);\n\n const handlePageSizeChange = useCallback(\n (size: number) => {\n setPageSize(size);\n void loadDataPage(0, size);\n },\n [loadDataPage]\n );\n\n // Handle export\n const handleExport = useCallback(() => {\n setIsExportDialogOpen(true);\n }, []);\n\n const handleExportClose = useCallback(() => {\n setIsExportDialogOpen(false);\n }, []);\n\n // Loading state\n if (loading && !dataSink) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <Loader2 className=\"w-8 h-8 text-text-secondary animate-spin\" />\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-card-padding\">\n <div className=\"text-center\">\n <AlertCircle className=\"w-12 h-12 text-state-error mx-auto mb-spacing-md\" />\n <p className=\"text-state-error text-body-sm font-medium mb-spacing-sm\">Failed to load DataSink</p>\n <p className=\"text-text-tertiary text-body-xs mb-spacing-lg\">{error.message}</p>\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Go back\n </Button>\n </div>\n </div>\n );\n }\n\n // Not found state\n if (!dataSink) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-card-padding\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"DataSink not found\"\n description=\"The DataSink you're looking for doesn't exist or has been deleted.\"\n action={\n <Button onClick={handleBack} variant=\"default\" size=\"default\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Go back to DataSinks\n </Button>\n }\n />\n </div>\n );\n }\n\n return (\n <div className=\"p-card-padding space-y-spacing-lg\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-spacing-md\">\n <div className=\"flex-1\">\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\" className=\"mb-spacing-md\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Back to DataSinks\n </Button>\n <div className=\"flex items-center gap-spacing-sm flex-wrap\">\n <h1 className=\"text-heading-h1 font-bold text-text-primary\">{dataSink.name || 'Untitled'}</h1>\n <DataSinkStatusBadge status={dataSink.status} />\n </div>\n <p className=\"text-body-sm text-text-tertiary font-mono mt-spacing-xs\">{dataSink.key}</p>\n {dataSink.description && (\n <p className=\"text-body-sm text-text-secondary mt-spacing-sm\">{dataSink.description}</p>\n )}\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-spacing-sm flex-wrap\">\n <Button onClick={handleRefresh} disabled={isRefreshing} variant=\"secondary\" size=\"default\">\n <RefreshCw className={`w-4 h-4 mr-2 ${isRefreshing ? 'animate-spin' : ''}`} />\n {isRefreshing ? 'Refreshing...' : 'Refresh'}\n </Button>\n <Button onClick={handleExport} variant=\"secondary\" size=\"default\">\n <Download className=\"w-4 h-4 mr-2\" />\n Export\n </Button>\n <Button onClick={handleEdit} variant=\"secondary\" size=\"default\">\n <Edit className=\"w-4 h-4 mr-2\" />\n Edit\n </Button>\n <Button onClick={handleDelete} disabled={isDeleting} variant=\"destructive\" size=\"default\">\n <Trash2 className=\"w-4 h-4 mr-2\" />\n {isDeleting ? 'Deleting...' : 'Delete'}\n </Button>\n </div>\n </div>\n\n <PagePurpose>\n A data sink is a structured store that holds your processed data and serves it to dashboards. From here you can\n inspect its schema and stats, preview the cached rows, refresh it from its linked workflow, or export it.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"bigconsole-datasink-view\"\n steps={[\n {\n id: 'preview',\n label: 'Preview the data',\n description: 'Open the cached rows to confirm the sink holds what you expect.',\n onClick: handleShowData,\n },\n {\n id: 'refresh',\n label: 'Refresh from source',\n description: 'Re-run the linked workflow to pull the latest data.',\n onClick: handleRefresh,\n },\n {\n id: 'build-dashboard',\n label: 'Visualize it on a dashboard',\n description: 'Add widgets that query this sink to see your data live.',\n onClick: () => navigate('/dashboards'),\n },\n {\n id: 'export',\n label: 'Export the data',\n description: 'Download the contents for use elsewhere.',\n onClick: handleExport,\n },\n ]}\n />\n\n {/* Details Grid */}\n <div className=\"grid gap-spacing-lg md:grid-cols-2\">\n {/* Info Panel — primary emphasis zone */}\n <GlassCard treatment=\"glass\" glow>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent>\n <dl className=\"space-y-spacing-md text-body-sm\">\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Status</dt>\n <dd>\n <DataSinkStatusBadge status={dataSink.status} />\n </dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Cache TTL</dt>\n <dd className=\"text-text-primary font-medium\">{dataSink.ttl ? `${dataSink.ttl}s` : 'None'}</dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Refresh Type</dt>\n <dd className=\"text-text-primary font-medium capitalize\">{dataSink.refresh?.type || 'Manual'}</dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Size</dt>\n <dd className=\"text-text-primary font-medium\">\n {dataSink.sizeBytes ? `${(dataSink.sizeBytes / 1024).toFixed(2)} KB` : '0 KB'}\n </dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Version</dt>\n <dd className=\"text-text-primary font-medium\">{dataSink.version || 1}</dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Last Refreshed</dt>\n <dd className=\"text-text-primary font-medium\">\n {dataSink.lastRefreshedAt\n ? formatDistanceToNow(new Date(dataSink.lastRefreshedAt), { addSuffix: true })\n : 'Never'}\n </dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Created</dt>\n <dd className=\"text-text-primary font-medium\">\n {formatDistanceToNow(new Date(dataSink.createdAt), { addSuffix: true })}\n </dd>\n </div>\n </dl>\n </CardContent>\n </GlassCard>\n\n {/* Workflow Status Panel */}\n {dataSink.fluidGridsWorkflowId && (\n <WorkflowStatusPanel\n workflowId={dataSink.fluidGridsWorkflowId}\n executionId={dataSink.fluidGridsWorkflowExecutionId || undefined}\n lastRefreshedAt={dataSink.lastRefreshedAt || undefined}\n setupState={dataSink.setupState}\n status={dataSink.status}\n onRerunWorkflow={handleRefresh}\n disabled={isRefreshing}\n isRerunning={isRefreshing}\n />\n )}\n\n {/* Linked Workflows */}\n <LinkedWorkflowsPanel workflowLinks={dataSink.workflowLinks} />\n\n {/* Linked Parsers */}\n <LinkedParsersPanel dataSinkKey={dataSink.key} />\n </div>\n\n {/* Data Preview Section */}\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle className=\"flex items-center gap-spacing-sm\">\n <Database className=\"w-4 h-4 text-text-secondary\" />\n Data Preview\n </CardTitle>\n <div className=\"flex items-center gap-2\">\n {showData && (\n <div className=\"flex items-center border border-border-default rounded-md overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setDataViewMode('table')}\n className={`p-1.5 ${dataViewMode === 'table' ? 'bg-bg-sunken text-text-primary' : 'text-text-secondary hover:text-text-primary'}`}\n title=\"Table view\"\n >\n <Grid3X3 className=\"w-4 h-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setDataViewMode('json')}\n className={`p-1.5 ${dataViewMode === 'json' ? 'bg-bg-sunken text-text-primary' : 'text-text-secondary hover:text-text-primary'}`}\n title=\"JSON view\"\n >\n <Code className=\"w-4 h-4\" />\n </button>\n </div>\n )}\n <Button\n onClick={handleShowData}\n disabled={dataLoading}\n variant={showData ? 'secondary' : 'default'}\n size=\"sm\"\n >\n {dataLoading ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n Loading...\n </>\n ) : showData ? (\n <>\n <EyeOff className=\"w-4 h-4 mr-2\" />\n Hide Data\n </>\n ) : (\n <>\n <Eye className=\"w-4 h-4 mr-2\" />\n Show Data\n </>\n )}\n </Button>\n </div>\n </CardHeader>\n\n {showData && dataPreview && (\n <CardContent>\n {dataViewMode === 'table' ? (\n <DataSinkTableViewer data={dataPreview} maxHeight=\"400px\" />\n ) : (\n <DataSinkDataViewer data={dataPreview} maxHeight=\"400px\" />\n )}\n {/* v3.0: pagination controls — shown only for array payloads. */}\n {dataTotalCount !== null && (\n <div className=\"flex items-center justify-between gap-spacing-md mt-spacing-md pt-spacing-md border-t border-border-subtle\">\n <div className=\"flex items-center gap-spacing-sm text-body-sm text-text-secondary\">\n <span>Rows per page</span>\n <select\n aria-label=\"Rows per page\"\n className=\"bg-bg-canvas border border-border-default rounded px-spacing-sm py-spacing-2xs text-text-primary\"\n value={pageSize}\n onChange={(e) => handlePageSizeChange(Number(e.target.value))}\n >\n {[100, 500, 1000].map((n) => (\n <option key={n} value={n}>\n {n}\n </option>\n ))}\n </select>\n </div>\n <div className=\"flex items-center gap-spacing-md text-body-sm text-text-secondary\">\n <span>\n {dataTotalCount === 0\n ? '0 rows'\n : `${pageOffset + 1}–${Math.min(pageOffset + pageSize, dataTotalCount)} of ${dataTotalCount}`}\n </span>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={pageOffset === 0 || dataLoading}\n onClick={() => void loadDataPage(Math.max(0, pageOffset - pageSize), pageSize)}\n >\n Previous\n </Button>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={!dataHasMore || dataLoading}\n onClick={() => void loadDataPage(pageOffset + pageSize, pageSize)}\n >\n Next\n </Button>\n </div>\n </div>\n )}\n </CardContent>\n )}\n\n {!showData && (\n <CardContent className=\"text-center p-spacing-xl\">\n <Database className=\"w-10 h-10 text-text-tertiary mx-auto mb-spacing-md\" />\n <p className=\"text-body-sm text-text-secondary\">Click &quot;Show Data&quot; to preview the cached data.</p>\n </CardContent>\n )}\n </Card>\n\n {/* Export DataSink Dialog */}\n <ExportDataSinkDialog\n isOpen={isExportDialogOpen}\n dataSinkId={dataSinkId || null}\n dataSinkName={dataSink.name || dataSink.key}\n onClose={handleExportClose}\n />\n </div>\n );\n});\n\nexport default DataSinkViewPage;\n"],"mappings":"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"}
1
+ {"version":3,"file":"DataSinkViewPage.js","names":[],"sources":["../../../src/bigconsole/pages/DataSinkViewPage.tsx"],"sourcesContent":["/**\n * DataSink View Page\n *\n * Page for viewing DataSink details, data preview, and actions.\n */\n\nimport { memo, useState, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeConfirm, nativeImpact, nativeNotify } from '../../utils/nativeBridge';\nimport { formatDistanceToNow } from 'date-fns';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { useDataSinkSubscription } from '../hooks/useDataSinkSubscription';\nimport { useDataSinkStore } from '../store/dataSinkStore';\nimport { DataSinkStatusBadge } from '../components/datasink/DataSinkStatusBadge';\nimport { DataSinkDataViewer } from '../components/datasink/DataSinkDataViewer';\nimport { DataSinkTableViewer } from '../components/datasink/DataSinkTableViewer';\nimport { LinkedWorkflowsPanel } from '../components/datasink/LinkedWorkflowsPanel';\nimport { LinkedParsersPanel } from '../components/datasink/LinkedParsersPanel';\nimport { WorkflowStatusPanel } from '../components/datasink/WorkflowStatusPanel';\nimport { ExportDataSinkDialog } from '../components/datasink/ExportDataSinkDialog';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { GlassCard, IllustratedEmptyState, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\n\n// Lucide icons\nimport {\n ArrowLeft,\n Edit,\n RefreshCw,\n Trash2,\n Database,\n Loader2,\n AlertCircle,\n Eye,\n EyeOff,\n Download,\n Grid3X3,\n Code,\n} from 'lucide-react';\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const DataSinkViewPage = memo(function DataSinkViewPage() {\n const { dataSinkId } = useParams<{ dataSinkId: string; consoleId: string }>();\n const navigate = useAppNavigate();\n\n // Local state\n const [showData, setShowData] = useState(false);\n const [isRefreshing, setIsRefreshing] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);\n const [dataPreview, setDataPreview] = useState<Record<string, unknown> | null>(null);\n const [dataLoading, setDataLoading] = useState(false);\n const [dataViewMode, setDataViewMode] = useState<'table' | 'json'>('table');\n // v3.0: server-side pagination for array payloads (avoids loading millions of rows).\n const [pageSize, setPageSize] = useState(100);\n const [pageOffset, setPageOffset] = useState(0);\n const [dataTotalCount, setDataTotalCount] = useState<number | null>(null);\n const [dataHasMore, setDataHasMore] = useState(false);\n\n // Store\n const dataSink = useDataSinkStore((state) => (dataSinkId ? state.dataSinks.get(dataSinkId) : undefined));\n\n // Operations\n const { loading, error, fetchDataSink, getDataSinkData, refreshDataSink, deleteDataSink } = useDataSinkOperations({\n skipFetch: true,\n });\n\n // Subscribe to updates\n useDataSinkSubscription({\n dataSinkId,\n enabled: !!dataSinkId,\n });\n\n // Load DataSink on mount - always fetch full data to get workflowLinks\n useEffect(() => {\n if (dataSinkId) {\n fetchDataSink(dataSinkId);\n }\n }, [dataSinkId, fetchDataSink]);\n\n // Handle back navigation\n const handleBack = useCallback(() => {\n navigate(`/datasinks`);\n }, [navigate]);\n\n // Handle edit\n const handleEdit = useCallback(() => {\n navigate(`/datasinks/${dataSinkId}/edit`);\n }, [navigate, dataSinkId]);\n\n // Handle refresh\n const handleRefresh = useCallback(async () => {\n if (!dataSinkId) return;\n setIsRefreshing(true);\n try {\n await refreshDataSink({ id: dataSinkId });\n } catch (err) {\n } finally {\n setIsRefreshing(false);\n }\n }, [dataSinkId, refreshDataSink]);\n\n // Handle delete\n const handleDelete = useCallback(async () => {\n if (!dataSinkId) return;\n const native = await nativeConfirm({\n title: 'Delete DataSink?',\n message: 'Are you sure you want to delete this DataSink?',\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const ok = native === null ? window.confirm('Are you sure you want to delete this DataSink?') : native;\n if (!ok) return;\n nativeImpact('medium');\n setIsDeleting(true);\n try {\n await deleteDataSink(dataSinkId);\n if (isNative()) void nativeNotify('success');\n navigate(`/datasinks`);\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n }\n }, [dataSinkId, deleteDataSink, navigate]);\n\n // Load one page of the sink's data (v3.0: server-side pagination).\n const loadDataPage = useCallback(\n async (offset: number, size: number) => {\n if (!dataSinkId) return;\n setDataLoading(true);\n try {\n const result = await getDataSinkData({ id: dataSinkId }, { limit: size, offset });\n if (result) {\n setDataPreview((result.raw as Record<string, unknown>) || {});\n // totalCount is non-null only for array payloads being paginated.\n setDataTotalCount(typeof result.totalCount === 'number' ? result.totalCount : null);\n setDataHasMore(Boolean(result.hasMore));\n setPageOffset(offset);\n }\n } catch {\n // handled by the hook's error state\n } finally {\n setDataLoading(false);\n }\n },\n [dataSinkId, getDataSinkData]\n );\n\n // Handle show data (toggles the preview; loads the first page).\n const handleShowData = useCallback(async () => {\n if (!dataSinkId) return;\n if (showData) {\n setShowData(false);\n return;\n }\n await loadDataPage(0, pageSize);\n setShowData(true);\n }, [dataSinkId, showData, loadDataPage, pageSize]);\n\n const handlePageSizeChange = useCallback(\n (size: number) => {\n setPageSize(size);\n void loadDataPage(0, size);\n },\n [loadDataPage]\n );\n\n // Handle export\n const handleExport = useCallback(() => {\n setIsExportDialogOpen(true);\n }, []);\n\n const handleExportClose = useCallback(() => {\n setIsExportDialogOpen(false);\n }, []);\n\n // Loading state\n if (loading && !dataSink) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <Loader2 className=\"w-8 h-8 text-text-secondary animate-spin\" />\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-card-padding\">\n <div className=\"text-center\">\n <AlertCircle className=\"w-12 h-12 text-state-error mx-auto mb-4\" />\n <p className=\"text-state-error text-body-sm font-medium mb-2\">Failed to load DataSink</p>\n <p className=\"text-text-tertiary text-body-xs mb-6\">{error.message}</p>\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Go back\n </Button>\n </div>\n </div>\n );\n }\n\n // Not found state\n if (!dataSink) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-card-padding\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"DataSink not found\"\n description=\"The DataSink you're looking for doesn't exist or has been deleted.\"\n action={\n <Button onClick={handleBack} variant=\"default\" size=\"default\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Go back to DataSinks\n </Button>\n }\n />\n </div>\n );\n }\n\n return (\n <div className=\"p-card-padding space-y-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4\">\n <div className=\"flex-1\">\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\" className=\"mb-4\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Back to DataSinks\n </Button>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h1 className=\"text-heading-h1 font-bold text-text-primary\">{dataSink.name || 'Untitled'}</h1>\n <DataSinkStatusBadge status={dataSink.status} />\n </div>\n <p className=\"text-body-sm text-text-tertiary font-mono mt-1\">{dataSink.key}</p>\n {dataSink.description && <p className=\"text-body-sm text-text-secondary mt-2\">{dataSink.description}</p>}\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-2 flex-wrap\">\n <Button onClick={handleRefresh} disabled={isRefreshing} variant=\"secondary\" size=\"default\">\n <RefreshCw className={`w-4 h-4 mr-2 ${isRefreshing ? 'animate-spin' : ''}`} />\n {isRefreshing ? 'Refreshing...' : 'Refresh'}\n </Button>\n <Button onClick={handleExport} variant=\"secondary\" size=\"default\">\n <Download className=\"w-4 h-4 mr-2\" />\n Export\n </Button>\n <Button onClick={handleEdit} variant=\"secondary\" size=\"default\">\n <Edit className=\"w-4 h-4 mr-2\" />\n Edit\n </Button>\n <Button onClick={handleDelete} disabled={isDeleting} variant=\"destructive\" size=\"default\">\n <Trash2 className=\"w-4 h-4 mr-2\" />\n {isDeleting ? 'Deleting...' : 'Delete'}\n </Button>\n </div>\n </div>\n\n <PagePurpose>\n A data sink is a structured store that holds your processed data and serves it to dashboards. From here you can\n inspect its schema and stats, preview the cached rows, refresh it from its linked workflow, or export it.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"bigconsole-datasink-view\"\n steps={[\n {\n id: 'preview',\n label: 'Preview the data',\n description: 'Open the cached rows to confirm the sink holds what you expect.',\n onClick: handleShowData,\n },\n {\n id: 'refresh',\n label: 'Refresh from source',\n description: 'Re-run the linked workflow to pull the latest data.',\n onClick: handleRefresh,\n },\n {\n id: 'build-dashboard',\n label: 'Visualize it on a dashboard',\n description: 'Add widgets that query this sink to see your data live.',\n onClick: () => navigate('/dashboards'),\n },\n {\n id: 'export',\n label: 'Export the data',\n description: 'Download the contents for use elsewhere.',\n onClick: handleExport,\n },\n ]}\n />\n\n {/* Details Grid */}\n <div className=\"grid gap-6 md:grid-cols-2\">\n {/* Info Panel — primary emphasis zone */}\n <GlassCard treatment=\"glass\" glow>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent>\n <dl className=\"space-y-4 text-body-sm\">\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Status</dt>\n <dd>\n <DataSinkStatusBadge status={dataSink.status} />\n </dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Cache TTL</dt>\n <dd className=\"text-text-primary font-medium\">{dataSink.ttl ? `${dataSink.ttl}s` : 'None'}</dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Refresh Type</dt>\n <dd className=\"text-text-primary font-medium capitalize\">{dataSink.refresh?.type || 'Manual'}</dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Size</dt>\n <dd className=\"text-text-primary font-medium\">\n {dataSink.sizeBytes ? `${(dataSink.sizeBytes / 1024).toFixed(2)} KB` : '0 KB'}\n </dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Version</dt>\n <dd className=\"text-text-primary font-medium\">{dataSink.version || 1}</dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Last Refreshed</dt>\n <dd className=\"text-text-primary font-medium\">\n {dataSink.lastRefreshedAt\n ? formatDistanceToNow(new Date(dataSink.lastRefreshedAt), { addSuffix: true })\n : 'Never'}\n </dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Created</dt>\n <dd className=\"text-text-primary font-medium\">\n {formatDistanceToNow(new Date(dataSink.createdAt), { addSuffix: true })}\n </dd>\n </div>\n </dl>\n </CardContent>\n </GlassCard>\n\n {/* Workflow Status Panel */}\n {dataSink.fluidGridsWorkflowId && (\n <WorkflowStatusPanel\n workflowId={dataSink.fluidGridsWorkflowId}\n executionId={dataSink.fluidGridsWorkflowExecutionId || undefined}\n lastRefreshedAt={dataSink.lastRefreshedAt || undefined}\n setupState={dataSink.setupState}\n status={dataSink.status}\n onRerunWorkflow={handleRefresh}\n disabled={isRefreshing}\n isRerunning={isRefreshing}\n />\n )}\n\n {/* Linked Workflows */}\n <LinkedWorkflowsPanel workflowLinks={dataSink.workflowLinks} />\n\n {/* Linked Parsers */}\n <LinkedParsersPanel dataSinkKey={dataSink.key} />\n </div>\n\n {/* Data Preview Section */}\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle className=\"flex items-center gap-2\">\n <Database className=\"w-4 h-4 text-text-secondary\" />\n Data Preview\n </CardTitle>\n <div className=\"flex items-center gap-2\">\n {showData && (\n <div className=\"flex items-center border border-border-default rounded-md overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setDataViewMode('table')}\n className={`p-1.5 ${dataViewMode === 'table' ? 'bg-bg-sunken text-text-primary' : 'text-text-secondary hover:text-text-primary'}`}\n title=\"Table view\"\n >\n <Grid3X3 className=\"w-4 h-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => setDataViewMode('json')}\n className={`p-1.5 ${dataViewMode === 'json' ? 'bg-bg-sunken text-text-primary' : 'text-text-secondary hover:text-text-primary'}`}\n title=\"JSON view\"\n >\n <Code className=\"w-4 h-4\" />\n </button>\n </div>\n )}\n <Button\n onClick={handleShowData}\n disabled={dataLoading}\n variant={showData ? 'secondary' : 'default'}\n size=\"sm\"\n >\n {dataLoading ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n Loading...\n </>\n ) : showData ? (\n <>\n <EyeOff className=\"w-4 h-4 mr-2\" />\n Hide Data\n </>\n ) : (\n <>\n <Eye className=\"w-4 h-4 mr-2\" />\n Show Data\n </>\n )}\n </Button>\n </div>\n </CardHeader>\n\n {showData && dataPreview && (\n <CardContent>\n {dataViewMode === 'table' ? (\n <DataSinkTableViewer data={dataPreview} maxHeight=\"400px\" />\n ) : (\n <DataSinkDataViewer data={dataPreview} maxHeight=\"400px\" />\n )}\n {/* v3.0: pagination controls — shown only for array payloads. */}\n {dataTotalCount !== null && (\n <div className=\"flex items-center justify-between gap-4 mt-4 pt-4 border-t border-border-subtle\">\n <div className=\"flex items-center gap-2 text-body-sm text-text-secondary\">\n <span>Rows per page</span>\n <select\n aria-label=\"Rows per page\"\n className=\"bg-bg-canvas border border-border-default rounded px-2 py-0.5 text-text-primary\"\n value={pageSize}\n onChange={(e) => handlePageSizeChange(Number(e.target.value))}\n >\n {[100, 500, 1000].map((n) => (\n <option key={n} value={n}>\n {n}\n </option>\n ))}\n </select>\n </div>\n <div className=\"flex items-center gap-4 text-body-sm text-text-secondary\">\n <span>\n {dataTotalCount === 0\n ? '0 rows'\n : `${pageOffset + 1}–${Math.min(pageOffset + pageSize, dataTotalCount)} of ${dataTotalCount}`}\n </span>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={pageOffset === 0 || dataLoading}\n onClick={() => void loadDataPage(Math.max(0, pageOffset - pageSize), pageSize)}\n >\n Previous\n </Button>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={!dataHasMore || dataLoading}\n onClick={() => void loadDataPage(pageOffset + pageSize, pageSize)}\n >\n Next\n </Button>\n </div>\n </div>\n )}\n </CardContent>\n )}\n\n {!showData && (\n <CardContent className=\"text-center p-8\">\n <Database className=\"w-10 h-10 text-text-tertiary mx-auto mb-4\" />\n <p className=\"text-body-sm text-text-secondary\">Click &quot;Show Data&quot; to preview the cached data.</p>\n </CardContent>\n )}\n </Card>\n\n {/* Export DataSink Dialog */}\n <ExportDataSinkDialog\n isOpen={isExportDialogOpen}\n dataSinkId={dataSinkId || null}\n dataSinkName={dataSink.name || dataSink.key}\n onClose={handleExportClose}\n />\n </div>\n );\n});\n\nexport default DataSinkViewPage;\n"],"mappings":"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"}
@@ -228,9 +228,9 @@ var _ = {
228
228
  children: /* @__PURE__ */ g("div", {
229
229
  className: "text-center",
230
230
  children: [
231
- /* @__PURE__ */ h(o, { className: "w-12 h-12 text-state-error mx-auto mb-spacing-md" }),
231
+ /* @__PURE__ */ h(o, { className: "w-12 h-12 text-state-error mx-auto mb-4" }),
232
232
  /* @__PURE__ */ h("p", {
233
- className: "text-state-error text-body-sm font-medium mb-spacing-sm",
233
+ className: "text-state-error text-body-sm font-medium mb-2",
234
234
  children: "Failed to load Parser"
235
235
  }),
236
236
  /* @__PURE__ */ h("p", {
@@ -247,13 +247,13 @@ var _ = {
247
247
  className: "max-w-7xl mx-auto p-card-padding",
248
248
  children: [
249
249
  /* @__PURE__ */ g("div", {
250
- className: "mb-spacing-lg",
250
+ className: "mb-6",
251
251
  children: [
252
252
  /* @__PURE__ */ g(l, {
253
253
  onClick: He,
254
254
  variant: "ghost",
255
255
  size: "sm",
256
- className: "mb-spacing-md",
256
+ className: "mb-4",
257
257
  children: [/* @__PURE__ */ h(ae, { className: "w-4 h-4 mr-2" }), "Back to Parsers"]
258
258
  }),
259
259
  /* @__PURE__ */ h("h1", {
@@ -261,17 +261,17 @@ var _ = {
261
261
  children: w ? "Edit Parser" : "Create Parser"
262
262
  }),
263
263
  /* @__PURE__ */ h("p", {
264
- className: "text-body-sm text-text-secondary mt-spacing-xs",
264
+ className: "text-body-sm text-text-secondary mt-1",
265
265
  children: w ? "Update the parser configuration and test your changes." : "Transform data using JQ or JSONata expressions."
266
266
  })
267
267
  ]
268
268
  }),
269
269
  /* @__PURE__ */ h(ge, {
270
- className: "mb-spacing-lg",
270
+ className: "mb-6",
271
271
  children: "A parser maps raw input into structured fields using a JQ or JSONata expression. Write the expression here, run it against a sample to confirm the output, and save it once it produces the shape you need — then reuse it across pipelines to normalise any source."
272
272
  }),
273
273
  P && /* @__PURE__ */ g("div", {
274
- className: "mb-spacing-lg p-4 bg-state-error/10 border border-state-error/30 rounded-lg flex items-start gap-3",
274
+ className: "mb-6 p-4 bg-state-error/10 border border-state-error/30 rounded-lg flex items-start gap-3",
275
275
  children: [/* @__PURE__ */ h(o, { className: "w-5 h-5 text-state-error flex-shrink-0 mt-0.5" }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("p", {
276
276
  className: "text-sm font-medium text-state-error",
277
277
  children: "Failed to save Parser"
@@ -711,7 +711,7 @@ var _ = {
711
711
  })
712
712
  ]
713
713
  }), /* @__PURE__ */ g("div", {
714
- className: "flex items-center justify-end gap-spacing-md pt-spacing-lg mt-spacing-lg border-t border-border-default",
714
+ className: "flex items-center justify-end gap-4 pt-6 mt-6 border-t border-border-default",
715
715
  children: [/* @__PURE__ */ h(l, {
716
716
  type: "button",
717
717
  onClick: He,
@@ -1 +1 @@
1
- {"version":3,"file":"ParserBuilderPage.js","names":[],"sources":["../../../src/bigconsole/pages/ParserBuilderPage.tsx"],"sourcesContent":["/**\n * Parser Builder Page\n *\n * Page for creating and editing Parsers with JQ or JSONata expressions.\n * Required fields: consoleId, name, type, expression, inputSinkKey, outputKey\n */\n\nimport { memo, useState, useMemo, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useAppNavigate } from '../contexts';\nimport { useParserOperations } from '../hooks/useParserOperations';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { useDashboardOperations } from '../hooks/useDashboardOperations';\nimport { CacheTTLSelector } from '../components/datasink/CacheTTLSelector';\nimport type { ParserType } from '../types';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\n\n// Lucide icons\nimport {\n ArrowLeft,\n Save,\n Play,\n Loader2,\n AlertCircle,\n CheckCircle2,\n FileCode2,\n Code,\n Settings,\n ChevronDown,\n ChevronRight,\n Eye,\n LayoutDashboard,\n} from 'lucide-react';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface FormState {\n name: string;\n outputKey: string;\n description: string;\n type: ParserType;\n expression: string;\n inputSinkKey: string;\n // `null` represents \"no caching / not set\" (matches Parser.ttl: number | null\n // on the backend and CacheTTLSelector's value prop). Using number-only here\n // would force a collapse to 0/900 that silently loses user intent on edit.\n ttl: number | null;\n}\n\ninterface FormErrors {\n name?: string;\n outputKey?: string;\n expression?: string;\n inputSinkKey?: string;\n ttl?: string;\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\n/**\n * Generate a key from name (slug format)\n */\nfunction generateKey(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .substring(0, 64);\n}\n\n/**\n * Validate form data\n */\nfunction validateForm(form: FormState): FormErrors {\n const errors: FormErrors = {};\n\n if (!form.name.trim()) {\n errors.name = 'Name is required';\n } else if (form.name.length < 2) {\n errors.name = 'Name must be at least 2 characters';\n }\n\n if (!form.outputKey.trim()) {\n errors.outputKey = 'Output key is required';\n } else if (!/^[a-z0-9_]+$/.test(form.outputKey)) {\n errors.outputKey = 'Output key can only contain lowercase letters, numbers, and underscores';\n }\n\n if (!form.expression.trim()) {\n errors.expression = 'Expression is required';\n }\n\n if (!form.inputSinkKey.trim()) {\n errors.inputSinkKey = 'Source DataSink is required';\n }\n\n if (form.ttl != null && form.ttl > 0 && (form.ttl < 30 || form.ttl > 86400)) {\n errors.ttl = 'Cache duration must be between 30 and 86400 seconds';\n }\n\n return errors;\n}\n\n// Parser type descriptions (JQ and JSONata only)\nconst PARSER_TYPE_INFO: Record<\n ParserType,\n { name: string; description: string; placeholder: string; example: string }\n> = {\n jq: {\n name: 'JQ',\n description: 'Lightweight JSON processor - fast and efficient for simple transformations',\n placeholder: '.data | map({name: .name, value: .count})',\n example: '.data | map(.name) - Extract names from data array',\n },\n jsonata: {\n name: 'JSONata',\n description: 'Query and transformation language - supports complex queries and calculations',\n placeholder: '$sum(data[*].value)',\n example: '$sum(data.values) - Calculate sum of values',\n },\n};\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\n// Default console ID for standalone parser creation (must match DataParsersPage)\nconst DEFAULT_CONSOLE_ID = 'default';\n\nexport const ParserBuilderPage = memo(function ParserBuilderPage() {\n const { parserId, consoleId: urlConsoleId } = useParams<{\n parserId?: string;\n consoleId?: string;\n }>();\n const navigate = useAppNavigate();\n\n // State for selected dashboard (when creating from global route)\n const [selectedDashboardId, setSelectedDashboardId] = useState<string>(DEFAULT_CONSOLE_ID);\n\n // Use URL consoleId, or selected dashboard, or fall back to default\n const consoleId = urlConsoleId || selectedDashboardId || DEFAULT_CONSOLE_ID;\n\n const isEdit = parserId !== 'new' && !!parserId;\n\n // Show dashboard selector when NOT on dashboard-scoped route (both create and edit)\n const showDashboardSelector = !urlConsoleId;\n\n // Form state\n const [form, setForm] = useState<FormState>({\n name: '',\n outputKey: '',\n description: '',\n type: 'jq',\n expression: '',\n inputSinkKey: '',\n // 15 minutes (Recommended preset). A 0 default made CacheTTLSelector\n // auto-select \"Custom\" with an invalid value, blocking form submission.\n ttl: 900,\n });\n const [errors, setErrors] = useState<FormErrors>({});\n const [keyManuallyEdited, setKeyManuallyEdited] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n\n // Test panel state\n const [testInput, setTestInput] = useState(\n '{\\n \"data\": [\\n {\"name\": \"Item 1\", \"count\": 10},\\n {\"name\": \"Item 2\", \"count\": 20}\\n ]\\n}'\n );\n const [testOutput, setTestOutput] = useState('');\n const [testError, setTestError] = useState<string | null>(null);\n const [isTesting, setIsTesting] = useState(false);\n\n // Data preview state\n const [showDataPreview, setShowDataPreview] = useState(false);\n const [dataPreview, setDataPreview] = useState<Record<string, unknown> | null>(null);\n const [dataPreviewLoading, setDataPreviewLoading] = useState(false);\n const [dataPreviewError, setDataPreviewError] = useState<string | null>(null);\n\n // Operations\n const { loading, error, parsers, fetchParser, createParser, updateParser, testParserExpression } =\n useParserOperations({\n consoleId: consoleId || '',\n skipFetch: true,\n });\n\n const { dataSinks, fetchDataSinks, getDataSinkData } = useDataSinkOperations({\n skipFetch: true,\n });\n\n // Fetch dashboards for the dashboard selector (only when showing selector)\n const { dashboards, fetchDashboards, loading: dashboardsLoading } = useDashboardOperations();\n\n // Get existing parser if editing\n const existingParser = isEdit ? parsers.find((p) => p.id === parserId) : null;\n\n // Load data on mount\n useEffect(() => {\n fetchDataSinks();\n if (isEdit && parserId) {\n fetchParser(parserId);\n }\n // Fetch dashboards for the selector (only when showing selector)\n if (showDashboardSelector) {\n fetchDashboards();\n }\n }, [isEdit, parserId, fetchParser, fetchDataSinks, showDashboardSelector, fetchDashboards]);\n\n // Populate form with existing data when editing\n useEffect(() => {\n if (existingParser) {\n setForm({\n name: existingParser.name || '',\n outputKey: existingParser.outputKey || '',\n description: existingParser.description || '',\n type: existingParser.type,\n expression: existingParser.expression || '',\n inputSinkKey: existingParser.inputSinkKey || '',\n // Preserve the stored ttl exactly as saved — `null` and `0` both mean\n // \"no caching\" and are legitimate user choices. The BOFF-2241 default\n // (900) applies only to the *initial* Create-form state, never to\n // hydrating an existing parser.\n ttl: existingParser.ttl ?? null,\n });\n setKeyManuallyEdited(true);\n // Set the dashboard scope from existing parser's consoleId\n if (existingParser.consoleId) {\n setSelectedDashboardId(existingParser.consoleId);\n }\n }\n }, [existingParser]);\n\n // Auto-generate key from name\n const handleNameChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setForm((prev) => ({\n ...prev,\n name,\n outputKey: keyManuallyEdited ? prev.outputKey : generateKey(name),\n }));\n setErrors((prev) => ({ ...prev, name: undefined }));\n },\n [keyManuallyEdited]\n );\n\n // Handle output key change\n const handleOutputKeyChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const outputKey = e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '');\n setForm((prev) => ({ ...prev, outputKey }));\n setKeyManuallyEdited(true);\n setErrors((prev) => ({ ...prev, outputKey: undefined }));\n }, []);\n\n // Handle description change\n const handleDescriptionChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setForm((prev) => ({ ...prev, description: e.target.value }));\n }, []);\n\n // Handle type change\n const handleTypeChange = useCallback((type: ParserType) => {\n setForm((prev) => ({ ...prev, type }));\n // Clear test output when type changes\n setTestOutput('');\n setTestError(null);\n }, []);\n\n // Handle expression change\n const handleExpressionChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setForm((prev) => ({ ...prev, expression: e.target.value }));\n setErrors((prev) => ({ ...prev, expression: undefined }));\n }, []);\n\n // Handle input sink change\n const handleInputSinkChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {\n setForm((prev) => ({ ...prev, inputSinkKey: e.target.value }));\n setErrors((prev) => ({ ...prev, inputSinkKey: undefined }));\n }, []);\n\n // Handle TTL change\n const handleTTLChange = useCallback((ttl: number | null) => {\n setForm((prev) => ({ ...prev, ttl }));\n setErrors((prev) => ({ ...prev, ttl: undefined }));\n }, []);\n\n // Handle dashboard selection change\n const handleDashboardChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {\n setSelectedDashboardId(e.target.value);\n }, []);\n\n // Load sample data from selected datasink\n const handleLoadSampleData = useCallback(async () => {\n if (!form.inputSinkKey) return;\n try {\n const result = await getDataSinkData({ key: form.inputSinkKey });\n if (result?.raw) {\n setTestInput(JSON.stringify(result.raw, null, 2));\n }\n } catch (err) {}\n }, [form.inputSinkKey, getDataSinkData]);\n\n // Handle data preview toggle\n const handleToggleDataPreview = useCallback(async () => {\n if (showDataPreview) {\n setShowDataPreview(false);\n return;\n }\n\n if (!form.inputSinkKey) {\n setDataPreviewError('Please select a source DataSink first');\n return;\n }\n\n setShowDataPreview(true);\n setDataPreviewLoading(true);\n setDataPreviewError(null);\n\n try {\n const result = await getDataSinkData({ key: form.inputSinkKey });\n if (result?.raw) {\n setDataPreview(result.raw as Record<string, unknown>);\n } else {\n setDataPreviewError('No data available in selected DataSink');\n }\n } catch (err) {\n setDataPreviewError(err instanceof Error ? err.message : 'Failed to load data');\n } finally {\n setDataPreviewLoading(false);\n }\n }, [showDataPreview, form.inputSinkKey, getDataSinkData]);\n\n // Handle test expression\n const handleTestExpression = useCallback(async () => {\n if (!form.expression.trim()) {\n setTestError('Expression is required');\n return;\n }\n\n setIsTesting(true);\n setTestError(null);\n setTestOutput('');\n\n try {\n const result = await testParserExpression({\n type: form.type,\n expression: form.expression,\n sampleData: JSON.parse(testInput),\n });\n\n if (result) {\n if (result.success) {\n setTestOutput(JSON.stringify(result.output, null, 2));\n } else {\n setTestError(result.error || 'Test failed');\n }\n }\n } catch (err) {\n setTestError(err instanceof Error ? err.message : 'Test failed');\n } finally {\n setIsTesting(false);\n }\n }, [form.type, form.expression, testInput, testParserExpression]);\n\n // Handle back navigation\n const handleBack = useCallback(() => {\n // Navigate to dashboard-scoped parsers list if consoleId is provided, otherwise global parsers\n if (urlConsoleId) {\n navigate(`/dashboards/${urlConsoleId}/parsers`);\n } else {\n navigate('/parsers');\n }\n }, [navigate, urlConsoleId]);\n\n // Handle form submission\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n setSubmitError(null);\n\n // Validate form\n const validationErrors = validateForm(form);\n if (Object.keys(validationErrors).length > 0) {\n setErrors(validationErrors);\n return;\n }\n\n setIsSaving(true);\n try {\n if (isEdit && existingParser) {\n // Update existing (type is immutable, not included in update)\n const result = await updateParser(\n existingParser.id,\n {\n name: form.name,\n description: form.description || undefined,\n expression: form.expression,\n inputSinkKey: form.inputSinkKey,\n outputKey: form.outputKey,\n // Pass `null` through so the backend distinguishes an explicit\n // \"clear caching\" from an unset field (InputMaybe<Int> = Int | null).\n ttl: form.ttl,\n },\n existingParser.version\n );\n if (!result) {\n throw new Error('Failed to update parser');\n }\n } else {\n // Create new - include all required fields\n const result = await createParser({\n consoleId: consoleId || '',\n name: form.name,\n type: form.type,\n expression: form.expression,\n inputSinkKey: form.inputSinkKey,\n outputKey: form.outputKey,\n description: form.description || undefined,\n ttl: form.ttl || undefined,\n });\n if (!result) {\n throw new Error('Failed to create parser');\n }\n }\n // Navigate to dashboard-scoped parsers list if consoleId is provided, otherwise global parsers\n if (urlConsoleId) {\n navigate(`/dashboards/${urlConsoleId}/parsers`);\n } else {\n navigate(`/parsers`);\n }\n } catch (err) {\n setSubmitError(err instanceof Error ? err.message : 'Failed to save parser');\n } finally {\n setIsSaving(false);\n }\n },\n [form, isEdit, existingParser, createParser, updateParser, navigate, consoleId, urlConsoleId]\n );\n\n // Check if form is valid\n const isFormValid = useMemo(() => {\n return (\n form.name.trim().length >= 2 &&\n form.expression.trim().length > 0 &&\n form.inputSinkKey.trim().length > 0 &&\n form.outputKey.trim().length > 0\n );\n }, [form.name, form.expression, form.inputSinkKey, form.outputKey]);\n\n // Loading state\n if (loading && isEdit) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <Loader2 className=\"w-8 h-8 text-text-secondary animate-spin\" />\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-card-padding\">\n <div className=\"text-center\">\n <AlertCircle className=\"w-12 h-12 text-state-error mx-auto mb-spacing-md\" />\n <p className=\"text-state-error text-body-sm font-medium mb-spacing-sm\">Failed to load Parser</p>\n <p className=\"text-text-tertiary text-body-xs\">{error.message}</p>\n </div>\n </div>\n );\n }\n\n const typeInfo = PARSER_TYPE_INFO[form.type];\n\n return (\n <div className=\"min-h-screen bg-bg-canvas\">\n <div className=\"max-w-7xl mx-auto p-card-padding\">\n {/* Header */}\n <div className=\"mb-spacing-lg\">\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\" className=\"mb-spacing-md\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Back to Parsers\n </Button>\n <h1 className=\"text-heading-h1 font-bold text-text-primary\">{isEdit ? 'Edit Parser' : 'Create Parser'}</h1>\n <p className=\"text-body-sm text-text-secondary mt-spacing-xs\">\n {isEdit\n ? 'Update the parser configuration and test your changes.'\n : 'Transform data using JQ or JSONata expressions.'}\n </p>\n </div>\n\n <PagePurpose className=\"mb-spacing-lg\">\n A parser maps raw input into structured fields using a JQ or JSONata expression. Write the expression here,\n run it against a sample to confirm the output, and save it once it produces the shape you need — then reuse it\n across pipelines to normalise any source.\n </PagePurpose>\n\n {/* Error Alert */}\n {submitError && (\n <div className=\"mb-spacing-lg p-4 bg-state-error/10 border border-state-error/30 rounded-lg flex items-start gap-3\">\n <AlertCircle className=\"w-5 h-5 text-state-error flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-state-error\">Failed to save Parser</p>\n <p className=\"text-xs text-state-error/80 mt-1\">{submitError}</p>\n </div>\n </div>\n )}\n\n <form onSubmit={handleSubmit}>\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Column 1 - Basic Information */}\n <div className=\"space-y-6\">\n <Card>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <FileCode2 className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Basic Information</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Essential details for your parser</p>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {/* Name field */}\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary mb-2\">\n Name <span className=\"text-state-error\">*</span>\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={form.name}\n onChange={handleNameChange}\n placeholder=\"e.g., User Analytics Parser\"\n className={`\n w-full px-3 py-2.5\n text-sm text-text-primary\n bg-bg-surface\n border ${errors.name ? 'border-state-error' : 'border-border-default'}\n rounded-md\n placeholder:text-text-tertiary\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n transition-all\n `}\n />\n {errors.name && <p className=\"text-xs text-state-error mt-1.5\">{errors.name}</p>}\n </div>\n\n {/* Output Key field */}\n <div>\n <label htmlFor=\"outputKey\" className=\"block text-sm font-medium text-text-primary mb-2\">\n Output Key <span className=\"text-state-error\">*</span>\n </label>\n <input\n id=\"outputKey\"\n type=\"text\"\n value={form.outputKey}\n onChange={handleOutputKeyChange}\n placeholder=\"user_analytics_parser\"\n disabled={isEdit}\n className={`\n w-full px-3 py-2.5\n text-sm text-text-primary font-mono\n bg-bg-surface\n border ${errors.outputKey ? 'border-state-error' : 'border-border-default'}\n rounded-md\n placeholder:text-text-tertiary\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n disabled:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-60\n transition-all\n `}\n />\n {errors.outputKey && <p className=\"text-xs text-state-error mt-1.5\">{errors.outputKey}</p>}\n {!isEdit && (\n <p className=\"text-xs text-text-tertiary mt-1.5\">\n Auto-generated from name. Customize if needed.\n </p>\n )}\n </div>\n\n {/* Description field */}\n <div>\n <label htmlFor=\"description\" className=\"block text-sm font-medium text-text-primary mb-2\">\n Description\n </label>\n <textarea\n id=\"description\"\n value={form.description}\n onChange={handleDescriptionChange}\n placeholder=\"Describe what this parser does...\"\n rows={3}\n className={`\n w-full px-3 py-2.5\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n placeholder:text-text-tertiary\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n resize-none\n transition-all\n `}\n />\n </div>\n\n {/* Dashboard Selector - shown when NOT on dashboard-scoped route */}\n {showDashboardSelector && (\n <div>\n <label htmlFor=\"dashboard\" className=\"block text-sm font-medium text-text-primary mb-2\">\n <div className=\"flex items-center gap-2\">\n <LayoutDashboard className=\"w-4 h-4\" />\n Dashboard Scope\n </div>\n </label>\n <select\n id=\"dashboard\"\n value={selectedDashboardId}\n onChange={handleDashboardChange}\n disabled={dashboardsLoading || isEdit}\n className={`\n w-full px-3 py-2.5\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n disabled:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-60\n transition-all\n `}\n >\n <option value=\"default\">Global (available everywhere)</option>\n {dashboards.map((dashboard) => (\n <option key={dashboard.id} value={dashboard.id}>\n {dashboard.name}\n </option>\n ))}\n </select>\n <p className=\"text-xs text-text-tertiary mt-1.5\">\n {isEdit\n ? 'Dashboard scope cannot be changed after creation.'\n : selectedDashboardId === 'default'\n ? 'Global parsers are available across all dashboards.'\n : 'This parser will only be available in the selected dashboard.'}\n </p>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* Column 2 - Parser Configuration — primary emphasis zone */}\n <div className=\"space-y-6\">\n <GlassCard treatment=\"glass\" glow>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Code className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Parser Configuration</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Configure parser type and data source</p>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {/* Parser Type */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-3\">\n Parser Type <span className=\"text-state-error\">*</span>\n </label>\n <div className=\"space-y-2\">\n {(Object.keys(PARSER_TYPE_INFO) as ParserType[]).map((type) => {\n const info = PARSER_TYPE_INFO[type];\n return (\n <label\n key={type}\n className={`\n flex items-start gap-3 p-3\n border rounded-md\n cursor-pointer\n ${\n form.type === type\n ? 'border-action-primary-bg bg-action-primary-bg/5'\n : 'border-border-default hover:bg-bg-sunken'\n }\n transition-colors\n `}\n >\n <input\n type=\"radio\"\n name=\"parserType\"\n value={type}\n checked={form.type === type}\n onChange={() => handleTypeChange(type)}\n className=\"mt-1 w-4 h-4 text-action-primary-bg\"\n />\n <div>\n <span className=\"text-sm font-medium text-text-primary\">{info.name}</span>\n <p className=\"text-xs text-text-secondary mt-0.5\">{info.description}</p>\n </div>\n </label>\n );\n })}\n </div>\n </div>\n\n {/* Source DataSink */}\n <div>\n <label htmlFor=\"inputSinkKey\" className=\"block text-sm font-medium text-text-primary mb-2\">\n Source DataSink <span className=\"text-state-error\">*</span>\n </label>\n <div className=\"flex gap-2\">\n <select\n id=\"inputSinkKey\"\n value={form.inputSinkKey}\n onChange={handleInputSinkChange}\n className={`\n flex-1 px-3 py-2.5\n text-sm text-text-primary\n bg-bg-surface\n border ${errors.inputSinkKey ? 'border-state-error' : 'border-border-default'}\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n transition-all\n `}\n >\n <option value=\"\">Select a DataSink...</option>\n {dataSinks.map((ds) => (\n <option key={ds.id} value={ds.key}>\n {ds.name || ds.key}\n </option>\n ))}\n </select>\n {form.inputSinkKey && (\n <button\n type=\"button\"\n onClick={handleLoadSampleData}\n className=\"px-3 py-2.5 text-sm font-medium text-action-primary-bg border border-action-primary-bg rounded-md hover:bg-action-primary-bg/10 transition-colors whitespace-nowrap\"\n >\n Load Sample\n </button>\n )}\n </div>\n {errors.inputSinkKey && <p className=\"text-xs text-state-error mt-1.5\">{errors.inputSinkKey}</p>}\n <p className=\"text-xs text-text-tertiary mt-1.5\">\n Select the data source for this parser to transform.\n </p>\n\n {/* Show Data Preview Button */}\n {form.inputSinkKey && (\n <div className=\"mt-3\">\n <button\n type=\"button\"\n onClick={handleToggleDataPreview}\n className=\"inline-flex items-center gap-2 text-sm text-action-primary-bg hover:text-action-primary-bgHover transition-colors\"\n >\n {showDataPreview ? <ChevronDown className=\"w-4 h-4\" /> : <ChevronRight className=\"w-4 h-4\" />}\n <Eye className=\"w-4 h-4\" />\n Show Data Preview\n </button>\n\n {/* Data Preview Panel */}\n {showDataPreview && (\n <div className=\"mt-3 border border-border-default rounded-md overflow-hidden\">\n {dataPreviewLoading ? (\n <div className=\"p-4 flex items-center justify-center\">\n <Loader2 className=\"w-5 h-5 text-text-secondary animate-spin\" />\n <span className=\"ml-2 text-sm text-text-secondary\">Loading data...</span>\n </div>\n ) : dataPreviewError ? (\n <div className=\"p-4 bg-state-error/5\">\n <p className=\"text-sm text-state-error\">{dataPreviewError}</p>\n </div>\n ) : dataPreview ? (\n <pre className=\"p-3 text-xs font-mono bg-bg-sunken text-text-primary overflow-auto max-h-48\">\n {JSON.stringify(dataPreview, null, 2)}\n </pre>\n ) : (\n <div className=\"p-4 text-sm text-text-secondary text-center\">No data available</div>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n\n {/* Expression */}\n <div>\n <label htmlFor=\"expression\" className=\"block text-sm font-medium text-text-primary mb-2\">\n Expression <span className=\"text-state-error\">*</span>\n </label>\n <textarea\n id=\"expression\"\n value={form.expression}\n onChange={handleExpressionChange}\n placeholder={typeInfo.placeholder}\n rows={8}\n className={`\n w-full px-3 py-2.5\n text-sm text-text-primary font-mono\n bg-bg-surface\n border ${errors.expression ? 'border-state-error' : 'border-border-default'}\n rounded-md\n placeholder:text-text-tertiary\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n resize-none\n transition-all\n `}\n />\n {errors.expression && <p className=\"text-xs text-state-error mt-1.5\">{errors.expression}</p>}\n <p className=\"text-xs text-text-tertiary mt-1.5\">Example: {typeInfo.example}</p>\n </div>\n </CardContent>\n </GlassCard>\n </div>\n\n {/* Column 3 - Test Panel & Advanced Settings */}\n <div className=\"space-y-6\">\n {/* Test Panel */}\n <Card>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Play className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Test Expression</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Test your parser with sample data</p>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {/* Test Input */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-2\">Input JSON</label>\n <textarea\n value={testInput}\n onChange={(e) => setTestInput(e.target.value)}\n placeholder='{\"data\": [{\"name\": \"test\", \"value\": 123}]}'\n rows={6}\n className={`\n w-full px-3 py-2.5\n text-xs text-text-primary font-mono leading-relaxed\n bg-bg-surface\n border border-border-default\n rounded-md\n placeholder:text-text-tertiary\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n resize-none\n transition-all\n `}\n />\n <p className=\"text-xs text-text-tertiary mt-1.5\">Paste test data or load from a DataSink above.</p>\n </div>\n\n {/* Test Button */}\n <Button\n type=\"button\"\n onClick={handleTestExpression}\n disabled={isTesting || !form.expression.trim()}\n variant=\"default\"\n className=\"w-full\"\n >\n {isTesting ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n Testing...\n </>\n ) : (\n <>\n <Play className=\"w-4 h-4 mr-2\" />\n Run Test\n </>\n )}\n </Button>\n\n {/* Test Output */}\n {(testOutput || testError) && (\n <div className=\"pt-4 border-t border-border-default\">\n <div className=\"flex items-center justify-between mb-2\">\n <label className=\"block text-sm font-medium text-text-primary\">\n {testError ? 'Error Output' : 'Success Output'}\n </label>\n {!testError ? (\n <span className=\"text-xs px-2 py-1 bg-state-success/10 text-state-success rounded flex items-center gap-1\">\n <CheckCircle2 className=\"w-3 h-3\" /> Passed\n </span>\n ) : (\n <span className=\"text-xs px-2 py-1 bg-state-error/10 text-state-error rounded flex items-center gap-1\">\n <AlertCircle className=\"w-3 h-3\" /> Failed\n </span>\n )}\n </div>\n <pre\n className={`\n w-full px-3 py-2.5\n text-xs font-mono leading-relaxed\n ${testError ? 'bg-state-error/5 text-state-error border-state-error/30' : 'bg-state-success/5 text-text-primary border-state-success/30'}\n border\n rounded-md\n overflow-auto\n max-h-[150px]\n `}\n >\n {testError || testOutput}\n </pre>\n </div>\n )}\n\n {/* Help Text */}\n {!testOutput && !testError && (\n <div className=\"p-3 bg-bg-sunken rounded-md border border-border-default\">\n <p className=\"text-xs text-text-secondary leading-relaxed\">\n <strong>Tip:</strong> Write your expression and click &ldquo;Run Test&rdquo; to see the\n transformation in real-time.\n </p>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Advanced Settings */}\n <Card>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Settings className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Advanced Settings</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Cache configuration</p>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {/* Cache TTL */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-3\">Cache Duration</label>\n <CacheTTLSelector value={form.ttl} onChange={handleTTLChange} error={errors.ttl} />\n <p className=\"text-xs text-text-tertiary mt-2\">\n Select a preset or enter a custom duration (30-86400 seconds).\n </p>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n\n {/* Submit Buttons */}\n <div className=\"flex items-center justify-end gap-spacing-md pt-spacing-lg mt-spacing-lg border-t border-border-default\">\n <Button type=\"button\" onClick={handleBack} variant=\"secondary\">\n Cancel\n </Button>\n <Button type=\"submit\" disabled={!isFormValid || isSaving} variant=\"default\">\n {isSaving ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n Saving...\n </>\n ) : (\n <>\n <Save className=\"w-4 h-4 mr-2\" />\n {isEdit ? 'Save Changes' : 'Create Parser'}\n </>\n )}\n </Button>\n </div>\n </form>\n </div>\n </div>\n );\n});\n\nexport default ParserBuilderPage;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ParserBuilderPage.js","names":[],"sources":["../../../src/bigconsole/pages/ParserBuilderPage.tsx"],"sourcesContent":["/**\n * Parser Builder Page\n *\n * Page for creating and editing Parsers with JQ or JSONata expressions.\n * Required fields: consoleId, name, type, expression, inputSinkKey, outputKey\n */\n\nimport { memo, useState, useMemo, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useAppNavigate } from '../contexts';\nimport { useParserOperations } from '../hooks/useParserOperations';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { useDashboardOperations } from '../hooks/useDashboardOperations';\nimport { CacheTTLSelector } from '../components/datasink/CacheTTLSelector';\nimport type { ParserType } from '../types';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\n\n// Lucide icons\nimport {\n ArrowLeft,\n Save,\n Play,\n Loader2,\n AlertCircle,\n CheckCircle2,\n FileCode2,\n Code,\n Settings,\n ChevronDown,\n ChevronRight,\n Eye,\n LayoutDashboard,\n} from 'lucide-react';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface FormState {\n name: string;\n outputKey: string;\n description: string;\n type: ParserType;\n expression: string;\n inputSinkKey: string;\n // `null` represents \"no caching / not set\" (matches Parser.ttl: number | null\n // on the backend and CacheTTLSelector's value prop). Using number-only here\n // would force a collapse to 0/900 that silently loses user intent on edit.\n ttl: number | null;\n}\n\ninterface FormErrors {\n name?: string;\n outputKey?: string;\n expression?: string;\n inputSinkKey?: string;\n ttl?: string;\n}\n\n// ============================================================================\n// HELPERS\n// ============================================================================\n\n/**\n * Generate a key from name (slug format)\n */\nfunction generateKey(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '_')\n .replace(/^_+|_+$/g, '')\n .substring(0, 64);\n}\n\n/**\n * Validate form data\n */\nfunction validateForm(form: FormState): FormErrors {\n const errors: FormErrors = {};\n\n if (!form.name.trim()) {\n errors.name = 'Name is required';\n } else if (form.name.length < 2) {\n errors.name = 'Name must be at least 2 characters';\n }\n\n if (!form.outputKey.trim()) {\n errors.outputKey = 'Output key is required';\n } else if (!/^[a-z0-9_]+$/.test(form.outputKey)) {\n errors.outputKey = 'Output key can only contain lowercase letters, numbers, and underscores';\n }\n\n if (!form.expression.trim()) {\n errors.expression = 'Expression is required';\n }\n\n if (!form.inputSinkKey.trim()) {\n errors.inputSinkKey = 'Source DataSink is required';\n }\n\n if (form.ttl != null && form.ttl > 0 && (form.ttl < 30 || form.ttl > 86400)) {\n errors.ttl = 'Cache duration must be between 30 and 86400 seconds';\n }\n\n return errors;\n}\n\n// Parser type descriptions (JQ and JSONata only)\nconst PARSER_TYPE_INFO: Record<\n ParserType,\n { name: string; description: string; placeholder: string; example: string }\n> = {\n jq: {\n name: 'JQ',\n description: 'Lightweight JSON processor - fast and efficient for simple transformations',\n placeholder: '.data | map({name: .name, value: .count})',\n example: '.data | map(.name) - Extract names from data array',\n },\n jsonata: {\n name: 'JSONata',\n description: 'Query and transformation language - supports complex queries and calculations',\n placeholder: '$sum(data[*].value)',\n example: '$sum(data.values) - Calculate sum of values',\n },\n};\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\n// Default console ID for standalone parser creation (must match DataParsersPage)\nconst DEFAULT_CONSOLE_ID = 'default';\n\nexport const ParserBuilderPage = memo(function ParserBuilderPage() {\n const { parserId, consoleId: urlConsoleId } = useParams<{\n parserId?: string;\n consoleId?: string;\n }>();\n const navigate = useAppNavigate();\n\n // State for selected dashboard (when creating from global route)\n const [selectedDashboardId, setSelectedDashboardId] = useState<string>(DEFAULT_CONSOLE_ID);\n\n // Use URL consoleId, or selected dashboard, or fall back to default\n const consoleId = urlConsoleId || selectedDashboardId || DEFAULT_CONSOLE_ID;\n\n const isEdit = parserId !== 'new' && !!parserId;\n\n // Show dashboard selector when NOT on dashboard-scoped route (both create and edit)\n const showDashboardSelector = !urlConsoleId;\n\n // Form state\n const [form, setForm] = useState<FormState>({\n name: '',\n outputKey: '',\n description: '',\n type: 'jq',\n expression: '',\n inputSinkKey: '',\n // 15 minutes (Recommended preset). A 0 default made CacheTTLSelector\n // auto-select \"Custom\" with an invalid value, blocking form submission.\n ttl: 900,\n });\n const [errors, setErrors] = useState<FormErrors>({});\n const [keyManuallyEdited, setKeyManuallyEdited] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n\n // Test panel state\n const [testInput, setTestInput] = useState(\n '{\\n \"data\": [\\n {\"name\": \"Item 1\", \"count\": 10},\\n {\"name\": \"Item 2\", \"count\": 20}\\n ]\\n}'\n );\n const [testOutput, setTestOutput] = useState('');\n const [testError, setTestError] = useState<string | null>(null);\n const [isTesting, setIsTesting] = useState(false);\n\n // Data preview state\n const [showDataPreview, setShowDataPreview] = useState(false);\n const [dataPreview, setDataPreview] = useState<Record<string, unknown> | null>(null);\n const [dataPreviewLoading, setDataPreviewLoading] = useState(false);\n const [dataPreviewError, setDataPreviewError] = useState<string | null>(null);\n\n // Operations\n const { loading, error, parsers, fetchParser, createParser, updateParser, testParserExpression } =\n useParserOperations({\n consoleId: consoleId || '',\n skipFetch: true,\n });\n\n const { dataSinks, fetchDataSinks, getDataSinkData } = useDataSinkOperations({\n skipFetch: true,\n });\n\n // Fetch dashboards for the dashboard selector (only when showing selector)\n const { dashboards, fetchDashboards, loading: dashboardsLoading } = useDashboardOperations();\n\n // Get existing parser if editing\n const existingParser = isEdit ? parsers.find((p) => p.id === parserId) : null;\n\n // Load data on mount\n useEffect(() => {\n fetchDataSinks();\n if (isEdit && parserId) {\n fetchParser(parserId);\n }\n // Fetch dashboards for the selector (only when showing selector)\n if (showDashboardSelector) {\n fetchDashboards();\n }\n }, [isEdit, parserId, fetchParser, fetchDataSinks, showDashboardSelector, fetchDashboards]);\n\n // Populate form with existing data when editing\n useEffect(() => {\n if (existingParser) {\n setForm({\n name: existingParser.name || '',\n outputKey: existingParser.outputKey || '',\n description: existingParser.description || '',\n type: existingParser.type,\n expression: existingParser.expression || '',\n inputSinkKey: existingParser.inputSinkKey || '',\n // Preserve the stored ttl exactly as saved — `null` and `0` both mean\n // \"no caching\" and are legitimate user choices. The BOFF-2241 default\n // (900) applies only to the *initial* Create-form state, never to\n // hydrating an existing parser.\n ttl: existingParser.ttl ?? null,\n });\n setKeyManuallyEdited(true);\n // Set the dashboard scope from existing parser's consoleId\n if (existingParser.consoleId) {\n setSelectedDashboardId(existingParser.consoleId);\n }\n }\n }, [existingParser]);\n\n // Auto-generate key from name\n const handleNameChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setForm((prev) => ({\n ...prev,\n name,\n outputKey: keyManuallyEdited ? prev.outputKey : generateKey(name),\n }));\n setErrors((prev) => ({ ...prev, name: undefined }));\n },\n [keyManuallyEdited]\n );\n\n // Handle output key change\n const handleOutputKeyChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const outputKey = e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, '');\n setForm((prev) => ({ ...prev, outputKey }));\n setKeyManuallyEdited(true);\n setErrors((prev) => ({ ...prev, outputKey: undefined }));\n }, []);\n\n // Handle description change\n const handleDescriptionChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setForm((prev) => ({ ...prev, description: e.target.value }));\n }, []);\n\n // Handle type change\n const handleTypeChange = useCallback((type: ParserType) => {\n setForm((prev) => ({ ...prev, type }));\n // Clear test output when type changes\n setTestOutput('');\n setTestError(null);\n }, []);\n\n // Handle expression change\n const handleExpressionChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setForm((prev) => ({ ...prev, expression: e.target.value }));\n setErrors((prev) => ({ ...prev, expression: undefined }));\n }, []);\n\n // Handle input sink change\n const handleInputSinkChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {\n setForm((prev) => ({ ...prev, inputSinkKey: e.target.value }));\n setErrors((prev) => ({ ...prev, inputSinkKey: undefined }));\n }, []);\n\n // Handle TTL change\n const handleTTLChange = useCallback((ttl: number | null) => {\n setForm((prev) => ({ ...prev, ttl }));\n setErrors((prev) => ({ ...prev, ttl: undefined }));\n }, []);\n\n // Handle dashboard selection change\n const handleDashboardChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {\n setSelectedDashboardId(e.target.value);\n }, []);\n\n // Load sample data from selected datasink\n const handleLoadSampleData = useCallback(async () => {\n if (!form.inputSinkKey) return;\n try {\n const result = await getDataSinkData({ key: form.inputSinkKey });\n if (result?.raw) {\n setTestInput(JSON.stringify(result.raw, null, 2));\n }\n } catch (err) {}\n }, [form.inputSinkKey, getDataSinkData]);\n\n // Handle data preview toggle\n const handleToggleDataPreview = useCallback(async () => {\n if (showDataPreview) {\n setShowDataPreview(false);\n return;\n }\n\n if (!form.inputSinkKey) {\n setDataPreviewError('Please select a source DataSink first');\n return;\n }\n\n setShowDataPreview(true);\n setDataPreviewLoading(true);\n setDataPreviewError(null);\n\n try {\n const result = await getDataSinkData({ key: form.inputSinkKey });\n if (result?.raw) {\n setDataPreview(result.raw as Record<string, unknown>);\n } else {\n setDataPreviewError('No data available in selected DataSink');\n }\n } catch (err) {\n setDataPreviewError(err instanceof Error ? err.message : 'Failed to load data');\n } finally {\n setDataPreviewLoading(false);\n }\n }, [showDataPreview, form.inputSinkKey, getDataSinkData]);\n\n // Handle test expression\n const handleTestExpression = useCallback(async () => {\n if (!form.expression.trim()) {\n setTestError('Expression is required');\n return;\n }\n\n setIsTesting(true);\n setTestError(null);\n setTestOutput('');\n\n try {\n const result = await testParserExpression({\n type: form.type,\n expression: form.expression,\n sampleData: JSON.parse(testInput),\n });\n\n if (result) {\n if (result.success) {\n setTestOutput(JSON.stringify(result.output, null, 2));\n } else {\n setTestError(result.error || 'Test failed');\n }\n }\n } catch (err) {\n setTestError(err instanceof Error ? err.message : 'Test failed');\n } finally {\n setIsTesting(false);\n }\n }, [form.type, form.expression, testInput, testParserExpression]);\n\n // Handle back navigation\n const handleBack = useCallback(() => {\n // Navigate to dashboard-scoped parsers list if consoleId is provided, otherwise global parsers\n if (urlConsoleId) {\n navigate(`/dashboards/${urlConsoleId}/parsers`);\n } else {\n navigate('/parsers');\n }\n }, [navigate, urlConsoleId]);\n\n // Handle form submission\n const handleSubmit = useCallback(\n async (e: React.FormEvent) => {\n e.preventDefault();\n setSubmitError(null);\n\n // Validate form\n const validationErrors = validateForm(form);\n if (Object.keys(validationErrors).length > 0) {\n setErrors(validationErrors);\n return;\n }\n\n setIsSaving(true);\n try {\n if (isEdit && existingParser) {\n // Update existing (type is immutable, not included in update)\n const result = await updateParser(\n existingParser.id,\n {\n name: form.name,\n description: form.description || undefined,\n expression: form.expression,\n inputSinkKey: form.inputSinkKey,\n outputKey: form.outputKey,\n // Pass `null` through so the backend distinguishes an explicit\n // \"clear caching\" from an unset field (InputMaybe<Int> = Int | null).\n ttl: form.ttl,\n },\n existingParser.version\n );\n if (!result) {\n throw new Error('Failed to update parser');\n }\n } else {\n // Create new - include all required fields\n const result = await createParser({\n consoleId: consoleId || '',\n name: form.name,\n type: form.type,\n expression: form.expression,\n inputSinkKey: form.inputSinkKey,\n outputKey: form.outputKey,\n description: form.description || undefined,\n ttl: form.ttl || undefined,\n });\n if (!result) {\n throw new Error('Failed to create parser');\n }\n }\n // Navigate to dashboard-scoped parsers list if consoleId is provided, otherwise global parsers\n if (urlConsoleId) {\n navigate(`/dashboards/${urlConsoleId}/parsers`);\n } else {\n navigate(`/parsers`);\n }\n } catch (err) {\n setSubmitError(err instanceof Error ? err.message : 'Failed to save parser');\n } finally {\n setIsSaving(false);\n }\n },\n [form, isEdit, existingParser, createParser, updateParser, navigate, consoleId, urlConsoleId]\n );\n\n // Check if form is valid\n const isFormValid = useMemo(() => {\n return (\n form.name.trim().length >= 2 &&\n form.expression.trim().length > 0 &&\n form.inputSinkKey.trim().length > 0 &&\n form.outputKey.trim().length > 0\n );\n }, [form.name, form.expression, form.inputSinkKey, form.outputKey]);\n\n // Loading state\n if (loading && isEdit) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <Loader2 className=\"w-8 h-8 text-text-secondary animate-spin\" />\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-card-padding\">\n <div className=\"text-center\">\n <AlertCircle className=\"w-12 h-12 text-state-error mx-auto mb-4\" />\n <p className=\"text-state-error text-body-sm font-medium mb-2\">Failed to load Parser</p>\n <p className=\"text-text-tertiary text-body-xs\">{error.message}</p>\n </div>\n </div>\n );\n }\n\n const typeInfo = PARSER_TYPE_INFO[form.type];\n\n return (\n <div className=\"min-h-screen bg-bg-canvas\">\n <div className=\"max-w-7xl mx-auto p-card-padding\">\n {/* Header */}\n <div className=\"mb-6\">\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\" className=\"mb-4\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Back to Parsers\n </Button>\n <h1 className=\"text-heading-h1 font-bold text-text-primary\">{isEdit ? 'Edit Parser' : 'Create Parser'}</h1>\n <p className=\"text-body-sm text-text-secondary mt-1\">\n {isEdit\n ? 'Update the parser configuration and test your changes.'\n : 'Transform data using JQ or JSONata expressions.'}\n </p>\n </div>\n\n <PagePurpose className=\"mb-6\">\n A parser maps raw input into structured fields using a JQ or JSONata expression. Write the expression here,\n run it against a sample to confirm the output, and save it once it produces the shape you need — then reuse it\n across pipelines to normalise any source.\n </PagePurpose>\n\n {/* Error Alert */}\n {submitError && (\n <div className=\"mb-6 p-4 bg-state-error/10 border border-state-error/30 rounded-lg flex items-start gap-3\">\n <AlertCircle className=\"w-5 h-5 text-state-error flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-state-error\">Failed to save Parser</p>\n <p className=\"text-xs text-state-error/80 mt-1\">{submitError}</p>\n </div>\n </div>\n )}\n\n <form onSubmit={handleSubmit}>\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Column 1 - Basic Information */}\n <div className=\"space-y-6\">\n <Card>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <FileCode2 className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Basic Information</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Essential details for your parser</p>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {/* Name field */}\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary mb-2\">\n Name <span className=\"text-state-error\">*</span>\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={form.name}\n onChange={handleNameChange}\n placeholder=\"e.g., User Analytics Parser\"\n className={`\n w-full px-3 py-2.5\n text-sm text-text-primary\n bg-bg-surface\n border ${errors.name ? 'border-state-error' : 'border-border-default'}\n rounded-md\n placeholder:text-text-tertiary\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n transition-all\n `}\n />\n {errors.name && <p className=\"text-xs text-state-error mt-1.5\">{errors.name}</p>}\n </div>\n\n {/* Output Key field */}\n <div>\n <label htmlFor=\"outputKey\" className=\"block text-sm font-medium text-text-primary mb-2\">\n Output Key <span className=\"text-state-error\">*</span>\n </label>\n <input\n id=\"outputKey\"\n type=\"text\"\n value={form.outputKey}\n onChange={handleOutputKeyChange}\n placeholder=\"user_analytics_parser\"\n disabled={isEdit}\n className={`\n w-full px-3 py-2.5\n text-sm text-text-primary font-mono\n bg-bg-surface\n border ${errors.outputKey ? 'border-state-error' : 'border-border-default'}\n rounded-md\n placeholder:text-text-tertiary\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n disabled:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-60\n transition-all\n `}\n />\n {errors.outputKey && <p className=\"text-xs text-state-error mt-1.5\">{errors.outputKey}</p>}\n {!isEdit && (\n <p className=\"text-xs text-text-tertiary mt-1.5\">\n Auto-generated from name. Customize if needed.\n </p>\n )}\n </div>\n\n {/* Description field */}\n <div>\n <label htmlFor=\"description\" className=\"block text-sm font-medium text-text-primary mb-2\">\n Description\n </label>\n <textarea\n id=\"description\"\n value={form.description}\n onChange={handleDescriptionChange}\n placeholder=\"Describe what this parser does...\"\n rows={3}\n className={`\n w-full px-3 py-2.5\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n placeholder:text-text-tertiary\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n resize-none\n transition-all\n `}\n />\n </div>\n\n {/* Dashboard Selector - shown when NOT on dashboard-scoped route */}\n {showDashboardSelector && (\n <div>\n <label htmlFor=\"dashboard\" className=\"block text-sm font-medium text-text-primary mb-2\">\n <div className=\"flex items-center gap-2\">\n <LayoutDashboard className=\"w-4 h-4\" />\n Dashboard Scope\n </div>\n </label>\n <select\n id=\"dashboard\"\n value={selectedDashboardId}\n onChange={handleDashboardChange}\n disabled={dashboardsLoading || isEdit}\n className={`\n w-full px-3 py-2.5\n text-sm text-text-primary\n bg-bg-surface\n border border-border-default\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n disabled:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-60\n transition-all\n `}\n >\n <option value=\"default\">Global (available everywhere)</option>\n {dashboards.map((dashboard) => (\n <option key={dashboard.id} value={dashboard.id}>\n {dashboard.name}\n </option>\n ))}\n </select>\n <p className=\"text-xs text-text-tertiary mt-1.5\">\n {isEdit\n ? 'Dashboard scope cannot be changed after creation.'\n : selectedDashboardId === 'default'\n ? 'Global parsers are available across all dashboards.'\n : 'This parser will only be available in the selected dashboard.'}\n </p>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n\n {/* Column 2 - Parser Configuration — primary emphasis zone */}\n <div className=\"space-y-6\">\n <GlassCard treatment=\"glass\" glow>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Code className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Parser Configuration</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Configure parser type and data source</p>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {/* Parser Type */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-3\">\n Parser Type <span className=\"text-state-error\">*</span>\n </label>\n <div className=\"space-y-2\">\n {(Object.keys(PARSER_TYPE_INFO) as ParserType[]).map((type) => {\n const info = PARSER_TYPE_INFO[type];\n return (\n <label\n key={type}\n className={`\n flex items-start gap-3 p-3\n border rounded-md\n cursor-pointer\n ${\n form.type === type\n ? 'border-action-primary-bg bg-action-primary-bg/5'\n : 'border-border-default hover:bg-bg-sunken'\n }\n transition-colors\n `}\n >\n <input\n type=\"radio\"\n name=\"parserType\"\n value={type}\n checked={form.type === type}\n onChange={() => handleTypeChange(type)}\n className=\"mt-1 w-4 h-4 text-action-primary-bg\"\n />\n <div>\n <span className=\"text-sm font-medium text-text-primary\">{info.name}</span>\n <p className=\"text-xs text-text-secondary mt-0.5\">{info.description}</p>\n </div>\n </label>\n );\n })}\n </div>\n </div>\n\n {/* Source DataSink */}\n <div>\n <label htmlFor=\"inputSinkKey\" className=\"block text-sm font-medium text-text-primary mb-2\">\n Source DataSink <span className=\"text-state-error\">*</span>\n </label>\n <div className=\"flex gap-2\">\n <select\n id=\"inputSinkKey\"\n value={form.inputSinkKey}\n onChange={handleInputSinkChange}\n className={`\n flex-1 px-3 py-2.5\n text-sm text-text-primary\n bg-bg-surface\n border ${errors.inputSinkKey ? 'border-state-error' : 'border-border-default'}\n rounded-md\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n transition-all\n `}\n >\n <option value=\"\">Select a DataSink...</option>\n {dataSinks.map((ds) => (\n <option key={ds.id} value={ds.key}>\n {ds.name || ds.key}\n </option>\n ))}\n </select>\n {form.inputSinkKey && (\n <button\n type=\"button\"\n onClick={handleLoadSampleData}\n className=\"px-3 py-2.5 text-sm font-medium text-action-primary-bg border border-action-primary-bg rounded-md hover:bg-action-primary-bg/10 transition-colors whitespace-nowrap\"\n >\n Load Sample\n </button>\n )}\n </div>\n {errors.inputSinkKey && <p className=\"text-xs text-state-error mt-1.5\">{errors.inputSinkKey}</p>}\n <p className=\"text-xs text-text-tertiary mt-1.5\">\n Select the data source for this parser to transform.\n </p>\n\n {/* Show Data Preview Button */}\n {form.inputSinkKey && (\n <div className=\"mt-3\">\n <button\n type=\"button\"\n onClick={handleToggleDataPreview}\n className=\"inline-flex items-center gap-2 text-sm text-action-primary-bg hover:text-action-primary-bgHover transition-colors\"\n >\n {showDataPreview ? <ChevronDown className=\"w-4 h-4\" /> : <ChevronRight className=\"w-4 h-4\" />}\n <Eye className=\"w-4 h-4\" />\n Show Data Preview\n </button>\n\n {/* Data Preview Panel */}\n {showDataPreview && (\n <div className=\"mt-3 border border-border-default rounded-md overflow-hidden\">\n {dataPreviewLoading ? (\n <div className=\"p-4 flex items-center justify-center\">\n <Loader2 className=\"w-5 h-5 text-text-secondary animate-spin\" />\n <span className=\"ml-2 text-sm text-text-secondary\">Loading data...</span>\n </div>\n ) : dataPreviewError ? (\n <div className=\"p-4 bg-state-error/5\">\n <p className=\"text-sm text-state-error\">{dataPreviewError}</p>\n </div>\n ) : dataPreview ? (\n <pre className=\"p-3 text-xs font-mono bg-bg-sunken text-text-primary overflow-auto max-h-48\">\n {JSON.stringify(dataPreview, null, 2)}\n </pre>\n ) : (\n <div className=\"p-4 text-sm text-text-secondary text-center\">No data available</div>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n\n {/* Expression */}\n <div>\n <label htmlFor=\"expression\" className=\"block text-sm font-medium text-text-primary mb-2\">\n Expression <span className=\"text-state-error\">*</span>\n </label>\n <textarea\n id=\"expression\"\n value={form.expression}\n onChange={handleExpressionChange}\n placeholder={typeInfo.placeholder}\n rows={8}\n className={`\n w-full px-3 py-2.5\n text-sm text-text-primary font-mono\n bg-bg-surface\n border ${errors.expression ? 'border-state-error' : 'border-border-default'}\n rounded-md\n placeholder:text-text-tertiary\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n resize-none\n transition-all\n `}\n />\n {errors.expression && <p className=\"text-xs text-state-error mt-1.5\">{errors.expression}</p>}\n <p className=\"text-xs text-text-tertiary mt-1.5\">Example: {typeInfo.example}</p>\n </div>\n </CardContent>\n </GlassCard>\n </div>\n\n {/* Column 3 - Test Panel & Advanced Settings */}\n <div className=\"space-y-6\">\n {/* Test Panel */}\n <Card>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Play className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Test Expression</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Test your parser with sample data</p>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {/* Test Input */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-2\">Input JSON</label>\n <textarea\n value={testInput}\n onChange={(e) => setTestInput(e.target.value)}\n placeholder='{\"data\": [{\"name\": \"test\", \"value\": 123}]}'\n rows={6}\n className={`\n w-full px-3 py-2.5\n text-xs text-text-primary font-mono leading-relaxed\n bg-bg-surface\n border border-border-default\n rounded-md\n placeholder:text-text-tertiary\n focus:outline-none focus:ring-2 focus:ring-action-primary-bg\n resize-none\n transition-all\n `}\n />\n <p className=\"text-xs text-text-tertiary mt-1.5\">Paste test data or load from a DataSink above.</p>\n </div>\n\n {/* Test Button */}\n <Button\n type=\"button\"\n onClick={handleTestExpression}\n disabled={isTesting || !form.expression.trim()}\n variant=\"default\"\n className=\"w-full\"\n >\n {isTesting ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n Testing...\n </>\n ) : (\n <>\n <Play className=\"w-4 h-4 mr-2\" />\n Run Test\n </>\n )}\n </Button>\n\n {/* Test Output */}\n {(testOutput || testError) && (\n <div className=\"pt-4 border-t border-border-default\">\n <div className=\"flex items-center justify-between mb-2\">\n <label className=\"block text-sm font-medium text-text-primary\">\n {testError ? 'Error Output' : 'Success Output'}\n </label>\n {!testError ? (\n <span className=\"text-xs px-2 py-1 bg-state-success/10 text-state-success rounded flex items-center gap-1\">\n <CheckCircle2 className=\"w-3 h-3\" /> Passed\n </span>\n ) : (\n <span className=\"text-xs px-2 py-1 bg-state-error/10 text-state-error rounded flex items-center gap-1\">\n <AlertCircle className=\"w-3 h-3\" /> Failed\n </span>\n )}\n </div>\n <pre\n className={`\n w-full px-3 py-2.5\n text-xs font-mono leading-relaxed\n ${testError ? 'bg-state-error/5 text-state-error border-state-error/30' : 'bg-state-success/5 text-text-primary border-state-success/30'}\n border\n rounded-md\n overflow-auto\n max-h-[150px]\n `}\n >\n {testError || testOutput}\n </pre>\n </div>\n )}\n\n {/* Help Text */}\n {!testOutput && !testError && (\n <div className=\"p-3 bg-bg-sunken rounded-md border border-border-default\">\n <p className=\"text-xs text-text-secondary leading-relaxed\">\n <strong>Tip:</strong> Write your expression and click &ldquo;Run Test&rdquo; to see the\n transformation in real-time.\n </p>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Advanced Settings */}\n <Card>\n <CardHeader className=\"pb-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-full bg-action-primary-bg/10 flex items-center justify-center\">\n <Settings className=\"w-4 h-4 text-action-primary-bg\" />\n </div>\n <div>\n <CardTitle className=\"text-base\">Advanced Settings</CardTitle>\n <p className=\"text-xs text-text-secondary mt-0.5\">Cache configuration</p>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-5\">\n {/* Cache TTL */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-3\">Cache Duration</label>\n <CacheTTLSelector value={form.ttl} onChange={handleTTLChange} error={errors.ttl} />\n <p className=\"text-xs text-text-tertiary mt-2\">\n Select a preset or enter a custom duration (30-86400 seconds).\n </p>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n\n {/* Submit Buttons */}\n <div className=\"flex items-center justify-end gap-4 pt-6 mt-6 border-t border-border-default\">\n <Button type=\"button\" onClick={handleBack} variant=\"secondary\">\n Cancel\n </Button>\n <Button type=\"submit\" disabled={!isFormValid || isSaving} variant=\"default\">\n {isSaving ? (\n <>\n <Loader2 className=\"w-4 h-4 mr-2 animate-spin\" />\n Saving...\n </>\n ) : (\n <>\n <Save className=\"w-4 h-4 mr-2\" />\n {isEdit ? 'Save Changes' : 'Create Parser'}\n </>\n )}\n </Button>\n </div>\n </form>\n </div>\n </div>\n );\n});\n\nexport default ParserBuilderPage;\n"],"mappings":"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"}
@@ -90,13 +90,13 @@ var j = "default", M = s(function() {
90
90
  children: /* @__PURE__ */ k("div", {
91
91
  className: "text-center",
92
92
  children: [
93
- /* @__PURE__ */ O(f, { className: "w-12 h-12 text-state-error mx-auto mb-spacing-md" }),
93
+ /* @__PURE__ */ O(f, { className: "w-12 h-12 text-state-error mx-auto mb-4" }),
94
94
  /* @__PURE__ */ O("p", {
95
- className: "text-state-error text-body-sm font-medium mb-spacing-sm",
95
+ className: "text-state-error text-body-sm font-medium mb-2",
96
96
  children: "Failed to load Parser"
97
97
  }),
98
98
  /* @__PURE__ */ O("p", {
99
- className: "text-text-tertiary text-body-xs mb-spacing-lg",
99
+ className: "text-text-tertiary text-body-xs mb-6",
100
100
  children: G.message
101
101
  }),
102
102
  /* @__PURE__ */ k(v, {
@@ -108,10 +108,10 @@ var j = "default", M = s(function() {
108
108
  ]
109
109
  })
110
110
  }) : W ? /* @__PURE__ */ k("div", {
111
- className: "p-card-padding space-y-spacing-lg",
111
+ className: "p-card-padding space-y-6",
112
112
  children: [
113
113
  /* @__PURE__ */ k("div", {
114
- className: "flex flex-col sm:flex-row sm:items-start sm:justify-between gap-spacing-md",
114
+ className: "flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4",
115
115
  children: [/* @__PURE__ */ k("div", {
116
116
  className: "flex-1",
117
117
  children: [
@@ -119,11 +119,11 @@ var j = "default", M = s(function() {
119
119
  onClick: Z,
120
120
  variant: "ghost",
121
121
  size: "sm",
122
- className: "mb-spacing-md",
122
+ className: "mb-4",
123
123
  children: [/* @__PURE__ */ O(p, { className: "w-4 h-4 mr-2" }), "Back to Parsers"]
124
124
  }),
125
125
  /* @__PURE__ */ k("div", {
126
- className: "flex items-center gap-spacing-sm flex-wrap",
126
+ className: "flex items-center gap-2 flex-wrap",
127
127
  children: [
128
128
  /* @__PURE__ */ O("h1", {
129
129
  className: "text-heading-h1 font-bold text-text-primary",
@@ -134,12 +134,12 @@ var j = "default", M = s(function() {
134
134
  ]
135
135
  }),
136
136
  W.description && /* @__PURE__ */ O("p", {
137
- className: "text-body-sm text-text-secondary mt-spacing-sm",
137
+ className: "text-body-sm text-text-secondary mt-2",
138
138
  children: W.description
139
139
  })
140
140
  ]
141
141
  }), /* @__PURE__ */ k("div", {
142
- className: "flex items-center gap-spacing-sm flex-wrap",
142
+ className: "flex items-center gap-2 flex-wrap",
143
143
  children: [
144
144
  /* @__PURE__ */ k(v, {
145
145
  onClick: $,
@@ -195,16 +195,16 @@ var j = "default", M = s(function() {
195
195
  ]
196
196
  }),
197
197
  /* @__PURE__ */ k("div", {
198
- className: "grid gap-spacing-lg lg:grid-cols-3",
198
+ className: "grid gap-6 lg:grid-cols-3",
199
199
  children: [/* @__PURE__ */ k("div", {
200
- className: "lg:col-span-2 space-y-spacing-lg",
200
+ className: "lg:col-span-2 space-y-6",
201
201
  children: [/* @__PURE__ */ k(C, {
202
202
  treatment: "glass",
203
203
  glow: !0,
204
204
  children: [/* @__PURE__ */ k(x, {
205
205
  className: "flex flex-row items-center justify-between space-y-0",
206
206
  children: [/* @__PURE__ */ k(S, {
207
- className: "flex items-center gap-spacing-sm",
207
+ className: "flex items-center gap-2",
208
208
  children: [
209
209
  /* @__PURE__ */ O(pe, { className: "w-4 h-4 text-text-secondary" }),
210
210
  "Expression",
@@ -229,7 +229,7 @@ var j = "default", M = s(function() {
229
229
  }), H && /* @__PURE__ */ k(y, { children: [/* @__PURE__ */ k(x, {
230
230
  className: "flex flex-row items-center justify-between space-y-0",
231
231
  children: [/* @__PURE__ */ k(S, {
232
- className: "flex items-center gap-spacing-sm",
232
+ className: "flex items-center gap-2",
233
233
  children: ["Execution Result", /* @__PURE__ */ O(_, {
234
234
  variant: H.success ? "default" : "destructive",
235
235
  children: H.success ? "Success" : "Failed"
@@ -240,17 +240,17 @@ var j = "default", M = s(function() {
240
240
  children: [H.executionTimeMs, "ms"]
241
241
  })]
242
242
  }), /* @__PURE__ */ O(b, { children: H.error ? /* @__PURE__ */ O("div", {
243
- className: "bg-state-error/5 border border-state-error/20 rounded-md p-spacing-md",
243
+ className: "bg-state-error/5 border border-state-error/20 rounded-md p-4",
244
244
  children: /* @__PURE__ */ O("p", {
245
245
  className: "text-body-sm text-state-error font-mono",
246
246
  children: H.error
247
247
  })
248
248
  }) : /* @__PURE__ */ O("pre", {
249
- className: "text-body-sm text-text-primary font-mono bg-bg-sunken rounded-md p-spacing-md overflow-auto max-h-[300px]",
249
+ className: "text-body-sm text-text-primary font-mono bg-bg-sunken rounded-md p-4 overflow-auto max-h-[300px]",
250
250
  children: JSON.stringify(H.output, null, 2)
251
251
  }) })] })]
252
252
  }), /* @__PURE__ */ k("div", {
253
- className: "space-y-spacing-lg",
253
+ className: "space-y-6",
254
254
  children: [
255
255
  /* @__PURE__ */ k(y, { children: [/* @__PURE__ */ O(x, { children: /* @__PURE__ */ O(S, { children: "Data Flow" }) }), /* @__PURE__ */ O(b, { children: /* @__PURE__ */ k("div", {
256
256
  className: "space-y-3",
@@ -292,7 +292,7 @@ var j = "default", M = s(function() {
292
292
  ]
293
293
  }) })] }),
294
294
  /* @__PURE__ */ k(y, { children: [/* @__PURE__ */ O(x, { children: /* @__PURE__ */ O(S, { children: "Configuration" }) }), /* @__PURE__ */ O(b, { children: /* @__PURE__ */ k("dl", {
295
- className: "space-y-spacing-md text-body-sm",
295
+ className: "space-y-4 text-body-sm",
296
296
  children: [
297
297
  /* @__PURE__ */ k("div", {
298
298
  className: "flex justify-between items-center",
@@ -331,7 +331,7 @@ var j = "default", M = s(function() {
331
331
  ]
332
332
  }) })] }),
333
333
  /* @__PURE__ */ k(y, { children: [/* @__PURE__ */ O(x, { children: /* @__PURE__ */ O(S, { children: "Timestamps" }) }), /* @__PURE__ */ O(b, { children: /* @__PURE__ */ k("dl", {
334
- className: "space-y-spacing-md text-body-sm",
334
+ className: "space-y-4 text-body-sm",
335
335
  children: [/* @__PURE__ */ k("div", {
336
336
  className: "flex justify-between items-center",
337
337
  children: [/* @__PURE__ */ O("dt", {
@@ -365,7 +365,7 @@ var j = "default", M = s(function() {
365
365
  "? This action cannot be undone."
366
366
  ] })] }),
367
367
  /* @__PURE__ */ k("div", {
368
- className: "bg-state-warning/10 border border-state-warning/20 rounded-md p-spacing-md flex items-start gap-spacing-sm",
368
+ className: "bg-state-warning/10 border border-state-warning/20 rounded-md p-4 flex items-start gap-2",
369
369
  children: [/* @__PURE__ */ O(oe, { className: "w-4 h-4 text-state-warning flex-shrink-0 mt-0.5" }), /* @__PURE__ */ O("p", {
370
370
  className: "text-body-xs text-state-warning",
371
371
  children: "Widgets and pipelines using this parser may stop working correctly after deletion."
@@ -1 +1 @@
1
- {"version":3,"file":"ParserViewPage.js","names":[],"sources":["../../../src/bigconsole/pages/ParserViewPage.tsx"],"sourcesContent":["/**\n * Parser View Page\n *\n * Page for viewing Parser details, expression, and test execution.\n */\n\nimport { memo, useState, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeCopyText, nativeImpact, nativeNotify, nativeToast } from '../../utils/nativeBridge';\nimport { formatDistanceToNow } from 'date-fns';\nimport { useParserOperations } from '../hooks/useParserOperations';\nimport { useParserStore } from '../store/parserStore';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { ParserStatusBadge } from '../components/parser/ParserStatusBadge';\nimport { ParserTypeBadge } from '../components/parser/ParserTypeBadge';\nimport { ExpressionEditor } from '../components/parser/ExpressionEditor';\nimport { ExportParserDialog } from '../components/parser/ExportParserDialog';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { GlassCard, IllustratedEmptyState, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@burdenoff/fe-libs/ui';\n\n// Lucide icons\nimport {\n ArrowLeft,\n Edit,\n Play,\n Trash2,\n Copy,\n Check,\n FileCode2,\n Database,\n ArrowDown,\n Loader2,\n AlertCircle,\n AlertTriangle,\n Download,\n} from 'lucide-react';\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\n// Default console ID for standalone parser viewing\nconst DEFAULT_CONSOLE_ID = 'default';\n\nexport const ParserViewPage = memo(function ParserViewPage() {\n const { parserId, consoleId: urlConsoleId } = useParams<{ parserId: string; consoleId?: string }>();\n const navigate = useAppNavigate();\n\n // Use URL consoleId or fall back to default\n const consoleId = urlConsoleId || DEFAULT_CONSOLE_ID;\n\n // Local state\n const [isDeleting, setIsDeleting] = useState(false);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n const [expressionCopied, setExpressionCopied] = useState(false);\n const [isTesting, setIsTesting] = useState(false);\n const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);\n const [testResult, setTestResult] = useState<{\n success: boolean;\n output: unknown;\n error: string | null;\n executionTimeMs: number;\n } | null>(null);\n\n // Store\n const parser = useParserStore((state) => (parserId ? state.parsers.get(parserId) : undefined));\n\n // Operations - use consoleId from URL or default\n const { loading, error, fetchParser, deleteParser, executeParser } = useParserOperations({\n consoleId,\n skipFetch: true,\n });\n\n // DataSink operations for source datasink info\n const { dataSinks, fetchDataSinks } = useDataSinkOperations({ skipFetch: true });\n\n // Load parser on mount - always fetch full data to get expression\n useEffect(() => {\n if (parserId) {\n fetchParser(parserId);\n }\n }, [parserId, fetchParser]);\n\n // Fetch datasinks to show source name\n useEffect(() => {\n fetchDataSinks();\n }, [fetchDataSinks]);\n\n // Get source datasink name\n const sourceDataSink = parser?.inputSinkKey ? dataSinks.find((ds) => ds.key === parser.inputSinkKey) : null;\n\n // Handle back navigation\n const handleBack = useCallback(() => {\n // Navigate to dashboard-scoped parsers list if consoleId is provided\n if (urlConsoleId) {\n navigate(`/dashboards/${urlConsoleId}/parsers`);\n } else {\n navigate(`/parsers`);\n }\n }, [navigate, urlConsoleId]);\n\n // Handle edit\n const handleEdit = useCallback(() => {\n // Navigate to dashboard-scoped parser edit if consoleId is provided\n if (urlConsoleId) {\n navigate(`/dashboards/${urlConsoleId}/parsers/${parserId}/edit`);\n } else {\n navigate(`/parsers/${parserId}/edit`);\n }\n }, [navigate, parserId, urlConsoleId]);\n\n // Handle delete\n const handleDelete = useCallback(async () => {\n if (!parserId) return;\n setIsDeleting(true);\n try {\n const success = await deleteParser(parserId);\n if (success) {\n if (isNative()) void nativeNotify('success');\n // Navigate to dashboard-scoped parsers list if consoleId is provided\n if (urlConsoleId) {\n navigate(`/dashboards/${urlConsoleId}/parsers`);\n } else {\n navigate(`/parsers`);\n }\n } else if (isNative()) {\n void nativeNotify('error');\n }\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n setShowDeleteConfirm(false);\n }\n }, [parserId, deleteParser, navigate, urlConsoleId]);\n\n // Handle copy expression\n const handleCopyExpression = useCallback(async () => {\n if (!parser?.expression) return;\n try {\n await nativeCopyText(parser.expression);\n setExpressionCopied(true);\n if (isNative()) void nativeToast('Expression copied');\n setTimeout(() => setExpressionCopied(false), 2000);\n } catch (err) {}\n }, [parser?.expression]);\n\n // Handle test execution\n const handleTestExecute = useCallback(async () => {\n if (!parserId) return;\n setIsTesting(true);\n setTestResult(null);\n try {\n const result = await executeParser(parserId);\n if (result) {\n setTestResult({\n success: result.success,\n output: result.output,\n error: result.error,\n executionTimeMs: result.executionTimeMs,\n });\n }\n } catch (err) {\n setTestResult({\n success: false,\n output: null,\n error: err instanceof Error ? err.message : 'Execution failed',\n executionTimeMs: 0,\n });\n } finally {\n setIsTesting(false);\n }\n }, [parserId, executeParser]);\n\n // Navigate to source datasink\n const handleViewSourceDataSink = useCallback(() => {\n if (sourceDataSink) {\n navigate(`/datasinks/${sourceDataSink.id}`);\n }\n }, [navigate, sourceDataSink]);\n\n // Handle export\n const handleExport = useCallback(() => {\n setIsExportDialogOpen(true);\n }, []);\n\n const handleExportClose = useCallback(() => {\n setIsExportDialogOpen(false);\n }, []);\n\n // Loading state\n if (loading && !parser) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <Loader2 className=\"w-8 h-8 text-text-secondary animate-spin\" />\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-card-padding\">\n <div className=\"text-center\">\n <AlertCircle className=\"w-12 h-12 text-state-error mx-auto mb-spacing-md\" />\n <p className=\"text-state-error text-body-sm font-medium mb-spacing-sm\">Failed to load Parser</p>\n <p className=\"text-text-tertiary text-body-xs mb-spacing-lg\">{error.message}</p>\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Go back\n </Button>\n </div>\n </div>\n );\n }\n\n // Not found state\n if (!parser) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-card-padding\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"Parser not found\"\n description=\"The Parser you're looking for doesn't exist or has been deleted.\"\n action={\n <Button onClick={handleBack} variant=\"default\" size=\"default\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Go back to Parsers\n </Button>\n }\n />\n </div>\n );\n }\n\n return (\n <div className=\"p-card-padding space-y-spacing-lg\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-spacing-md\">\n <div className=\"flex-1\">\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\" className=\"mb-spacing-md\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Back to Parsers\n </Button>\n <div className=\"flex items-center gap-spacing-sm flex-wrap\">\n <h1 className=\"text-heading-h1 font-bold text-text-primary\">{parser.name || 'Untitled'}</h1>\n <ParserTypeBadge type={parser.type} />\n <ParserStatusBadge status={parser.status} />\n </div>\n {parser.description && <p className=\"text-body-sm text-text-secondary mt-spacing-sm\">{parser.description}</p>}\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-spacing-sm flex-wrap\">\n <Button onClick={handleTestExecute} disabled={isTesting} variant=\"secondary\" size=\"default\">\n <Play className=\"w-4 h-4 mr-2\" />\n {isTesting ? 'Running...' : 'Execute'}\n </Button>\n <Button onClick={handleExport} variant=\"secondary\" size=\"default\">\n <Download className=\"w-4 h-4 mr-2\" />\n Export\n </Button>\n <Button onClick={handleEdit} variant=\"secondary\" size=\"default\">\n <Edit className=\"w-4 h-4 mr-2\" />\n Edit\n </Button>\n <Button onClick={() => setShowDeleteConfirm(true)} disabled={isDeleting} variant=\"destructive\" size=\"default\">\n <Trash2 className=\"w-4 h-4 mr-2\" />\n Delete\n </Button>\n </div>\n </div>\n\n <PagePurpose>\n A parser turns raw input into structured fields using its expression. Review or copy the expression, run it\n against a sample to see the output, then edit it or use it inside a pipeline to shape incoming data.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"bigconsole-parser-view\"\n steps={[\n {\n id: 'execute',\n label: 'Run a test execution',\n description: 'Check the expression produces the fields you expect.',\n onClick: handleTestExecute,\n },\n {\n id: 'edit',\n label: 'Edit the expression',\n description: 'Refine how this parser maps your input.',\n onClick: handleEdit,\n },\n {\n id: 'pipeline',\n label: 'Use it in a pipeline',\n description: 'Chain this parser with a data sink to process data end to end.',\n onClick: () => navigate('/pipelines'),\n },\n ]}\n />\n\n {/* Main Content Grid */}\n <div className=\"grid gap-spacing-lg lg:grid-cols-3\">\n {/* Expression Panel (2/3 width) — primary emphasis zone */}\n <div className=\"lg:col-span-2 space-y-spacing-lg\">\n {/* Expression */}\n <GlassCard treatment=\"glass\" glow>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle className=\"flex items-center gap-spacing-sm\">\n <FileCode2 className=\"w-4 h-4 text-text-secondary\" />\n Expression\n <Badge variant=\"secondary\" className=\"text-xs uppercase\">\n {parser.type}\n </Badge>\n </CardTitle>\n <Button onClick={handleCopyExpression} variant=\"ghost\" size=\"sm\">\n {expressionCopied ? (\n <>\n <Check className=\"w-3.5 h-3.5 text-state-success mr-2\" />\n Copied!\n </>\n ) : (\n <>\n <Copy className=\"w-3.5 h-3.5 mr-2\" />\n Copy\n </>\n )}\n </Button>\n </CardHeader>\n <CardContent>\n <ExpressionEditor value={parser.expression} type={parser.type} readOnly height=\"300px\" />\n </CardContent>\n </GlassCard>\n\n {/* Test Result Panel */}\n {testResult && (\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle className=\"flex items-center gap-spacing-sm\">\n Execution Result\n <Badge variant={testResult.success ? 'default' : 'destructive'}>\n {testResult.success ? 'Success' : 'Failed'}\n </Badge>\n </CardTitle>\n <Badge variant=\"secondary\" className=\"text-xs\">\n {testResult.executionTimeMs}ms\n </Badge>\n </CardHeader>\n <CardContent>\n {testResult.error ? (\n <div className=\"bg-state-error/5 border border-state-error/20 rounded-md p-spacing-md\">\n <p className=\"text-body-sm text-state-error font-mono\">{testResult.error}</p>\n </div>\n ) : (\n <pre className=\"text-body-sm text-text-primary font-mono bg-bg-sunken rounded-md p-spacing-md overflow-auto max-h-[300px]\">\n {JSON.stringify(testResult.output, null, 2)}\n </pre>\n )}\n </CardContent>\n </Card>\n )}\n </div>\n\n {/* Side Panel (1/3 width) */}\n <div className=\"space-y-spacing-lg\">\n {/* Data Flow */}\n <Card>\n <CardHeader>\n <CardTitle>Data Flow</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {/* Source DataSink */}\n <div>\n <label className=\"text-xs font-medium text-text-secondary\">Source DataSink</label>\n {parser.inputSinkKey ? (\n <div className=\"mt-1\">\n <p className=\"text-body-sm text-text-primary font-mono\">{parser.inputSinkKey}</p>\n {sourceDataSink && (\n <Button onClick={handleViewSourceDataSink} variant=\"link\" size=\"sm\" className=\"mt-1 p-0 h-auto\">\n <Database className=\"w-3 h-3 mr-1\" />\n View {sourceDataSink.name || 'DataSink'}\n </Button>\n )}\n </div>\n ) : (\n <p className=\"text-body-sm text-text-tertiary mt-1\">None configured</p>\n )}\n </div>\n\n {/* Arrow */}\n <div className=\"flex items-center justify-center py-1\">\n <ArrowDown className=\"w-4 h-4 text-text-tertiary\" />\n </div>\n\n {/* Output Key */}\n <div>\n <label className=\"text-xs font-medium text-text-secondary\">Output Key</label>\n <p className=\"text-body-sm text-text-primary font-mono mt-1\">\n {parser.outputKey || 'Not configured'}\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* Configuration */}\n <Card>\n <CardHeader>\n <CardTitle>Configuration</CardTitle>\n </CardHeader>\n <CardContent>\n <dl className=\"space-y-spacing-md text-body-sm\">\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Status</dt>\n <dd>\n <ParserStatusBadge status={parser.status} />\n </dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Type</dt>\n <dd>\n <ParserTypeBadge type={parser.type} />\n </dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Cache TTL</dt>\n <dd className=\"text-text-primary font-medium\">{parser.ttl ? `${parser.ttl}s` : 'No caching'}</dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Version</dt>\n <dd className=\"text-text-primary font-medium\">{parser.version || 1}</dd>\n </div>\n </dl>\n </CardContent>\n </Card>\n\n {/* Timestamps */}\n <Card>\n <CardHeader>\n <CardTitle>Timestamps</CardTitle>\n </CardHeader>\n <CardContent>\n <dl className=\"space-y-spacing-md text-body-sm\">\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Created</dt>\n <dd className=\"text-text-primary font-medium\">\n {parser.createdAt\n ? formatDistanceToNow(new Date(parser.createdAt), { addSuffix: true })\n : 'Unknown'}\n </dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Updated</dt>\n <dd className=\"text-text-primary font-medium\">\n {parser.updatedAt\n ? formatDistanceToNow(new Date(parser.updatedAt), { addSuffix: true })\n : 'Unknown'}\n </dd>\n </div>\n </dl>\n </CardContent>\n </Card>\n </div>\n </div>\n\n {/* Delete Confirmation Dialog */}\n <Dialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Delete Parser</DialogTitle>\n <DialogDescription>\n Are you sure you want to delete <strong>{parser.name}</strong>? This action cannot be undone.\n </DialogDescription>\n </DialogHeader>\n <div className=\"bg-state-warning/10 border border-state-warning/20 rounded-md p-spacing-md flex items-start gap-spacing-sm\">\n <AlertTriangle className=\"w-4 h-4 text-state-warning flex-shrink-0 mt-0.5\" />\n <p className=\"text-body-xs text-state-warning\">\n Widgets and pipelines using this parser may stop working correctly after deletion.\n </p>\n </div>\n <DialogFooter>\n <Button onClick={() => setShowDeleteConfirm(false)} variant=\"secondary\">\n Cancel\n </Button>\n <Button\n onClick={() => {\n nativeImpact('medium');\n void handleDelete();\n }}\n disabled={isDeleting}\n variant=\"destructive\"\n >\n {isDeleting ? 'Deleting...' : 'Delete Parser'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* Export Parser Dialog */}\n <ExportParserDialog\n isOpen={isExportDialogOpen}\n parserId={parserId || null}\n parserName={parser.name}\n onClose={handleExportClose}\n />\n </div>\n );\n});\n\nexport default ParserViewPage;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ParserViewPage.js","names":[],"sources":["../../../src/bigconsole/pages/ParserViewPage.tsx"],"sourcesContent":["/**\n * Parser View Page\n *\n * Page for viewing Parser details, expression, and test execution.\n */\n\nimport { memo, useState, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeCopyText, nativeImpact, nativeNotify, nativeToast } from '../../utils/nativeBridge';\nimport { formatDistanceToNow } from 'date-fns';\nimport { useParserOperations } from '../hooks/useParserOperations';\nimport { useParserStore } from '../store/parserStore';\nimport { useDataSinkOperations } from '../hooks/useDataSinkOperations';\nimport { ParserStatusBadge } from '../components/parser/ParserStatusBadge';\nimport { ParserTypeBadge } from '../components/parser/ParserTypeBadge';\nimport { ExpressionEditor } from '../components/parser/ExpressionEditor';\nimport { ExportParserDialog } from '../components/parser/ExportParserDialog';\n\n// fe-libs UI components\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { GlassCard, IllustratedEmptyState, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@burdenoff/fe-libs/ui';\n\n// Lucide icons\nimport {\n ArrowLeft,\n Edit,\n Play,\n Trash2,\n Copy,\n Check,\n FileCode2,\n Database,\n ArrowDown,\n Loader2,\n AlertCircle,\n AlertTriangle,\n Download,\n} from 'lucide-react';\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\n// Default console ID for standalone parser viewing\nconst DEFAULT_CONSOLE_ID = 'default';\n\nexport const ParserViewPage = memo(function ParserViewPage() {\n const { parserId, consoleId: urlConsoleId } = useParams<{ parserId: string; consoleId?: string }>();\n const navigate = useAppNavigate();\n\n // Use URL consoleId or fall back to default\n const consoleId = urlConsoleId || DEFAULT_CONSOLE_ID;\n\n // Local state\n const [isDeleting, setIsDeleting] = useState(false);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n const [expressionCopied, setExpressionCopied] = useState(false);\n const [isTesting, setIsTesting] = useState(false);\n const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);\n const [testResult, setTestResult] = useState<{\n success: boolean;\n output: unknown;\n error: string | null;\n executionTimeMs: number;\n } | null>(null);\n\n // Store\n const parser = useParserStore((state) => (parserId ? state.parsers.get(parserId) : undefined));\n\n // Operations - use consoleId from URL or default\n const { loading, error, fetchParser, deleteParser, executeParser } = useParserOperations({\n consoleId,\n skipFetch: true,\n });\n\n // DataSink operations for source datasink info\n const { dataSinks, fetchDataSinks } = useDataSinkOperations({ skipFetch: true });\n\n // Load parser on mount - always fetch full data to get expression\n useEffect(() => {\n if (parserId) {\n fetchParser(parserId);\n }\n }, [parserId, fetchParser]);\n\n // Fetch datasinks to show source name\n useEffect(() => {\n fetchDataSinks();\n }, [fetchDataSinks]);\n\n // Get source datasink name\n const sourceDataSink = parser?.inputSinkKey ? dataSinks.find((ds) => ds.key === parser.inputSinkKey) : null;\n\n // Handle back navigation\n const handleBack = useCallback(() => {\n // Navigate to dashboard-scoped parsers list if consoleId is provided\n if (urlConsoleId) {\n navigate(`/dashboards/${urlConsoleId}/parsers`);\n } else {\n navigate(`/parsers`);\n }\n }, [navigate, urlConsoleId]);\n\n // Handle edit\n const handleEdit = useCallback(() => {\n // Navigate to dashboard-scoped parser edit if consoleId is provided\n if (urlConsoleId) {\n navigate(`/dashboards/${urlConsoleId}/parsers/${parserId}/edit`);\n } else {\n navigate(`/parsers/${parserId}/edit`);\n }\n }, [navigate, parserId, urlConsoleId]);\n\n // Handle delete\n const handleDelete = useCallback(async () => {\n if (!parserId) return;\n setIsDeleting(true);\n try {\n const success = await deleteParser(parserId);\n if (success) {\n if (isNative()) void nativeNotify('success');\n // Navigate to dashboard-scoped parsers list if consoleId is provided\n if (urlConsoleId) {\n navigate(`/dashboards/${urlConsoleId}/parsers`);\n } else {\n navigate(`/parsers`);\n }\n } else if (isNative()) {\n void nativeNotify('error');\n }\n } catch (err) {\n if (isNative()) void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n setShowDeleteConfirm(false);\n }\n }, [parserId, deleteParser, navigate, urlConsoleId]);\n\n // Handle copy expression\n const handleCopyExpression = useCallback(async () => {\n if (!parser?.expression) return;\n try {\n await nativeCopyText(parser.expression);\n setExpressionCopied(true);\n if (isNative()) void nativeToast('Expression copied');\n setTimeout(() => setExpressionCopied(false), 2000);\n } catch (err) {}\n }, [parser?.expression]);\n\n // Handle test execution\n const handleTestExecute = useCallback(async () => {\n if (!parserId) return;\n setIsTesting(true);\n setTestResult(null);\n try {\n const result = await executeParser(parserId);\n if (result) {\n setTestResult({\n success: result.success,\n output: result.output,\n error: result.error,\n executionTimeMs: result.executionTimeMs,\n });\n }\n } catch (err) {\n setTestResult({\n success: false,\n output: null,\n error: err instanceof Error ? err.message : 'Execution failed',\n executionTimeMs: 0,\n });\n } finally {\n setIsTesting(false);\n }\n }, [parserId, executeParser]);\n\n // Navigate to source datasink\n const handleViewSourceDataSink = useCallback(() => {\n if (sourceDataSink) {\n navigate(`/datasinks/${sourceDataSink.id}`);\n }\n }, [navigate, sourceDataSink]);\n\n // Handle export\n const handleExport = useCallback(() => {\n setIsExportDialogOpen(true);\n }, []);\n\n const handleExportClose = useCallback(() => {\n setIsExportDialogOpen(false);\n }, []);\n\n // Loading state\n if (loading && !parser) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <Loader2 className=\"w-8 h-8 text-text-secondary animate-spin\" />\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-card-padding\">\n <div className=\"text-center\">\n <AlertCircle className=\"w-12 h-12 text-state-error mx-auto mb-4\" />\n <p className=\"text-state-error text-body-sm font-medium mb-2\">Failed to load Parser</p>\n <p className=\"text-text-tertiary text-body-xs mb-6\">{error.message}</p>\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Go back\n </Button>\n </div>\n </div>\n );\n }\n\n // Not found state\n if (!parser) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px] p-card-padding\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"Parser not found\"\n description=\"The Parser you're looking for doesn't exist or has been deleted.\"\n action={\n <Button onClick={handleBack} variant=\"default\" size=\"default\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Go back to Parsers\n </Button>\n }\n />\n </div>\n );\n }\n\n return (\n <div className=\"p-card-padding space-y-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4\">\n <div className=\"flex-1\">\n <Button onClick={handleBack} variant=\"ghost\" size=\"sm\" className=\"mb-4\">\n <ArrowLeft className=\"w-4 h-4 mr-2\" />\n Back to Parsers\n </Button>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h1 className=\"text-heading-h1 font-bold text-text-primary\">{parser.name || 'Untitled'}</h1>\n <ParserTypeBadge type={parser.type} />\n <ParserStatusBadge status={parser.status} />\n </div>\n {parser.description && <p className=\"text-body-sm text-text-secondary mt-2\">{parser.description}</p>}\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-2 flex-wrap\">\n <Button onClick={handleTestExecute} disabled={isTesting} variant=\"secondary\" size=\"default\">\n <Play className=\"w-4 h-4 mr-2\" />\n {isTesting ? 'Running...' : 'Execute'}\n </Button>\n <Button onClick={handleExport} variant=\"secondary\" size=\"default\">\n <Download className=\"w-4 h-4 mr-2\" />\n Export\n </Button>\n <Button onClick={handleEdit} variant=\"secondary\" size=\"default\">\n <Edit className=\"w-4 h-4 mr-2\" />\n Edit\n </Button>\n <Button onClick={() => setShowDeleteConfirm(true)} disabled={isDeleting} variant=\"destructive\" size=\"default\">\n <Trash2 className=\"w-4 h-4 mr-2\" />\n Delete\n </Button>\n </div>\n </div>\n\n <PagePurpose>\n A parser turns raw input into structured fields using its expression. Review or copy the expression, run it\n against a sample to see the output, then edit it or use it inside a pipeline to shape incoming data.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"bigconsole-parser-view\"\n steps={[\n {\n id: 'execute',\n label: 'Run a test execution',\n description: 'Check the expression produces the fields you expect.',\n onClick: handleTestExecute,\n },\n {\n id: 'edit',\n label: 'Edit the expression',\n description: 'Refine how this parser maps your input.',\n onClick: handleEdit,\n },\n {\n id: 'pipeline',\n label: 'Use it in a pipeline',\n description: 'Chain this parser with a data sink to process data end to end.',\n onClick: () => navigate('/pipelines'),\n },\n ]}\n />\n\n {/* Main Content Grid */}\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Expression Panel (2/3 width) — primary emphasis zone */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Expression */}\n <GlassCard treatment=\"glass\" glow>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle className=\"flex items-center gap-2\">\n <FileCode2 className=\"w-4 h-4 text-text-secondary\" />\n Expression\n <Badge variant=\"secondary\" className=\"text-xs uppercase\">\n {parser.type}\n </Badge>\n </CardTitle>\n <Button onClick={handleCopyExpression} variant=\"ghost\" size=\"sm\">\n {expressionCopied ? (\n <>\n <Check className=\"w-3.5 h-3.5 text-state-success mr-2\" />\n Copied!\n </>\n ) : (\n <>\n <Copy className=\"w-3.5 h-3.5 mr-2\" />\n Copy\n </>\n )}\n </Button>\n </CardHeader>\n <CardContent>\n <ExpressionEditor value={parser.expression} type={parser.type} readOnly height=\"300px\" />\n </CardContent>\n </GlassCard>\n\n {/* Test Result Panel */}\n {testResult && (\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle className=\"flex items-center gap-2\">\n Execution Result\n <Badge variant={testResult.success ? 'default' : 'destructive'}>\n {testResult.success ? 'Success' : 'Failed'}\n </Badge>\n </CardTitle>\n <Badge variant=\"secondary\" className=\"text-xs\">\n {testResult.executionTimeMs}ms\n </Badge>\n </CardHeader>\n <CardContent>\n {testResult.error ? (\n <div className=\"bg-state-error/5 border border-state-error/20 rounded-md p-4\">\n <p className=\"text-body-sm text-state-error font-mono\">{testResult.error}</p>\n </div>\n ) : (\n <pre className=\"text-body-sm text-text-primary font-mono bg-bg-sunken rounded-md p-4 overflow-auto max-h-[300px]\">\n {JSON.stringify(testResult.output, null, 2)}\n </pre>\n )}\n </CardContent>\n </Card>\n )}\n </div>\n\n {/* Side Panel (1/3 width) */}\n <div className=\"space-y-6\">\n {/* Data Flow */}\n <Card>\n <CardHeader>\n <CardTitle>Data Flow</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {/* Source DataSink */}\n <div>\n <label className=\"text-xs font-medium text-text-secondary\">Source DataSink</label>\n {parser.inputSinkKey ? (\n <div className=\"mt-1\">\n <p className=\"text-body-sm text-text-primary font-mono\">{parser.inputSinkKey}</p>\n {sourceDataSink && (\n <Button onClick={handleViewSourceDataSink} variant=\"link\" size=\"sm\" className=\"mt-1 p-0 h-auto\">\n <Database className=\"w-3 h-3 mr-1\" />\n View {sourceDataSink.name || 'DataSink'}\n </Button>\n )}\n </div>\n ) : (\n <p className=\"text-body-sm text-text-tertiary mt-1\">None configured</p>\n )}\n </div>\n\n {/* Arrow */}\n <div className=\"flex items-center justify-center py-1\">\n <ArrowDown className=\"w-4 h-4 text-text-tertiary\" />\n </div>\n\n {/* Output Key */}\n <div>\n <label className=\"text-xs font-medium text-text-secondary\">Output Key</label>\n <p className=\"text-body-sm text-text-primary font-mono mt-1\">\n {parser.outputKey || 'Not configured'}\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n\n {/* Configuration */}\n <Card>\n <CardHeader>\n <CardTitle>Configuration</CardTitle>\n </CardHeader>\n <CardContent>\n <dl className=\"space-y-4 text-body-sm\">\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Status</dt>\n <dd>\n <ParserStatusBadge status={parser.status} />\n </dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Type</dt>\n <dd>\n <ParserTypeBadge type={parser.type} />\n </dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Cache TTL</dt>\n <dd className=\"text-text-primary font-medium\">{parser.ttl ? `${parser.ttl}s` : 'No caching'}</dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Version</dt>\n <dd className=\"text-text-primary font-medium\">{parser.version || 1}</dd>\n </div>\n </dl>\n </CardContent>\n </Card>\n\n {/* Timestamps */}\n <Card>\n <CardHeader>\n <CardTitle>Timestamps</CardTitle>\n </CardHeader>\n <CardContent>\n <dl className=\"space-y-4 text-body-sm\">\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Created</dt>\n <dd className=\"text-text-primary font-medium\">\n {parser.createdAt\n ? formatDistanceToNow(new Date(parser.createdAt), { addSuffix: true })\n : 'Unknown'}\n </dd>\n </div>\n <div className=\"flex justify-between items-center\">\n <dt className=\"text-text-secondary\">Updated</dt>\n <dd className=\"text-text-primary font-medium\">\n {parser.updatedAt\n ? formatDistanceToNow(new Date(parser.updatedAt), { addSuffix: true })\n : 'Unknown'}\n </dd>\n </div>\n </dl>\n </CardContent>\n </Card>\n </div>\n </div>\n\n {/* Delete Confirmation Dialog */}\n <Dialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Delete Parser</DialogTitle>\n <DialogDescription>\n Are you sure you want to delete <strong>{parser.name}</strong>? This action cannot be undone.\n </DialogDescription>\n </DialogHeader>\n <div className=\"bg-state-warning/10 border border-state-warning/20 rounded-md p-4 flex items-start gap-2\">\n <AlertTriangle className=\"w-4 h-4 text-state-warning flex-shrink-0 mt-0.5\" />\n <p className=\"text-body-xs text-state-warning\">\n Widgets and pipelines using this parser may stop working correctly after deletion.\n </p>\n </div>\n <DialogFooter>\n <Button onClick={() => setShowDeleteConfirm(false)} variant=\"secondary\">\n Cancel\n </Button>\n <Button\n onClick={() => {\n nativeImpact('medium');\n void handleDelete();\n }}\n disabled={isDeleting}\n variant=\"destructive\"\n >\n {isDeleting ? 'Deleting...' : 'Delete Parser'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n\n {/* Export Parser Dialog */}\n <ExportParserDialog\n isOpen={isExportDialogOpen}\n parserId={parserId || null}\n parserName={parser.name}\n onClose={handleExportClose}\n />\n </div>\n );\n});\n\nexport default ParserViewPage;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-bigconsole",
3
- "version": "2026.717.3",
3
+ "version": "2026.717.4",
4
4
  "description": "BigConsole - AI-powered analytics and dashboard platform",
5
5
  "type": "module",
6
6
  "files": [