@burdenoff/microfe-bigconsole 2026.711.2 → 2026.712.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ImportDataSinkDialog.js","names":[],"sources":["../../../../src/bigconsole/components/datasink/ImportDataSinkDialog.tsx"],"sourcesContent":["/**\n * ImportDataSinkDialog Component\n *\n * Modal dialog for importing a DataSink from JSON file.\n * Supports both creating new DataSinks and updating existing ones.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect, useRef } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Label } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent } from '@burdenoff/fe-libs/ui';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { Upload, FileJson, Check, XCircle, ArrowLeft, RefreshCw, Info } from 'lucide-react';\nimport { useImportDataSink, type ImportDataSinkOptions } from '../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ImportDataSinkDialogProps {\n isOpen: boolean;\n onClose: () => void;\n onSuccess?: (dataSinkId: string, dataSinkName: string | null, originalUpdated: boolean) => void;\n}\n\ntype ImportStep = 'select' | 'preview' | 'importing' | 'complete';\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ImportDataSinkDialog: FC<ImportDataSinkDialogProps> = memo(function ImportDataSinkDialog({\n isOpen,\n onClose,\n onSuccess,\n}) {\n const [step, setStep] = useState<ImportStep>('select');\n const [, setSelectedFile] = useState<File | null>(null);\n const [fileContent, setFileContent] = useState<string | null>(null);\n const [nameOverride, setNameOverride] = useState('');\n const [keyOverride, setKeyOverride] = useState('');\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const {\n loading,\n error,\n validationResult,\n importResult,\n validateImport,\n importDataSink,\n extractDataSinkId,\n clearState,\n } = useImportDataSink();\n\n // Reset state when dialog opens\n useEffect(() => {\n if (isOpen) {\n setStep('select');\n setSelectedFile(null);\n setFileContent(null);\n setNameOverride('');\n setKeyOverride('');\n clearState();\n }\n }, [isOpen, clearState]);\n\n // Handle file selection\n const handleFileSelect = useCallback(\n async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n\n if (!file.name.endsWith('.json')) {\n alert('Please select a JSON file');\n return;\n }\n\n setSelectedFile(file);\n\n const reader = new FileReader();\n reader.onload = async (event) => {\n const content = event.target?.result as string;\n setFileContent(content);\n\n const result = validateImport(content);\n if (result.isValid && result.dataSinkInfo) {\n setStep('preview');\n setNameOverride(result.dataSinkInfo.name || '');\n setKeyOverride(result.dataSinkInfo.key || '');\n } else {\n setStep('preview');\n }\n };\n reader.onerror = () => {\n alert('Failed to read file');\n };\n reader.readAsText(file);\n },\n [validateImport]\n );\n\n // Handle file drop\n const handleDrop = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n const file = e.dataTransfer.files?.[0];\n if (file) {\n const dt = new DataTransfer();\n dt.items.add(file);\n if (fileInputRef.current) {\n fileInputRef.current.files = dt.files;\n fileInputRef.current.dispatchEvent(new Event('change', { bubbles: true }));\n }\n }\n }, []);\n\n // Handle import\n const handleImport = useCallback(async () => {\n if (!fileContent) return;\n\n setStep('importing');\n\n const options: ImportDataSinkOptions = {\n name: nameOverride.trim() || undefined,\n key: keyOverride.trim() || undefined,\n };\n\n const result = await importDataSink(fileContent, options);\n\n if (result?.success && result.dataSink) {\n setStep('complete');\n onSuccess?.(result.dataSink.id, result.dataSink.name, result.originalUpdated ?? false);\n } else {\n setStep('preview');\n }\n }, [fileContent, nameOverride, keyOverride, importDataSink, onSuccess]);\n\n const canClose = !loading && step !== 'importing';\n const dataSinkId = fileContent ? extractDataSinkId(fileContent) : null;\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && canClose && onClose()}>\n <DialogContent className=\"sm:max-w-lg max-h-[85vh] flex flex-col\">\n <DialogHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10\">\n <Upload className=\"size-5 text-primary\" />\n </div>\n <DialogTitle>Import DataSink</DialogTitle>\n </div>\n <DialogDescription>Upload a DataSink export file to restore it into this workspace.</DialogDescription>\n </DialogHeader>\n\n <div className=\"flex-1 overflow-y-auto py-4 space-y-4\">\n {/* Step 1: File Selection */}\n {step === 'select' && (\n <div\n onDragOver={(e) => e.preventDefault()}\n onDrop={handleDrop}\n className=\"border-2 border-dashed rounded-lg p-8 text-center hover:border-primary transition-colors\"\n >\n <FileJson className=\"size-12 mx-auto text-text-secondary mb-4\" />\n <p className=\"text-sm mb-2\">Drag and drop a DataSink export file here</p>\n <p className=\"text-xs text-text-secondary mb-4\">or</p>\n <input ref={fileInputRef} type=\"file\" accept=\".json\" onChange={handleFileSelect} className=\"hidden\" />\n <Button onClick={() => fileInputRef.current?.click()} disabled={loading}>\n Browse Files\n </Button>\n <p className=\"text-xs text-text-secondary mt-4\">\n Only .json files exported from BigConsole are supported\n </p>\n </div>\n )}\n\n {/* Step 2: Preview & Options */}\n {(step === 'preview' || step === 'importing') && validationResult && (\n <>\n {/* DataSink Info */}\n {validationResult.dataSinkInfo && (\n <Card>\n <CardContent className=\"p-4 space-y-2\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Name</span>\n <span className=\"text-sm font-medium\">{validationResult.dataSinkInfo.name || 'Unnamed'}</span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Key</span>\n <span className=\"text-xs font-mono\">{validationResult.dataSinkInfo.key}</span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Status</span>\n <Badge variant=\"secondary\">{validationResult.dataSinkInfo.status}</Badge>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Setup State</span>\n <Badge variant=\"outline\">{validationResult.dataSinkInfo.setupState}</Badge>\n </div>\n {validationResult.dataSinkInfo.exportedFrom && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Source ID</span>\n <span className=\"text-xs text-text-secondary font-mono truncate max-w-[180px]\">\n {validationResult.dataSinkInfo.exportedFrom}\n </span>\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Schema Version</span>\n <span className=\"text-xs text-text-secondary\">{validationResult.dataSinkInfo.schemaVersion}</span>\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Validation Errors */}\n {validationResult.errors.length > 0 && (\n <Card className=\"border-status-error-border/50 bg-status-error-bg/5\">\n <CardContent className=\"p-3\">\n <div className=\"flex items-center gap-2 mb-2\">\n <XCircle className=\"size-4 text-status-error-text\" />\n <span className=\"text-sm font-medium text-status-error-text\">\n Errors ({validationResult.errors.length})\n </span>\n </div>\n <ul className=\"text-xs text-status-error-text space-y-1\">\n {validationResult.errors.map((err, idx) => (\n <li key={idx}>\n <span className=\"font-mono\">[{err.code}]</span> {err.message}\n </li>\n ))}\n </ul>\n </CardContent>\n </Card>\n )}\n\n {/* Import Options */}\n {validationResult.isValid && (\n <div className=\"space-y-4\">\n <p className=\"text-sm font-medium\">Import Options</p>\n\n {/* Import Info */}\n <Card className=\"border-blue-500/50 bg-blue-500/5\">\n <CardContent className=\"p-3\">\n <div className=\"flex items-start gap-2\">\n <Info className=\"size-4 text-blue-600 mt-0.5 shrink-0\" />\n <div className=\"text-xs text-blue-700 dark:text-blue-400\">\n {dataSinkId ? (\n <>\n <p className=\"font-medium mb-1\">Import will check for existing DataSink</p>\n <p>\n If the original DataSink (ID:{' '}\n <code className=\"font-mono bg-blue-200/50 px-1 rounded\">\n {dataSinkId.slice(0, 8)}...\n </code>\n ) exists:\n </p>\n <ul className=\"list-disc list-inside mt-1 space-y-0.5\">\n <li>With changes → Update the existing DataSink</li>\n <li>No changes → Show &quot;already exists&quot; message</li>\n </ul>\n <p className=\"mt-1\">If original doesn&apos;t exist → Create a new DataSink</p>\n </>\n ) : (\n <>\n <p className=\"font-medium mb-1\">A new DataSink will be created</p>\n <p>Import as a new DataSink with the specified configuration.</p>\n </>\n )}\n </div>\n </div>\n </CardContent>\n </Card>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"import-name\">DataSink Name</Label>\n <Input\n id=\"import-name\"\n type=\"text\"\n value={nameOverride}\n onChange={(e) => setNameOverride(e.target.value)}\n placeholder={validationResult.dataSinkInfo?.name || 'DataSink name'}\n disabled={step === 'importing'}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"import-key\">DataSink Key</Label>\n <Input\n id=\"import-key\"\n type=\"text\"\n value={keyOverride}\n onChange={(e) => setKeyOverride(e.target.value)}\n placeholder={validationResult.dataSinkInfo?.key || 'datasink-key'}\n disabled={step === 'importing'}\n />\n <p className=\"text-xs text-text-secondary\">\n Use a unique key to avoid conflicts with existing DataSinks.\n </p>\n </div>\n </div>\n )}\n </>\n )}\n\n {/* Step 3: Complete */}\n {step === 'complete' && importResult?.success && (\n <div className=\"text-center py-6\">\n {/* Already Exists - No Changes */}\n {importResult.alreadyExists && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center\">\n <Info className=\"size-8 text-blue-600\" />\n </div>\n <h3 className=\"text-lg font-medium mb-2\">DataSink Already Exists</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n DataSink &quot;{importResult.dataSink?.name || importResult.dataSink?.key}&quot; already exists with\n the same data. No changes were made.\n </p>\n </>\n )}\n\n {/* Updated Existing */}\n {importResult.originalUpdated && !importResult.alreadyExists && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center\">\n <RefreshCw className=\"size-8 text-green-600\" />\n </div>\n <h3 className=\"text-lg font-medium mb-2\">DataSink Updated!</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n DataSink &quot;{importResult.dataSink?.name || importResult.dataSink?.key}&quot; has been updated\n with the imported data.\n </p>\n </>\n )}\n\n {/* Created New */}\n {!importResult.originalUpdated && !importResult.alreadyExists && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center\">\n <Check className=\"size-8 text-green-600\" />\n </div>\n <h3 className=\"text-lg font-medium mb-2\">Import Successful!</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n DataSink &quot;{importResult.dataSink?.name || importResult.dataSink?.key}&quot; has been created.\n </p>\n </>\n )}\n </div>\n )}\n\n {/* Error */}\n {error && (\n <Card className=\"border-status-error-border/50 bg-status-error-bg/5\">\n <CardContent className=\"p-3\">\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n </CardContent>\n </Card>\n )}\n\n {importResult && !importResult.success && importResult.errorMessage && (\n <Card className=\"border-status-error-border/50 bg-status-error-bg/5\">\n <CardContent className=\"p-3\">\n <p className=\"text-sm text-status-error-text\">{importResult.errorMessage}</p>\n </CardContent>\n </Card>\n )}\n </div>\n\n <DialogFooter className=\"flex-row justify-between sm:justify-between\">\n <div>\n {step === 'preview' && (\n <Button\n variant=\"ghost\"\n onClick={() => {\n setStep('select');\n setSelectedFile(null);\n setFileContent(null);\n clearState();\n }}\n disabled={loading}\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </Button>\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n {step !== 'complete' && (\n <Button variant=\"outline\" onClick={onClose} disabled={!canClose}>\n Cancel\n </Button>\n )}\n\n {step === 'preview' && validationResult?.isValid && (\n <Button onClick={handleImport} disabled={loading}>\n <Upload className=\"size-4\" />\n Import DataSink\n </Button>\n )}\n\n {step === 'importing' && (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Spinner className=\"size-4\" />\n Importing...\n </div>\n )}\n\n {step === 'complete' && <Button onClick={onClose}>Done</Button>}\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n});\n\nexport default ImportDataSinkDialog;\n"],"mappings":"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"}
1
+ {"version":3,"file":"ImportDataSinkDialog.js","names":[],"sources":["../../../../src/bigconsole/components/datasink/ImportDataSinkDialog.tsx"],"sourcesContent":["/**\n * ImportDataSinkDialog Component\n *\n * Modal dialog for importing a DataSink from JSON file.\n * Supports both creating new DataSinks and updating existing ones.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect, useRef } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Label } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent } from '@burdenoff/fe-libs/ui';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { Upload, FileJson, Check, XCircle, ArrowLeft, RefreshCw, Info } from 'lucide-react';\nimport { useImportDataSink, type ImportDataSinkOptions } from '../../hooks';\nimport { useBigConsoleAnalytics } from '../../hooks/useBigConsoleAnalytics';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ImportDataSinkDialogProps {\n isOpen: boolean;\n onClose: () => void;\n onSuccess?: (dataSinkId: string, dataSinkName: string | null, originalUpdated: boolean) => void;\n}\n\ntype ImportStep = 'select' | 'preview' | 'importing' | 'complete';\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ImportDataSinkDialog: FC<ImportDataSinkDialogProps> = memo(function ImportDataSinkDialog({\n isOpen,\n onClose,\n onSuccess,\n}) {\n const [step, setStep] = useState<ImportStep>('select');\n const [, setSelectedFile] = useState<File | null>(null);\n const [fileContent, setFileContent] = useState<string | null>(null);\n const [nameOverride, setNameOverride] = useState('');\n const [keyOverride, setKeyOverride] = useState('');\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const {\n loading,\n error,\n validationResult,\n importResult,\n validateImport,\n importDataSink,\n extractDataSinkId,\n clearState,\n } = useImportDataSink();\n const emitBigConsoleEvent = useBigConsoleAnalytics();\n\n // Reset state when dialog opens\n useEffect(() => {\n if (isOpen) {\n setStep('select');\n setSelectedFile(null);\n setFileContent(null);\n setNameOverride('');\n setKeyOverride('');\n clearState();\n }\n }, [isOpen, clearState]);\n\n // Handle file selection\n const handleFileSelect = useCallback(\n async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n\n if (!file.name.endsWith('.json')) {\n alert('Please select a JSON file');\n return;\n }\n\n setSelectedFile(file);\n\n const reader = new FileReader();\n reader.onload = async (event) => {\n const content = event.target?.result as string;\n setFileContent(content);\n\n const result = validateImport(content);\n if (result.isValid && result.dataSinkInfo) {\n setStep('preview');\n setNameOverride(result.dataSinkInfo.name || '');\n setKeyOverride(result.dataSinkInfo.key || '');\n } else {\n setStep('preview');\n }\n };\n reader.onerror = () => {\n alert('Failed to read file');\n };\n reader.readAsText(file);\n },\n [validateImport]\n );\n\n // Handle file drop\n const handleDrop = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n const file = e.dataTransfer.files?.[0];\n if (file) {\n const dt = new DataTransfer();\n dt.items.add(file);\n if (fileInputRef.current) {\n fileInputRef.current.files = dt.files;\n fileInputRef.current.dispatchEvent(new Event('change', { bubbles: true }));\n }\n }\n }, []);\n\n // Handle import\n const handleImport = useCallback(async () => {\n if (!fileContent) return;\n\n setStep('importing');\n\n const options: ImportDataSinkOptions = {\n name: nameOverride.trim() || undefined,\n key: keyOverride.trim() || undefined,\n };\n\n emitBigConsoleEvent('bigconsole.data_sink.import_clicked', { source: 'import' });\n\n const result = await importDataSink(fileContent, options);\n\n if (result?.success && result.dataSink) {\n setStep('complete');\n emitBigConsoleEvent('bigconsole.data_sink.imported', {\n dataSinkId: result.dataSink.id,\n source: 'import',\n originalUpdated: result.originalUpdated ?? false,\n });\n onSuccess?.(result.dataSink.id, result.dataSink.name, result.originalUpdated ?? false);\n } else {\n setStep('preview');\n }\n }, [emitBigConsoleEvent, fileContent, importDataSink, keyOverride, nameOverride, onSuccess]);\n\n const canClose = !loading && step !== 'importing';\n const dataSinkId = fileContent ? extractDataSinkId(fileContent) : null;\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && canClose && onClose()}>\n <DialogContent className=\"sm:max-w-lg max-h-[85vh] flex flex-col\">\n <DialogHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10\">\n <Upload className=\"size-5 text-primary\" />\n </div>\n <DialogTitle>Import DataSink</DialogTitle>\n </div>\n <DialogDescription>Upload a DataSink export file to restore it into this workspace.</DialogDescription>\n </DialogHeader>\n\n <div className=\"flex-1 overflow-y-auto py-4 space-y-4\">\n {/* Step 1: File Selection */}\n {step === 'select' && (\n <div\n onDragOver={(e) => e.preventDefault()}\n onDrop={handleDrop}\n className=\"border-2 border-dashed rounded-lg p-8 text-center hover:border-primary transition-colors\"\n >\n <FileJson className=\"size-12 mx-auto text-text-secondary mb-4\" />\n <p className=\"text-sm mb-2\">Drag and drop a DataSink export file here</p>\n <p className=\"text-xs text-text-secondary mb-4\">or</p>\n <input ref={fileInputRef} type=\"file\" accept=\".json\" onChange={handleFileSelect} className=\"hidden\" />\n <Button onClick={() => fileInputRef.current?.click()} disabled={loading}>\n Browse Files\n </Button>\n <p className=\"text-xs text-text-secondary mt-4\">\n Only .json files exported from BigConsole are supported\n </p>\n </div>\n )}\n\n {/* Step 2: Preview & Options */}\n {(step === 'preview' || step === 'importing') && validationResult && (\n <>\n {/* DataSink Info */}\n {validationResult.dataSinkInfo && (\n <Card>\n <CardContent className=\"p-4 space-y-2\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Name</span>\n <span className=\"text-sm font-medium\">{validationResult.dataSinkInfo.name || 'Unnamed'}</span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Key</span>\n <span className=\"text-xs font-mono\">{validationResult.dataSinkInfo.key}</span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Status</span>\n <Badge variant=\"secondary\">{validationResult.dataSinkInfo.status}</Badge>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Setup State</span>\n <Badge variant=\"outline\">{validationResult.dataSinkInfo.setupState}</Badge>\n </div>\n {validationResult.dataSinkInfo.exportedFrom && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Source ID</span>\n <span className=\"text-xs text-text-secondary font-mono truncate max-w-[180px]\">\n {validationResult.dataSinkInfo.exportedFrom}\n </span>\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Schema Version</span>\n <span className=\"text-xs text-text-secondary\">{validationResult.dataSinkInfo.schemaVersion}</span>\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Validation Errors */}\n {validationResult.errors.length > 0 && (\n <Card className=\"border-status-error-border/50 bg-status-error-bg/5\">\n <CardContent className=\"p-3\">\n <div className=\"flex items-center gap-2 mb-2\">\n <XCircle className=\"size-4 text-status-error-text\" />\n <span className=\"text-sm font-medium text-status-error-text\">\n Errors ({validationResult.errors.length})\n </span>\n </div>\n <ul className=\"text-xs text-status-error-text space-y-1\">\n {validationResult.errors.map((err, idx) => (\n <li key={idx}>\n <span className=\"font-mono\">[{err.code}]</span> {err.message}\n </li>\n ))}\n </ul>\n </CardContent>\n </Card>\n )}\n\n {/* Import Options */}\n {validationResult.isValid && (\n <div className=\"space-y-4\">\n <p className=\"text-sm font-medium\">Import Options</p>\n\n {/* Import Info */}\n <Card className=\"border-blue-500/50 bg-blue-500/5\">\n <CardContent className=\"p-3\">\n <div className=\"flex items-start gap-2\">\n <Info className=\"size-4 text-blue-600 mt-0.5 shrink-0\" />\n <div className=\"text-xs text-blue-700 dark:text-blue-400\">\n {dataSinkId ? (\n <>\n <p className=\"font-medium mb-1\">Import will check for existing DataSink</p>\n <p>\n If the original DataSink (ID:{' '}\n <code className=\"font-mono bg-blue-200/50 px-1 rounded\">\n {dataSinkId.slice(0, 8)}...\n </code>\n ) exists:\n </p>\n <ul className=\"list-disc list-inside mt-1 space-y-0.5\">\n <li>With changes → Update the existing DataSink</li>\n <li>No changes → Show &quot;already exists&quot; message</li>\n </ul>\n <p className=\"mt-1\">If original doesn&apos;t exist → Create a new DataSink</p>\n </>\n ) : (\n <>\n <p className=\"font-medium mb-1\">A new DataSink will be created</p>\n <p>Import as a new DataSink with the specified configuration.</p>\n </>\n )}\n </div>\n </div>\n </CardContent>\n </Card>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"import-name\">DataSink Name</Label>\n <Input\n id=\"import-name\"\n type=\"text\"\n value={nameOverride}\n onChange={(e) => setNameOverride(e.target.value)}\n placeholder={validationResult.dataSinkInfo?.name || 'DataSink name'}\n disabled={step === 'importing'}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"import-key\">DataSink Key</Label>\n <Input\n id=\"import-key\"\n type=\"text\"\n value={keyOverride}\n onChange={(e) => setKeyOverride(e.target.value)}\n placeholder={validationResult.dataSinkInfo?.key || 'datasink-key'}\n disabled={step === 'importing'}\n />\n <p className=\"text-xs text-text-secondary\">\n Use a unique key to avoid conflicts with existing DataSinks.\n </p>\n </div>\n </div>\n )}\n </>\n )}\n\n {/* Step 3: Complete */}\n {step === 'complete' && importResult?.success && (\n <div className=\"text-center py-6\">\n {/* Already Exists - No Changes */}\n {importResult.alreadyExists && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center\">\n <Info className=\"size-8 text-blue-600\" />\n </div>\n <h3 className=\"text-lg font-medium mb-2\">DataSink Already Exists</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n DataSink &quot;{importResult.dataSink?.name || importResult.dataSink?.key}&quot; already exists with\n the same data. No changes were made.\n </p>\n </>\n )}\n\n {/* Updated Existing */}\n {importResult.originalUpdated && !importResult.alreadyExists && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center\">\n <RefreshCw className=\"size-8 text-green-600\" />\n </div>\n <h3 className=\"text-lg font-medium mb-2\">DataSink Updated!</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n DataSink &quot;{importResult.dataSink?.name || importResult.dataSink?.key}&quot; has been updated\n with the imported data.\n </p>\n </>\n )}\n\n {/* Created New */}\n {!importResult.originalUpdated && !importResult.alreadyExists && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center\">\n <Check className=\"size-8 text-green-600\" />\n </div>\n <h3 className=\"text-lg font-medium mb-2\">Import Successful!</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n DataSink &quot;{importResult.dataSink?.name || importResult.dataSink?.key}&quot; has been created.\n </p>\n </>\n )}\n </div>\n )}\n\n {/* Error */}\n {error && (\n <Card className=\"border-status-error-border/50 bg-status-error-bg/5\">\n <CardContent className=\"p-3\">\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n </CardContent>\n </Card>\n )}\n\n {importResult && !importResult.success && importResult.errorMessage && (\n <Card className=\"border-status-error-border/50 bg-status-error-bg/5\">\n <CardContent className=\"p-3\">\n <p className=\"text-sm text-status-error-text\">{importResult.errorMessage}</p>\n </CardContent>\n </Card>\n )}\n </div>\n\n <DialogFooter className=\"flex-row justify-between sm:justify-between\">\n <div>\n {step === 'preview' && (\n <Button\n variant=\"ghost\"\n onClick={() => {\n setStep('select');\n setSelectedFile(null);\n setFileContent(null);\n clearState();\n }}\n disabled={loading}\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </Button>\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n {step !== 'complete' && (\n <Button variant=\"outline\" onClick={onClose} disabled={!canClose}>\n Cancel\n </Button>\n )}\n\n {step === 'preview' && validationResult?.isValid && (\n <Button onClick={handleImport} disabled={loading}>\n <Upload className=\"size-4\" />\n Import DataSink\n </Button>\n )}\n\n {step === 'importing' && (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Spinner className=\"size-4\" />\n Importing...\n </div>\n )}\n\n {step === 'complete' && <Button onClick={onClose}>Done</Button>}\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n});\n\nexport default ImportDataSinkDialog;\n"],"mappings":"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"}
@@ -3,6 +3,7 @@ import "./useWorkflowOperations.js";
3
3
  import "./useWidgetData.js";
4
4
  import "./useWidgetCatalog.js";
5
5
  import "./useAutoRefresh.js";
6
+ import "./useBigConsoleAnalytics.js";
6
7
  import "./useWidgetOperations.js";
7
8
  import "./useWidgetComments.js";
8
9
  import "./useWidgetConfigPersist.js";
@@ -0,0 +1,13 @@
1
+ import { useCallback as e } from "react";
2
+ import { useEventBus as t } from "@burdenoff/fe-libs/shared/events";
3
+ //#region src/bigconsole/hooks/useBigConsoleAnalytics.ts
4
+ function n() {
5
+ let n = t();
6
+ return e((e, t) => {
7
+ n.emitCustom(e, t, { component: "bigconsole" });
8
+ }, [n]);
9
+ }
10
+ //#endregion
11
+ export { n as default };
12
+
13
+ //# sourceMappingURL=useBigConsoleAnalytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBigConsoleAnalytics.js","names":[],"sources":["../../../src/bigconsole/hooks/useBigConsoleAnalytics.ts"],"sourcesContent":["import { useCallback } from 'react';\n\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\n\nexport type BigConsoleAnalyticsEventName =\n | 'bigconsole.ai.create_dashboard_clicked'\n | 'bigconsole.ai.dashboard_created'\n | 'bigconsole.ai.dashboard_generated'\n | 'bigconsole.ai.generate_clicked'\n | 'bigconsole.dashboard.import_clicked'\n | 'bigconsole.dashboard.imported'\n | 'bigconsole.dashboard.share_clicked'\n | 'bigconsole.dashboard.shared'\n | 'bigconsole.data_sink.import_clicked'\n | 'bigconsole.data_sink.imported'\n | 'bigconsole.widget.create_clicked'\n | 'bigconsole.widget.created'\n | 'bigconsole.widget.duplicate_clicked'\n | 'bigconsole.widget.duplicated';\n\ninterface BigConsoleImportPayload {\n source?: 'import';\n}\n\ninterface BigConsoleDashboardPayload extends BigConsoleImportPayload {\n dashboardId?: string;\n originalUpdated?: boolean;\n}\n\ninterface BigConsoleDashboardSharePayload {\n dashboardId?: string;\n permission?: string;\n recipientType?: string;\n}\n\ninterface BigConsoleDataSinkPayload extends BigConsoleImportPayload {\n dataSinkId?: string;\n originalUpdated?: boolean;\n}\n\ninterface BigConsoleWidgetPayload {\n dashboardId?: string;\n pageId?: string;\n sourceWidgetId?: string;\n widgetId?: string;\n widgetType?: string;\n}\n\ninterface BigConsoleAiPayload {\n dashboardId?: string;\n generationId?: string;\n maxWidgets?: number;\n}\n\nexport interface BigConsoleAnalyticsPayloadMap {\n 'bigconsole.ai.create_dashboard_clicked': BigConsoleAiPayload;\n 'bigconsole.ai.dashboard_created': BigConsoleAiPayload;\n 'bigconsole.ai.dashboard_generated': BigConsoleAiPayload;\n 'bigconsole.ai.generate_clicked': BigConsoleAiPayload;\n 'bigconsole.dashboard.import_clicked': BigConsoleImportPayload;\n 'bigconsole.dashboard.imported': BigConsoleDashboardPayload;\n 'bigconsole.dashboard.share_clicked': BigConsoleDashboardSharePayload;\n 'bigconsole.dashboard.shared': BigConsoleDashboardSharePayload;\n 'bigconsole.data_sink.import_clicked': BigConsoleImportPayload;\n 'bigconsole.data_sink.imported': BigConsoleDataSinkPayload;\n 'bigconsole.widget.create_clicked': BigConsoleWidgetPayload;\n 'bigconsole.widget.created': BigConsoleWidgetPayload;\n 'bigconsole.widget.duplicate_clicked': BigConsoleWidgetPayload;\n 'bigconsole.widget.duplicated': BigConsoleWidgetPayload;\n}\n\nexport function useBigConsoleAnalytics(): <TName extends BigConsoleAnalyticsEventName>(\n name: TName,\n payload: BigConsoleAnalyticsPayloadMap[TName]\n) => void {\n const bus = useEventBus();\n\n return useCallback(\n <TName extends BigConsoleAnalyticsEventName>(name: TName, payload: BigConsoleAnalyticsPayloadMap[TName]) => {\n bus.emitCustom(name, payload, { component: 'bigconsole' });\n },\n [bus]\n );\n}\n\nexport default useBigConsoleAnalytics;\n"],"mappings":";;;AAuEA,SAAgB,IAGN;CACR,IAAM,IAAM,GAAa;AAEzB,QAAO,GACwC,GAAa,MAAkD;AAC1G,IAAI,WAAW,GAAM,GAAS,EAAE,WAAW,cAAc,CAAC;IAE5D,CAAC,EAAI,CACN"}
@@ -2,10 +2,11 @@ import { useWidgetStore as e } from "../store/widgetStore.js";
2
2
  import "../store/index.js";
3
3
  import { BatchUpdateWidgetPositionsDocument as t, CreateWidgetDocument as n, DeleteWidgetDocument as r, DuplicateWidgetDocument as i, ListWidgetsByDashboardDocument as a, ListWidgetsForPageDocument as o, RefreshWidgetDataDocument as s, UpdateWidgetDocument as c } from "../../generated/wspace-operations.js";
4
4
  import { mapFromFederationWidgetType as l, normalizeWidgetTypeForApi as u } from "../utils/widgetTypeMapping.js";
5
- import { useCallback as d, useEffect as f, useMemo as p, useState as m } from "react";
6
- import { useApolloClient as h } from "@apollo/client/react";
5
+ import d from "./useBigConsoleAnalytics.js";
6
+ import { useCallback as f, useEffect as p, useMemo as m, useState as h } from "react";
7
+ import { useApolloClient as g } from "@apollo/client/react";
7
8
  //#region src/bigconsole/hooks/useWidgetOperations.ts
8
- function g(e) {
9
+ function _(e) {
9
10
  let t = e.actions, n = e.metadata, r = e.positionResponsive, i = e.position || {
10
11
  x: e.positionX ?? 0,
11
12
  y: e.positionY ?? 0,
@@ -52,38 +53,38 @@ function g(e) {
52
53
  updatedAt: e.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
53
54
  };
54
55
  }
55
- function _(l, _, v = {}) {
56
- let { skipFetch: y = !1 } = v, [b, x] = m(!1), [S, C] = m(null), w = h(), T = e((e) => e.widgets), E = e((e) => e.setWidgets), D = e((e) => e.addWidget), O = e((e) => e.updateWidget), k = e((e) => e.removeWidget), A = p(() => Array.from(T.values()), [T]), j = d(async () => {
57
- if (!(!l && !_)) try {
58
- x(!0), C(null), _ ? E(((await w.query({
56
+ function v(l, v, y = {}) {
57
+ let { skipFetch: b = !1 } = y, [x, S] = h(!1), [C, w] = h(null), T = g(), E = d(), D = e((e) => e.widgets), O = e((e) => e.setWidgets), k = e((e) => e.addWidget), A = e((e) => e.updateWidget), j = e((e) => e.removeWidget), M = m(() => Array.from(D.values()), [D]), N = f(async () => {
58
+ if (!(!l && !v)) try {
59
+ S(!0), w(null), v ? O(((await T.query({
59
60
  query: a,
60
- variables: { dashboardId: _ },
61
+ variables: { dashboardId: v },
61
62
  fetchPolicy: "network-only"
62
- })).data?.listWidgetsByDashboard || []).map(g)) : l && E(((await w.query({
63
+ })).data?.listWidgetsByDashboard || []).map(_)) : l && O(((await T.query({
63
64
  query: o,
64
65
  variables: { pageId: l },
65
66
  fetchPolicy: "network-only"
66
- })).data?.listWidgets?.edges || []).map((e) => e.node).map(g));
67
+ })).data?.listWidgets?.edges || []).map((e) => e.node).map(_));
67
68
  } catch (e) {
68
- C(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to fetch widgets"));
69
+ w(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to fetch widgets"));
69
70
  } finally {
70
- x(!1);
71
+ S(!1);
71
72
  }
72
73
  }, [
73
- w,
74
+ T,
74
75
  l,
75
- _,
76
- E
76
+ v,
77
+ O
77
78
  ]);
78
- return f(() => {
79
- y || j();
80
- }, [j, y]), {
81
- loading: b,
82
- error: S,
83
- widgets: A,
84
- createWidget: d(async (e) => {
79
+ return p(() => {
80
+ b || N();
81
+ }, [N, b]), {
82
+ loading: x,
83
+ error: C,
84
+ widgets: M,
85
+ createWidget: f(async (e) => {
85
86
  try {
86
- x(!0), C(null);
87
+ S(!0), w(null);
87
88
  let t = u(e.type), r = {
88
89
  ...e.config || {},
89
90
  _internalWidgetType: e.type
@@ -112,25 +113,39 @@ function _(l, _, v = {}) {
112
113
  sm: e.positionResponsive.sm ?? 6,
113
114
  md: e.positionResponsive.md ?? 4,
114
115
  lg: e.positionResponsive.lg ?? 3
115
- }), e.rowOrder !== void 0 && (i.rowOrder = e.rowOrder);
116
- let a = (await w.mutate({
116
+ }), e.rowOrder !== void 0 && (i.rowOrder = e.rowOrder), E("bigconsole.widget.create_clicked", {
117
+ dashboardId: e.dashboardId || v,
118
+ pageId: e.pageId,
119
+ widgetType: e.type
120
+ });
121
+ let a = (await T.mutate({
117
122
  mutation: n,
118
123
  variables: { input: i }
119
124
  })).data?.createBigConsoleWidget;
120
125
  if (a) {
121
- let e = g(a);
122
- return D(e), e;
126
+ let t = _(a);
127
+ return k(t), E("bigconsole.widget.created", {
128
+ dashboardId: t.dashboardId || e.dashboardId || v,
129
+ pageId: t.pageId,
130
+ widgetId: t.id,
131
+ widgetType: t.type
132
+ }), t;
123
133
  }
124
134
  return null;
125
135
  } catch (e) {
126
- return C(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to create widget")), null;
136
+ return w(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to create widget")), null;
127
137
  } finally {
128
- x(!1);
138
+ S(!1);
129
139
  }
130
- }, [w, D]),
131
- updateWidget: d(async (t) => {
140
+ }, [
141
+ T,
142
+ k,
143
+ v,
144
+ E
145
+ ]),
146
+ updateWidget: f(async (t) => {
132
147
  try {
133
- if (x(!0), C(null), !e.getState().widgets.get(t.id)) throw Error(`Widget not found: ${t.id}`);
148
+ if (S(!0), w(null), !e.getState().widgets.get(t.id)) throw Error(`Widget not found: ${t.id}`);
134
149
  let n = { id: t.id };
135
150
  t.title !== void 0 && (n.title = t.title), t.renderer !== void 0 && (n.renderer = t.renderer), t.template !== void 0 && (n.template = t.template), t.dataSinkId !== void 0 && (n.dataSinkId = t.dataSinkId), t.datasetId !== void 0 && (n.datasetId = t.datasetId), t.parserId !== void 0 && (n.parserId = t.parserId), t.parserRules !== void 0 && (n.parserRules = t.parserRules), t.config !== void 0 && (n.config = t.config), t.refreshInterval !== void 0 && (n.refreshInterval = t.refreshInterval), t.metadata !== void 0 && (n.metadata = t.metadata), t.position && (n.position = {
136
151
  x: t.position.x,
@@ -143,39 +158,47 @@ function _(l, _, v = {}) {
143
158
  md: t.positionResponsive.md,
144
159
  lg: t.positionResponsive.lg
145
160
  }), t.rowOrder !== void 0 && (n.rowOrder = t.rowOrder);
146
- let r = await w.mutate({
161
+ let r = await T.mutate({
147
162
  mutation: c,
148
163
  variables: { input: n }
149
164
  });
150
165
  if (r.error) throw r.error;
151
166
  let i = r.data?.updateBigConsoleWidget;
152
167
  if (i) {
153
- let e = g(i);
154
- return O(t.id, e), e;
168
+ let e = _(i);
169
+ return A(t.id, e), e;
155
170
  }
156
171
  return null;
157
172
  } catch (e) {
158
- return C(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to update widget")), null;
173
+ return w(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to update widget")), null;
159
174
  } finally {
160
- x(!1);
175
+ S(!1);
161
176
  }
162
- }, [w, O]),
163
- deleteWidget: d(async (e) => {
177
+ }, [T, A]),
178
+ deleteWidget: f(async (e) => {
164
179
  try {
165
- return x(!0), C(null), (await w.mutate({
180
+ return S(!0), w(null), (await T.mutate({
166
181
  mutation: r,
167
182
  variables: { id: e }
168
- })).data?.deleteBigConsoleWidget ? (k(e), !0) : !1;
183
+ })).data?.deleteBigConsoleWidget ? (j(e), !0) : !1;
169
184
  } catch (e) {
170
- return C(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to delete widget")), !1;
185
+ return w(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to delete widget")), !1;
171
186
  } finally {
172
- x(!1);
187
+ S(!1);
173
188
  }
174
- }, [w, k]),
175
- duplicateWidget: d(async (t, n, r) => {
189
+ }, [T, j]),
190
+ duplicateWidget: f(async (t, n, r) => {
176
191
  try {
177
- if (x(!0), C(null), !e.getState().widgets.get(t)) throw Error(`Widget not found: ${t}`);
178
- let a = await w.mutate({
192
+ S(!0), w(null);
193
+ let a = e.getState().widgets.get(t);
194
+ if (!a) throw Error(`Widget not found: ${t}`);
195
+ E("bigconsole.widget.duplicate_clicked", {
196
+ dashboardId: a.dashboardId || v,
197
+ pageId: a.pageId,
198
+ sourceWidgetId: t,
199
+ widgetType: a.type
200
+ });
201
+ let o = await T.mutate({
179
202
  mutation: i,
180
203
  variables: {
181
204
  id: t,
@@ -188,22 +211,33 @@ function _(l, _, v = {}) {
188
211
  newTitle: r
189
212
  }
190
213
  });
191
- if (a.error) throw a.error;
192
- let o = a.data?.duplicateWidget;
193
- if (o) {
194
- let e = g(o);
195
- return D(e), e;
214
+ if (o.error) throw o.error;
215
+ let s = o.data?.duplicateWidget;
216
+ if (s) {
217
+ let e = _(s);
218
+ return k(e), E("bigconsole.widget.duplicated", {
219
+ dashboardId: e.dashboardId || a.dashboardId || v,
220
+ pageId: e.pageId,
221
+ widgetId: e.id,
222
+ sourceWidgetId: t,
223
+ widgetType: e.type
224
+ }), e;
196
225
  }
197
226
  return null;
198
227
  } catch (e) {
199
- return C(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to duplicate widget")), null;
228
+ return w(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to duplicate widget")), null;
200
229
  } finally {
201
- x(!1);
230
+ S(!1);
202
231
  }
203
- }, [w, D]),
204
- batchUpdatePositions: d(async (e) => {
232
+ }, [
233
+ T,
234
+ k,
235
+ v,
236
+ E
237
+ ]),
238
+ batchUpdatePositions: f(async (e) => {
205
239
  try {
206
- return x(!0), C(null), (await w.mutate({
240
+ return S(!0), w(null), (await T.mutate({
207
241
  mutation: t,
208
242
  variables: { updates: e.map((e) => ({
209
243
  id: e.id,
@@ -215,7 +249,7 @@ function _(l, _, v = {}) {
215
249
  }
216
250
  })) }
217
251
  })).data?.batchUpdateWidgetPositions ? (e.forEach((e) => {
218
- O(e.id, {
252
+ A(e.id, {
219
253
  position: e.position,
220
254
  positionX: e.position.x,
221
255
  positionY: e.position.y,
@@ -225,25 +259,25 @@ function _(l, _, v = {}) {
225
259
  });
226
260
  }), !0) : !1;
227
261
  } catch (e) {
228
- return C(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to batch update positions")), !1;
262
+ return w(e instanceof Error ? e : /* @__PURE__ */ Error("Failed to batch update positions")), !1;
229
263
  } finally {
230
- x(!1);
264
+ S(!1);
231
265
  }
232
- }, [w, O]),
233
- refreshWidgetData: d(async (e) => {
266
+ }, [T, A]),
267
+ refreshWidgetData: f(async (e) => {
234
268
  try {
235
- await w.mutate({
269
+ await T.mutate({
236
270
  mutation: s,
237
271
  variables: { widgetId: e }
238
272
  });
239
273
  } catch {}
240
- }, [w]),
241
- refetch: d(async () => {
242
- await j();
243
- }, [j])
274
+ }, [T]),
275
+ refetch: f(async () => {
276
+ await N();
277
+ }, [N])
244
278
  };
245
279
  }
246
280
  //#endregion
247
- export { _ as default };
281
+ export { v as default };
248
282
 
249
283
  //# sourceMappingURL=useWidgetOperations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useWidgetOperations.js","names":[],"sources":["../../../src/bigconsole/hooks/useWidgetOperations.ts"],"sourcesContent":["/**\n * useWidgetOperations Hook\n *\n * Provides CRUD operations for widgets using GraphQL.\n */\n\nimport { useCallback, useState, useMemo, useEffect } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport type {\n Widget,\n WidgetType,\n WidgetPosition,\n ResponsivePosition,\n ResponsivePositionInput,\n RendererType,\n} from '../types';\nimport { useWidgetStore } from '../store';\nimport { normalizeWidgetTypeForApi, mapFromFederationWidgetType } from '../utils/widgetTypeMapping';\nimport {\n ListWidgetsForPageDocument,\n ListWidgetsByDashboardDocument,\n CreateWidgetDocument,\n UpdateWidgetDocument,\n DeleteWidgetDocument,\n DuplicateWidgetDocument,\n BatchUpdateWidgetPositionsDocument,\n RefreshWidgetDataDocument,\n} from '../../generated/wspace-operations';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface CreateWidgetInput {\n pageId: string;\n dashboardId?: string;\n type: WidgetType;\n title: string;\n description?: string;\n // v2.0: Renderer and template support\n renderer?: RendererType;\n template?: Record<string, unknown> | null;\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId?: string;\n datasetId?: string;\n parserId?: string;\n parserRules?: string;\n config?: Record<string, unknown>;\n // Fixed Grid Position (legacy v1.2)\n position: WidgetPosition;\n // Responsive Position (v1.0 spec)\n positionResponsive?: ResponsivePositionInput;\n rowOrder?: number;\n refreshInterval?: number;\n metadata?: Record<string, unknown>;\n}\n\ninterface UpdateWidgetInput {\n id: string;\n title?: string;\n description?: string;\n // v2.0: Renderer and template support\n renderer?: RendererType;\n template?: Record<string, unknown> | null;\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId?: string;\n datasetId?: string;\n parserId?: string;\n parserRules?: string;\n config?: Record<string, unknown>;\n // Fixed Grid Position (legacy v1.2)\n position?: WidgetPosition;\n // Responsive Position (v1.0 spec)\n positionResponsive?: ResponsivePositionInput;\n rowOrder?: number;\n refreshInterval?: number | null;\n cacheTTL?: number | null;\n metadata?: Record<string, unknown>;\n}\n\ninterface WidgetOperationsResult {\n // Query state\n loading: boolean;\n error: Error | null;\n widgets: Widget[];\n\n // Operations\n createWidget: (input: CreateWidgetInput) => Promise<Widget | null>;\n updateWidget: (input: UpdateWidgetInput) => Promise<Widget | null>;\n deleteWidget: (id: string) => Promise<boolean>;\n duplicateWidget: (id: string, newPosition?: WidgetPosition, newTitle?: string) => Promise<Widget | null>;\n batchUpdatePositions: (updates: Array<{ id: string; position: WidgetPosition }>) => Promise<boolean>;\n refreshWidgetData: (widgetId: string) => Promise<void>;\n refetch: () => Promise<void>;\n}\n\n// ============================================================================\n// Helper: Normalize widget from GraphQL response\n// ============================================================================\n\nfunction normalizeWidget(widget: Record<string, unknown>): Widget {\n const actions = widget.actions as unknown[] | undefined;\n const metadata = widget.metadata as Record<string, unknown> | undefined;\n const respPos = widget.positionResponsive as Record<string, unknown> | undefined;\n\n // Debug: Log raw widget from GraphQL\n\n // Fixed Grid Position (legacy v1.2)\n const position = (widget.position as WidgetPosition) || {\n x: (widget.positionX as number) ?? 0,\n y: (widget.positionY as number) ?? 0,\n width: (widget.positionWidth as number) ?? 4,\n height: (widget.positionHeight as number) ?? 4,\n };\n\n // Responsive Position (v1.0 spec)\n const positionResponsive: ResponsivePosition | null = respPos\n ? {\n xs: (respPos.xs as number) ?? 12,\n sm: (respPos.sm as number) ?? 6,\n md: (respPos.md as number) ?? 4,\n lg: (respPos.lg as number) ?? 3,\n // xl is optional; fall back to lg when absent (server has no xl tier yet)\n ...(respPos.xl !== undefined && respPos.xl !== null ? { xl: respPos.xl as number } : {}),\n }\n : null;\n\n // Read description from metadata if not directly available\n const description = (widget.description as string) || (metadata?.description as string) || '';\n\n // Get the config object\n const config = (widget.config as Record<string, unknown>) || {};\n\n // Check for stored internal widget type in config (preserves original type like 'gauge', 'progress', etc.)\n // This is needed because multiple internal types map to the same federation type (e.g., gauge -> KPI)\n const internalType = config._internalWidgetType as string | undefined;\n\n return {\n id: widget.id,\n pageId: widget.pageId || 'local',\n dashboardId: widget.dashboardId,\n // Use stored internal type if available, otherwise map from federation type\n type: internalType || mapFromFederationWidgetType((widget.type as string) || 'KPI'),\n title: widget.title || 'Untitled Widget',\n description: description,\n // v2.0: Renderer and template support\n renderer: widget.renderer || 'BIGCONSOLE',\n template: widget.template || null,\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId: widget.dataSinkId || null,\n datasetId: widget.datasetId || null,\n parserId: widget.parserId || null,\n parserRules: widget.parserRules || null,\n config: config,\n // Fixed Grid Position (legacy v1.2)\n position,\n positionX: position.x,\n positionY: position.y,\n positionWidth: position.width,\n positionHeight: position.height,\n // Responsive Position (v1.0 spec)\n positionResponsive,\n rowOrder: (widget.rowOrder as number | null) ?? null,\n refreshInterval: widget.refreshInterval,\n lastRefreshedAt: widget.lastRefreshedAt,\n cacheTTL: (widget.cacheTTL as number | null) ?? null,\n metadata: metadata,\n context: widget.context,\n drilldown: widget.drilldown,\n translations: widget.translations || [],\n // v2.0: Widget actions\n actions: actions || [],\n // Include widget data from backend\n data: widget.data || null,\n createdAt: widget.createdAt || new Date().toISOString(),\n updatedAt: widget.updatedAt || new Date().toISOString(),\n } as Widget;\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\ninterface UseWidgetOperationsOptions {\n /** Skip auto-fetching widgets on mount (use when parent already fetches) */\n skipFetch?: boolean;\n}\n\nexport function useWidgetOperations(\n pageId?: string,\n dashboardId?: string,\n options: UseWidgetOperationsOptions = {}\n): WidgetOperationsResult {\n const { skipFetch = false } = options;\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Get Apollo Client from context\n const apolloClient = useApolloClient();\n\n // Store actions\n const widgetsMap = useWidgetStore((state) => state.widgets);\n const setWidgets = useWidgetStore((state) => state.setWidgets);\n const addWidgetToStore = useWidgetStore((state) => state.addWidget);\n const updateWidgetInStore = useWidgetStore((state) => state.updateWidget);\n const removeWidgetFromStore = useWidgetStore((state) => state.removeWidget);\n\n // Get widgets array from store - memoized\n const widgets = useMemo(() => Array.from(widgetsMap.values()), [widgetsMap]);\n\n // Fetch widgets from backend\n const fetchWidgets = useCallback(async () => {\n if (!pageId && !dashboardId) {\n return;\n }\n\n try {\n setLoading(true);\n setError(null);\n\n if (dashboardId) {\n const result = await apolloClient.query<Record<string, unknown>>({\n query: ListWidgetsByDashboardDocument,\n variables: { dashboardId },\n fetchPolicy: 'network-only',\n });\n const widgetsList = (result.data?.listWidgetsByDashboard as Record<string, unknown>[]) || [];\n setWidgets(widgetsList.map(normalizeWidget));\n } else if (pageId) {\n const result = await apolloClient.query<Record<string, unknown>>({\n query: ListWidgetsForPageDocument,\n variables: { pageId },\n fetchPolicy: 'network-only',\n });\n const listResult = result.data?.listWidgets as Record<string, unknown> | undefined;\n const edges = (listResult?.edges as { node: Record<string, unknown> }[]) || [];\n const widgetsList = edges.map((edge: { node: Record<string, unknown> }) => edge.node);\n setWidgets(widgetsList.map(normalizeWidget));\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch widgets');\n setError(error);\n } finally {\n setLoading(false);\n }\n }, [apolloClient, pageId, dashboardId, setWidgets]);\n\n // Fetch on mount and when pageId/dashboardId changes (unless skipFetch is true)\n useEffect(() => {\n if (!skipFetch) {\n fetchWidgets();\n }\n }, [fetchWidgets, skipFetch]);\n\n // Create widget\n const createWidget = useCallback(\n async (input: CreateWidgetInput): Promise<Widget | null> => {\n try {\n setLoading(true);\n setError(null);\n\n // Build mutation input\n // Map internal widget type to federation-accessible type for API calls\n const federationType = normalizeWidgetTypeForApi(input.type);\n\n // Store the original internal widget type in config for proper restoration\n // This is needed because multiple internal types (gauge, progress, text, etc.)\n // map to the same federation type (KPI), but we need to preserve the original\n // type for correct widget rendering\n const configWithInternalType = {\n ...(input.config || {}),\n _internalWidgetType: input.type,\n };\n\n const mutationInput: Record<string, unknown> = {\n pageId: input.pageId,\n type: federationType,\n title: input.title,\n // v2.0: Renderer and template support\n renderer: input.renderer || 'BIGCONSOLE',\n template: input.template || null,\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId: input.dataSinkId,\n datasetId: input.datasetId,\n parserId: input.parserId,\n parserRules: input.parserRules,\n config: configWithInternalType,\n // Fixed Grid Position (legacy v1.2)\n position: {\n x: input.position.x,\n y: input.position.y,\n width: input.position.width,\n height: input.position.height,\n },\n refreshInterval: input.refreshInterval,\n metadata: input.metadata,\n };\n\n // Add Responsive Position if provided (v1.0 spec)\n if (input.positionResponsive) {\n mutationInput.positionResponsive = {\n xs: input.positionResponsive.xs ?? 12,\n sm: input.positionResponsive.sm ?? 6,\n md: input.positionResponsive.md ?? 4,\n lg: input.positionResponsive.lg ?? 3,\n };\n }\n\n // Add rowOrder if provided\n if (input.rowOrder !== undefined) {\n mutationInput.rowOrder = input.rowOrder;\n }\n\n const result = await apolloClient.mutate<Record<string, unknown>>({\n mutation: CreateWidgetDocument,\n variables: { input: mutationInput },\n });\n\n const createResult = result.data?.createBigConsoleWidget as Record<string, unknown> | undefined;\n if (createResult) {\n const newWidget = normalizeWidget(createResult);\n addWidgetToStore(newWidget);\n return newWidget;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to create widget');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, addWidgetToStore]\n );\n\n // Update widget\n const updateWidget = useCallback(\n async (input: UpdateWidgetInput): Promise<Widget | null> => {\n try {\n setLoading(true);\n setError(null);\n\n // Get latest widget from store (avoid stale closure)\n const currentWidgetsMap = useWidgetStore.getState().widgets;\n const existingWidget = currentWidgetsMap.get(input.id);\n if (!existingWidget) {\n throw new Error(`Widget not found: ${input.id}`);\n }\n\n const updateInput: Record<string, unknown> = { id: input.id };\n if (input.title !== undefined) updateInput.title = input.title;\n // v2.0: Renderer and template support\n if (input.renderer !== undefined) updateInput.renderer = input.renderer;\n if (input.template !== undefined) updateInput.template = input.template;\n // v2.0: Data source fields (DataSink → Parser → Widget)\n if (input.dataSinkId !== undefined) updateInput.dataSinkId = input.dataSinkId;\n if (input.datasetId !== undefined) updateInput.datasetId = input.datasetId;\n if (input.parserId !== undefined) updateInput.parserId = input.parserId;\n if (input.parserRules !== undefined) updateInput.parserRules = input.parserRules;\n if (input.config !== undefined) updateInput.config = input.config;\n if (input.refreshInterval !== undefined) updateInput.refreshInterval = input.refreshInterval;\n if (input.metadata !== undefined) updateInput.metadata = input.metadata;\n\n // Fixed Grid Position (legacy v1.2)\n if (input.position) {\n updateInput.position = {\n x: input.position.x,\n y: input.position.y,\n width: input.position.width,\n height: input.position.height,\n };\n }\n\n // Responsive Position (v1.0 spec)\n if (input.positionResponsive) {\n updateInput.positionResponsive = {\n xs: input.positionResponsive.xs,\n sm: input.positionResponsive.sm,\n md: input.positionResponsive.md,\n lg: input.positionResponsive.lg,\n };\n }\n\n // Row order\n if (input.rowOrder !== undefined) {\n updateInput.rowOrder = input.rowOrder;\n }\n\n const result = await apolloClient.mutate<Record<string, unknown>>({\n mutation: UpdateWidgetDocument,\n variables: { input: updateInput },\n });\n\n if (result.error) {\n throw result.error;\n }\n\n const updateResult = result.data?.updateBigConsoleWidget as Record<string, unknown> | undefined;\n if (updateResult) {\n const updatedWidget = normalizeWidget(updateResult);\n updateWidgetInStore(input.id, updatedWidget);\n return updatedWidget;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to update widget');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, updateWidgetInStore]\n );\n\n // Delete widget\n const deleteWidget = useCallback(\n async (id: string): Promise<boolean> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.mutate<Record<string, unknown>>({\n mutation: DeleteWidgetDocument,\n variables: { id },\n });\n\n if (result.data?.deleteBigConsoleWidget) {\n removeWidgetFromStore(id);\n return true;\n }\n\n return false;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to delete widget');\n setError(error);\n return false;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, removeWidgetFromStore]\n );\n\n // Duplicate widget\n const duplicateWidget = useCallback(\n async (id: string, newPosition?: WidgetPosition, newTitle?: string): Promise<Widget | null> => {\n try {\n setLoading(true);\n setError(null);\n\n // Get latest widget from store (avoid stale closure)\n const currentWidgetsMap = useWidgetStore.getState().widgets;\n const existingWidget = currentWidgetsMap.get(id);\n if (!existingWidget) {\n throw new Error(`Widget not found: ${id}`);\n }\n\n const result = await apolloClient.mutate<Record<string, unknown>>({\n mutation: DuplicateWidgetDocument,\n variables: {\n id,\n newPosition: newPosition\n ? {\n x: newPosition.x,\n y: newPosition.y,\n width: newPosition.width,\n height: newPosition.height,\n }\n : undefined,\n newTitle,\n },\n });\n\n if (result.error) {\n throw result.error;\n }\n\n const dupResult = result.data?.duplicateWidget as Record<string, unknown> | undefined;\n if (dupResult) {\n const duplicatedWidget = normalizeWidget(dupResult);\n addWidgetToStore(duplicatedWidget);\n return duplicatedWidget;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to duplicate widget');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, addWidgetToStore]\n );\n\n // Batch update positions\n const batchUpdatePositions = useCallback(\n async (updates: Array<{ id: string; position: WidgetPosition }>): Promise<boolean> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.mutate<Record<string, unknown>>({\n mutation: BatchUpdateWidgetPositionsDocument,\n variables: {\n updates: updates.map((u) => ({\n id: u.id,\n position: {\n x: u.position.x,\n y: u.position.y,\n width: u.position.width,\n height: u.position.height,\n },\n })),\n },\n });\n\n if (result.data?.batchUpdateWidgetPositions) {\n // Update local store with results\n updates.forEach((update) => {\n updateWidgetInStore(update.id, {\n position: update.position,\n positionX: update.position.x,\n positionY: update.position.y,\n positionWidth: update.position.width,\n positionHeight: update.position.height,\n updatedAt: new Date().toISOString(),\n });\n });\n return true;\n }\n\n return false;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to batch update positions');\n setError(error);\n return false;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, updateWidgetInStore]\n );\n\n // Refresh widget data\n const refreshWidgetData = useCallback(\n async (widgetId: string): Promise<void> => {\n try {\n await apolloClient.mutate<Record<string, unknown>>({\n mutation: RefreshWidgetDataDocument,\n variables: { widgetId },\n });\n } catch (err) {}\n },\n [apolloClient]\n );\n\n // Refetch\n const refetch = useCallback(async (): Promise<void> => {\n await fetchWidgets();\n }, [fetchWidgets]);\n\n return {\n loading,\n error,\n widgets,\n createWidget,\n updateWidget,\n deleteWidget,\n duplicateWidget,\n batchUpdatePositions,\n refreshWidgetData,\n refetch,\n };\n}\n\nexport default useWidgetOperations;\n"],"mappings":"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"}
1
+ {"version":3,"file":"useWidgetOperations.js","names":[],"sources":["../../../src/bigconsole/hooks/useWidgetOperations.ts"],"sourcesContent":["/**\n * useWidgetOperations Hook\n *\n * Provides CRUD operations for widgets using GraphQL.\n */\n\nimport { useCallback, useState, useMemo, useEffect } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport type {\n Widget,\n WidgetType,\n WidgetPosition,\n ResponsivePosition,\n ResponsivePositionInput,\n RendererType,\n} from '../types';\nimport { useWidgetStore } from '../store';\nimport { normalizeWidgetTypeForApi, mapFromFederationWidgetType } from '../utils/widgetTypeMapping';\nimport {\n ListWidgetsForPageDocument,\n ListWidgetsByDashboardDocument,\n CreateWidgetDocument,\n UpdateWidgetDocument,\n DeleteWidgetDocument,\n DuplicateWidgetDocument,\n BatchUpdateWidgetPositionsDocument,\n RefreshWidgetDataDocument,\n} from '../../generated/wspace-operations';\nimport { useBigConsoleAnalytics } from './useBigConsoleAnalytics';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface CreateWidgetInput {\n pageId: string;\n dashboardId?: string;\n type: WidgetType;\n title: string;\n description?: string;\n // v2.0: Renderer and template support\n renderer?: RendererType;\n template?: Record<string, unknown> | null;\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId?: string;\n datasetId?: string;\n parserId?: string;\n parserRules?: string;\n config?: Record<string, unknown>;\n // Fixed Grid Position (legacy v1.2)\n position: WidgetPosition;\n // Responsive Position (v1.0 spec)\n positionResponsive?: ResponsivePositionInput;\n rowOrder?: number;\n refreshInterval?: number;\n metadata?: Record<string, unknown>;\n}\n\ninterface UpdateWidgetInput {\n id: string;\n title?: string;\n description?: string;\n // v2.0: Renderer and template support\n renderer?: RendererType;\n template?: Record<string, unknown> | null;\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId?: string;\n datasetId?: string;\n parserId?: string;\n parserRules?: string;\n config?: Record<string, unknown>;\n // Fixed Grid Position (legacy v1.2)\n position?: WidgetPosition;\n // Responsive Position (v1.0 spec)\n positionResponsive?: ResponsivePositionInput;\n rowOrder?: number;\n refreshInterval?: number | null;\n cacheTTL?: number | null;\n metadata?: Record<string, unknown>;\n}\n\ninterface WidgetOperationsResult {\n // Query state\n loading: boolean;\n error: Error | null;\n widgets: Widget[];\n\n // Operations\n createWidget: (input: CreateWidgetInput) => Promise<Widget | null>;\n updateWidget: (input: UpdateWidgetInput) => Promise<Widget | null>;\n deleteWidget: (id: string) => Promise<boolean>;\n duplicateWidget: (id: string, newPosition?: WidgetPosition, newTitle?: string) => Promise<Widget | null>;\n batchUpdatePositions: (updates: Array<{ id: string; position: WidgetPosition }>) => Promise<boolean>;\n refreshWidgetData: (widgetId: string) => Promise<void>;\n refetch: () => Promise<void>;\n}\n\n// ============================================================================\n// Helper: Normalize widget from GraphQL response\n// ============================================================================\n\nfunction normalizeWidget(widget: Record<string, unknown>): Widget {\n const actions = widget.actions as unknown[] | undefined;\n const metadata = widget.metadata as Record<string, unknown> | undefined;\n const respPos = widget.positionResponsive as Record<string, unknown> | undefined;\n\n // Debug: Log raw widget from GraphQL\n\n // Fixed Grid Position (legacy v1.2)\n const position = (widget.position as WidgetPosition) || {\n x: (widget.positionX as number) ?? 0,\n y: (widget.positionY as number) ?? 0,\n width: (widget.positionWidth as number) ?? 4,\n height: (widget.positionHeight as number) ?? 4,\n };\n\n // Responsive Position (v1.0 spec)\n const positionResponsive: ResponsivePosition | null = respPos\n ? {\n xs: (respPos.xs as number) ?? 12,\n sm: (respPos.sm as number) ?? 6,\n md: (respPos.md as number) ?? 4,\n lg: (respPos.lg as number) ?? 3,\n // xl is optional; fall back to lg when absent (server has no xl tier yet)\n ...(respPos.xl !== undefined && respPos.xl !== null ? { xl: respPos.xl as number } : {}),\n }\n : null;\n\n // Read description from metadata if not directly available\n const description = (widget.description as string) || (metadata?.description as string) || '';\n\n // Get the config object\n const config = (widget.config as Record<string, unknown>) || {};\n\n // Check for stored internal widget type in config (preserves original type like 'gauge', 'progress', etc.)\n // This is needed because multiple internal types map to the same federation type (e.g., gauge -> KPI)\n const internalType = config._internalWidgetType as string | undefined;\n\n return {\n id: widget.id,\n pageId: widget.pageId || 'local',\n dashboardId: widget.dashboardId,\n // Use stored internal type if available, otherwise map from federation type\n type: internalType || mapFromFederationWidgetType((widget.type as string) || 'KPI'),\n title: widget.title || 'Untitled Widget',\n description: description,\n // v2.0: Renderer and template support\n renderer: widget.renderer || 'BIGCONSOLE',\n template: widget.template || null,\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId: widget.dataSinkId || null,\n datasetId: widget.datasetId || null,\n parserId: widget.parserId || null,\n parserRules: widget.parserRules || null,\n config: config,\n // Fixed Grid Position (legacy v1.2)\n position,\n positionX: position.x,\n positionY: position.y,\n positionWidth: position.width,\n positionHeight: position.height,\n // Responsive Position (v1.0 spec)\n positionResponsive,\n rowOrder: (widget.rowOrder as number | null) ?? null,\n refreshInterval: widget.refreshInterval,\n lastRefreshedAt: widget.lastRefreshedAt,\n cacheTTL: (widget.cacheTTL as number | null) ?? null,\n metadata: metadata,\n context: widget.context,\n drilldown: widget.drilldown,\n translations: widget.translations || [],\n // v2.0: Widget actions\n actions: actions || [],\n // Include widget data from backend\n data: widget.data || null,\n createdAt: widget.createdAt || new Date().toISOString(),\n updatedAt: widget.updatedAt || new Date().toISOString(),\n } as Widget;\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\ninterface UseWidgetOperationsOptions {\n /** Skip auto-fetching widgets on mount (use when parent already fetches) */\n skipFetch?: boolean;\n}\n\nexport function useWidgetOperations(\n pageId?: string,\n dashboardId?: string,\n options: UseWidgetOperationsOptions = {}\n): WidgetOperationsResult {\n const { skipFetch = false } = options;\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Get Apollo Client from context\n const apolloClient = useApolloClient();\n const emitBigConsoleEvent = useBigConsoleAnalytics();\n\n // Store actions\n const widgetsMap = useWidgetStore((state) => state.widgets);\n const setWidgets = useWidgetStore((state) => state.setWidgets);\n const addWidgetToStore = useWidgetStore((state) => state.addWidget);\n const updateWidgetInStore = useWidgetStore((state) => state.updateWidget);\n const removeWidgetFromStore = useWidgetStore((state) => state.removeWidget);\n\n // Get widgets array from store - memoized\n const widgets = useMemo(() => Array.from(widgetsMap.values()), [widgetsMap]);\n\n // Fetch widgets from backend\n const fetchWidgets = useCallback(async () => {\n if (!pageId && !dashboardId) {\n return;\n }\n\n try {\n setLoading(true);\n setError(null);\n\n if (dashboardId) {\n const result = await apolloClient.query<Record<string, unknown>>({\n query: ListWidgetsByDashboardDocument,\n variables: { dashboardId },\n fetchPolicy: 'network-only',\n });\n const widgetsList = (result.data?.listWidgetsByDashboard as Record<string, unknown>[]) || [];\n setWidgets(widgetsList.map(normalizeWidget));\n } else if (pageId) {\n const result = await apolloClient.query<Record<string, unknown>>({\n query: ListWidgetsForPageDocument,\n variables: { pageId },\n fetchPolicy: 'network-only',\n });\n const listResult = result.data?.listWidgets as Record<string, unknown> | undefined;\n const edges = (listResult?.edges as { node: Record<string, unknown> }[]) || [];\n const widgetsList = edges.map((edge: { node: Record<string, unknown> }) => edge.node);\n setWidgets(widgetsList.map(normalizeWidget));\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch widgets');\n setError(error);\n } finally {\n setLoading(false);\n }\n }, [apolloClient, pageId, dashboardId, setWidgets]);\n\n // Fetch on mount and when pageId/dashboardId changes (unless skipFetch is true)\n useEffect(() => {\n if (!skipFetch) {\n fetchWidgets();\n }\n }, [fetchWidgets, skipFetch]);\n\n // Create widget\n const createWidget = useCallback(\n async (input: CreateWidgetInput): Promise<Widget | null> => {\n try {\n setLoading(true);\n setError(null);\n\n // Build mutation input\n // Map internal widget type to federation-accessible type for API calls\n const federationType = normalizeWidgetTypeForApi(input.type);\n\n // Store the original internal widget type in config for proper restoration\n // This is needed because multiple internal types (gauge, progress, text, etc.)\n // map to the same federation type (KPI), but we need to preserve the original\n // type for correct widget rendering\n const configWithInternalType = {\n ...(input.config || {}),\n _internalWidgetType: input.type,\n };\n\n const mutationInput: Record<string, unknown> = {\n pageId: input.pageId,\n type: federationType,\n title: input.title,\n // v2.0: Renderer and template support\n renderer: input.renderer || 'BIGCONSOLE',\n template: input.template || null,\n // v2.0: Data source fields (DataSink → Parser → Widget)\n dataSinkId: input.dataSinkId,\n datasetId: input.datasetId,\n parserId: input.parserId,\n parserRules: input.parserRules,\n config: configWithInternalType,\n // Fixed Grid Position (legacy v1.2)\n position: {\n x: input.position.x,\n y: input.position.y,\n width: input.position.width,\n height: input.position.height,\n },\n refreshInterval: input.refreshInterval,\n metadata: input.metadata,\n };\n\n // Add Responsive Position if provided (v1.0 spec)\n if (input.positionResponsive) {\n mutationInput.positionResponsive = {\n xs: input.positionResponsive.xs ?? 12,\n sm: input.positionResponsive.sm ?? 6,\n md: input.positionResponsive.md ?? 4,\n lg: input.positionResponsive.lg ?? 3,\n };\n }\n\n // Add rowOrder if provided\n if (input.rowOrder !== undefined) {\n mutationInput.rowOrder = input.rowOrder;\n }\n\n emitBigConsoleEvent('bigconsole.widget.create_clicked', {\n dashboardId: input.dashboardId || dashboardId,\n pageId: input.pageId,\n widgetType: input.type,\n });\n\n const result = await apolloClient.mutate<Record<string, unknown>>({\n mutation: CreateWidgetDocument,\n variables: { input: mutationInput },\n });\n\n const createResult = result.data?.createBigConsoleWidget as Record<string, unknown> | undefined;\n if (createResult) {\n const newWidget = normalizeWidget(createResult);\n addWidgetToStore(newWidget);\n emitBigConsoleEvent('bigconsole.widget.created', {\n dashboardId: newWidget.dashboardId || input.dashboardId || dashboardId,\n pageId: newWidget.pageId,\n widgetId: newWidget.id,\n widgetType: newWidget.type,\n });\n return newWidget;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to create widget');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, addWidgetToStore, dashboardId, emitBigConsoleEvent]\n );\n\n // Update widget\n const updateWidget = useCallback(\n async (input: UpdateWidgetInput): Promise<Widget | null> => {\n try {\n setLoading(true);\n setError(null);\n\n // Get latest widget from store (avoid stale closure)\n const currentWidgetsMap = useWidgetStore.getState().widgets;\n const existingWidget = currentWidgetsMap.get(input.id);\n if (!existingWidget) {\n throw new Error(`Widget not found: ${input.id}`);\n }\n\n const updateInput: Record<string, unknown> = { id: input.id };\n if (input.title !== undefined) updateInput.title = input.title;\n // v2.0: Renderer and template support\n if (input.renderer !== undefined) updateInput.renderer = input.renderer;\n if (input.template !== undefined) updateInput.template = input.template;\n // v2.0: Data source fields (DataSink → Parser → Widget)\n if (input.dataSinkId !== undefined) updateInput.dataSinkId = input.dataSinkId;\n if (input.datasetId !== undefined) updateInput.datasetId = input.datasetId;\n if (input.parserId !== undefined) updateInput.parserId = input.parserId;\n if (input.parserRules !== undefined) updateInput.parserRules = input.parserRules;\n if (input.config !== undefined) updateInput.config = input.config;\n if (input.refreshInterval !== undefined) updateInput.refreshInterval = input.refreshInterval;\n if (input.metadata !== undefined) updateInput.metadata = input.metadata;\n\n // Fixed Grid Position (legacy v1.2)\n if (input.position) {\n updateInput.position = {\n x: input.position.x,\n y: input.position.y,\n width: input.position.width,\n height: input.position.height,\n };\n }\n\n // Responsive Position (v1.0 spec)\n if (input.positionResponsive) {\n updateInput.positionResponsive = {\n xs: input.positionResponsive.xs,\n sm: input.positionResponsive.sm,\n md: input.positionResponsive.md,\n lg: input.positionResponsive.lg,\n };\n }\n\n // Row order\n if (input.rowOrder !== undefined) {\n updateInput.rowOrder = input.rowOrder;\n }\n\n const result = await apolloClient.mutate<Record<string, unknown>>({\n mutation: UpdateWidgetDocument,\n variables: { input: updateInput },\n });\n\n if (result.error) {\n throw result.error;\n }\n\n const updateResult = result.data?.updateBigConsoleWidget as Record<string, unknown> | undefined;\n if (updateResult) {\n const updatedWidget = normalizeWidget(updateResult);\n updateWidgetInStore(input.id, updatedWidget);\n return updatedWidget;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to update widget');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, updateWidgetInStore]\n );\n\n // Delete widget\n const deleteWidget = useCallback(\n async (id: string): Promise<boolean> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.mutate<Record<string, unknown>>({\n mutation: DeleteWidgetDocument,\n variables: { id },\n });\n\n if (result.data?.deleteBigConsoleWidget) {\n removeWidgetFromStore(id);\n return true;\n }\n\n return false;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to delete widget');\n setError(error);\n return false;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, removeWidgetFromStore]\n );\n\n // Duplicate widget\n const duplicateWidget = useCallback(\n async (id: string, newPosition?: WidgetPosition, newTitle?: string): Promise<Widget | null> => {\n try {\n setLoading(true);\n setError(null);\n\n // Get latest widget from store (avoid stale closure)\n const currentWidgetsMap = useWidgetStore.getState().widgets;\n const existingWidget = currentWidgetsMap.get(id);\n if (!existingWidget) {\n throw new Error(`Widget not found: ${id}`);\n }\n\n emitBigConsoleEvent('bigconsole.widget.duplicate_clicked', {\n dashboardId: existingWidget.dashboardId || dashboardId,\n pageId: existingWidget.pageId,\n sourceWidgetId: id,\n widgetType: existingWidget.type,\n });\n\n const result = await apolloClient.mutate<Record<string, unknown>>({\n mutation: DuplicateWidgetDocument,\n variables: {\n id,\n newPosition: newPosition\n ? {\n x: newPosition.x,\n y: newPosition.y,\n width: newPosition.width,\n height: newPosition.height,\n }\n : undefined,\n newTitle,\n },\n });\n\n if (result.error) {\n throw result.error;\n }\n\n const dupResult = result.data?.duplicateWidget as Record<string, unknown> | undefined;\n if (dupResult) {\n const duplicatedWidget = normalizeWidget(dupResult);\n addWidgetToStore(duplicatedWidget);\n emitBigConsoleEvent('bigconsole.widget.duplicated', {\n dashboardId: duplicatedWidget.dashboardId || existingWidget.dashboardId || dashboardId,\n pageId: duplicatedWidget.pageId,\n widgetId: duplicatedWidget.id,\n sourceWidgetId: id,\n widgetType: duplicatedWidget.type,\n });\n return duplicatedWidget;\n }\n\n return null;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to duplicate widget');\n setError(error);\n return null;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, addWidgetToStore, dashboardId, emitBigConsoleEvent]\n );\n\n // Batch update positions\n const batchUpdatePositions = useCallback(\n async (updates: Array<{ id: string; position: WidgetPosition }>): Promise<boolean> => {\n try {\n setLoading(true);\n setError(null);\n\n const result = await apolloClient.mutate<Record<string, unknown>>({\n mutation: BatchUpdateWidgetPositionsDocument,\n variables: {\n updates: updates.map((u) => ({\n id: u.id,\n position: {\n x: u.position.x,\n y: u.position.y,\n width: u.position.width,\n height: u.position.height,\n },\n })),\n },\n });\n\n if (result.data?.batchUpdateWidgetPositions) {\n // Update local store with results\n updates.forEach((update) => {\n updateWidgetInStore(update.id, {\n position: update.position,\n positionX: update.position.x,\n positionY: update.position.y,\n positionWidth: update.position.width,\n positionHeight: update.position.height,\n updatedAt: new Date().toISOString(),\n });\n });\n return true;\n }\n\n return false;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to batch update positions');\n setError(error);\n return false;\n } finally {\n setLoading(false);\n }\n },\n [apolloClient, updateWidgetInStore]\n );\n\n // Refresh widget data\n const refreshWidgetData = useCallback(\n async (widgetId: string): Promise<void> => {\n try {\n await apolloClient.mutate<Record<string, unknown>>({\n mutation: RefreshWidgetDataDocument,\n variables: { widgetId },\n });\n } catch (err) {}\n },\n [apolloClient]\n );\n\n // Refetch\n const refetch = useCallback(async (): Promise<void> => {\n await fetchWidgets();\n }, [fetchWidgets]);\n\n return {\n loading,\n error,\n widgets,\n createWidget,\n updateWidget,\n deleteWidget,\n duplicateWidget,\n batchUpdatePositions,\n refreshWidgetData,\n refetch,\n };\n}\n\nexport default useWidgetOperations;\n"],"mappings":";;;;;;;;AAqGA,SAAS,EAAgB,GAAyC;CAChE,IAAM,IAAU,EAAO,SACjB,IAAW,EAAO,UAClB,IAAU,EAAO,oBAKjB,IAAY,EAAO,YAA+B;EACtD,GAAI,EAAO,aAAwB;EACnC,GAAI,EAAO,aAAwB;EACnC,OAAQ,EAAO,iBAA4B;EAC3C,QAAS,EAAO,kBAA6B;EAC9C,EAGK,IAAgD,IAClD;EACE,IAAK,EAAQ,MAAiB;EAC9B,IAAK,EAAQ,MAAiB;EAC9B,IAAK,EAAQ,MAAiB;EAC9B,IAAK,EAAQ,MAAiB;EAE9B,GAAI,EAAQ,OAAO,KAAA,KAAa,EAAQ,OAAO,OAAO,EAAE,IAAI,EAAQ,IAAc,GAAG,EAAE;EACxF,GACD,MAGE,IAAe,EAAO,eAA2B,GAAU,eAA0B,IAGrF,IAAU,EAAO,UAAsC,EAAE,EAIzD,IAAe,EAAO;AAE5B,QAAO;EACL,IAAI,EAAO;EACX,QAAQ,EAAO,UAAU;EACzB,aAAa,EAAO;EAEpB,MAAM,KAAgB,EAA6B,EAAO,QAAmB,MAAM;EACnF,OAAO,EAAO,SAAS;EACV;EAEb,UAAU,EAAO,YAAY;EAC7B,UAAU,EAAO,YAAY;EAE7B,YAAY,EAAO,cAAc;EACjC,WAAW,EAAO,aAAa;EAC/B,UAAU,EAAO,YAAY;EAC7B,aAAa,EAAO,eAAe;EAC3B;EAER;EACA,WAAW,EAAS;EACpB,WAAW,EAAS;EACpB,eAAe,EAAS;EACxB,gBAAgB,EAAS;EAEzB;EACA,UAAW,EAAO,YAA8B;EAChD,iBAAiB,EAAO;EACxB,iBAAiB,EAAO;EACxB,UAAW,EAAO,YAA8B;EACtC;EACV,SAAS,EAAO;EAChB,WAAW,EAAO;EAClB,cAAc,EAAO,gBAAgB,EAAE;EAEvC,SAAS,KAAW,EAAE;EAEtB,MAAM,EAAO,QAAQ;EACrB,WAAW,EAAO,8BAAa,IAAI,MAAM,EAAC,aAAa;EACvD,WAAW,EAAO,8BAAa,IAAI,MAAM,EAAC,aAAa;EACxD;;AAYH,SAAgB,EACd,GACA,GACA,IAAsC,EAAE,EAChB;CACxB,IAAM,EAAE,eAAY,OAAU,GACxB,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,CAAC,GAAO,KAAY,EAAuB,KAAK,EAGhD,IAAe,GAAiB,EAChC,IAAsB,GAAwB,EAG9C,IAAa,GAAgB,MAAU,EAAM,QAAQ,EACrD,IAAa,GAAgB,MAAU,EAAM,WAAW,EACxD,IAAmB,GAAgB,MAAU,EAAM,UAAU,EAC7D,IAAsB,GAAgB,MAAU,EAAM,aAAa,EACnE,IAAwB,GAAgB,MAAU,EAAM,aAAa,EAGrE,IAAU,QAAc,MAAM,KAAK,EAAW,QAAQ,CAAC,EAAE,CAAC,EAAW,CAAC,EAGtE,IAAe,EAAY,YAAY;AACvC,SAAC,KAAU,CAAC,GAIhB,KAAI;AAIF,GAHA,EAAW,GAAK,EAChB,EAAS,KAAK,EAEV,IAOF,IANe,MAAM,EAAa,MAA+B;IAC/D,OAAO;IACP,WAAW,EAAE,gBAAa;IAC1B,aAAa;IACd,CAAC,EAC0B,MAAM,0BAAwD,EAAE,EACrE,IAAI,EAAgB,CAAC,GACnC,KAST,IARe,MAAM,EAAa,MAA+B;IAC/D,OAAO;IACP,WAAW,EAAE,WAAQ;IACrB,aAAa;IACd,CAAC,EACwB,MAAM,aACL,SAAiD,EAAE,EACpD,KAAK,MAA4C,EAAK,KAAK,CAC9D,IAAI,EAAgB,CAAC;WAEvC,GAAK;AAEZ,KADc,aAAe,QAAQ,IAAM,gBAAI,MAAM,0BAA0B,CAChE;YACP;AACR,KAAW,GAAM;;IAElB;EAAC;EAAc;EAAQ;EAAa;EAAW,CAAC;AA4VnD,QAzVA,QAAgB;AACd,EAAK,KACH,GAAc;IAEf,CAAC,GAAc,EAAU,CAAC,EAqVtB;EACL;EACA;EACA;EACA,cAtVmB,EACnB,OAAO,MAAqD;AAC1D,OAAI;AAEF,IADA,EAAW,GAAK,EAChB,EAAS,KAAK;IAId,IAAM,IAAiB,EAA0B,EAAM,KAAK,EAMtD,IAAyB;KAC7B,GAAI,EAAM,UAAU,EAAE;KACtB,qBAAqB,EAAM;KAC5B,EAEK,IAAyC;KAC7C,QAAQ,EAAM;KACd,MAAM;KACN,OAAO,EAAM;KAEb,UAAU,EAAM,YAAY;KAC5B,UAAU,EAAM,YAAY;KAE5B,YAAY,EAAM;KAClB,WAAW,EAAM;KACjB,UAAU,EAAM;KAChB,aAAa,EAAM;KACnB,QAAQ;KAER,UAAU;MACR,GAAG,EAAM,SAAS;MAClB,GAAG,EAAM,SAAS;MAClB,OAAO,EAAM,SAAS;MACtB,QAAQ,EAAM,SAAS;MACxB;KACD,iBAAiB,EAAM;KACvB,UAAU,EAAM;KACjB;AAiBD,IAdI,EAAM,uBACR,EAAc,qBAAqB;KACjC,IAAI,EAAM,mBAAmB,MAAM;KACnC,IAAI,EAAM,mBAAmB,MAAM;KACnC,IAAI,EAAM,mBAAmB,MAAM;KACnC,IAAI,EAAM,mBAAmB,MAAM;KACpC,GAIC,EAAM,aAAa,KAAA,MACrB,EAAc,WAAW,EAAM,WAGjC,EAAoB,oCAAoC;KACtD,aAAa,EAAM,eAAe;KAClC,QAAQ,EAAM;KACd,YAAY,EAAM;KACnB,CAAC;IAOF,IAAM,KALS,MAAM,EAAa,OAAgC;KAChE,UAAU;KACV,WAAW,EAAE,OAAO,GAAe;KACpC,CAAC,EAE0B,MAAM;AAClC,QAAI,GAAc;KAChB,IAAM,IAAY,EAAgB,EAAa;AAQ/C,YAPA,EAAiB,EAAU,EAC3B,EAAoB,6BAA6B;MAC/C,aAAa,EAAU,eAAe,EAAM,eAAe;MAC3D,QAAQ,EAAU;MAClB,UAAU,EAAU;MACpB,YAAY,EAAU;MACvB,CAAC,EACK;;AAGT,WAAO;YACA,GAAK;AAGZ,WADA,EADc,aAAe,QAAQ,IAAM,gBAAI,MAAM,0BAA0B,CAChE,EACR;aACC;AACR,MAAW,GAAM;;KAGrB;GAAC;GAAc;GAAkB;GAAa;GAAoB,CACnE;EA2PC,cAxPmB,EACnB,OAAO,MAAqD;AAC1D,OAAI;AAOF,QANA,EAAW,GAAK,EAChB,EAAS,KAAK,EAKV,CAFsB,EAAe,UAAU,CAAC,QACX,IAAI,EAAM,GAAG,CAEpD,OAAU,MAAM,qBAAqB,EAAM,KAAK;IAGlD,IAAM,IAAuC,EAAE,IAAI,EAAM,IAAI;AAmC7D,IAlCI,EAAM,UAAU,KAAA,MAAW,EAAY,QAAQ,EAAM,QAErD,EAAM,aAAa,KAAA,MAAW,EAAY,WAAW,EAAM,WAC3D,EAAM,aAAa,KAAA,MAAW,EAAY,WAAW,EAAM,WAE3D,EAAM,eAAe,KAAA,MAAW,EAAY,aAAa,EAAM,aAC/D,EAAM,cAAc,KAAA,MAAW,EAAY,YAAY,EAAM,YAC7D,EAAM,aAAa,KAAA,MAAW,EAAY,WAAW,EAAM,WAC3D,EAAM,gBAAgB,KAAA,MAAW,EAAY,cAAc,EAAM,cACjE,EAAM,WAAW,KAAA,MAAW,EAAY,SAAS,EAAM,SACvD,EAAM,oBAAoB,KAAA,MAAW,EAAY,kBAAkB,EAAM,kBACzE,EAAM,aAAa,KAAA,MAAW,EAAY,WAAW,EAAM,WAG3D,EAAM,aACR,EAAY,WAAW;KACrB,GAAG,EAAM,SAAS;KAClB,GAAG,EAAM,SAAS;KAClB,OAAO,EAAM,SAAS;KACtB,QAAQ,EAAM,SAAS;KACxB,GAIC,EAAM,uBACR,EAAY,qBAAqB;KAC/B,IAAI,EAAM,mBAAmB;KAC7B,IAAI,EAAM,mBAAmB;KAC7B,IAAI,EAAM,mBAAmB;KAC7B,IAAI,EAAM,mBAAmB;KAC9B,GAIC,EAAM,aAAa,KAAA,MACrB,EAAY,WAAW,EAAM;IAG/B,IAAM,IAAS,MAAM,EAAa,OAAgC;KAChE,UAAU;KACV,WAAW,EAAE,OAAO,GAAa;KAClC,CAAC;AAEF,QAAI,EAAO,MACT,OAAM,EAAO;IAGf,IAAM,IAAe,EAAO,MAAM;AAClC,QAAI,GAAc;KAChB,IAAM,IAAgB,EAAgB,EAAa;AAEnD,YADA,EAAoB,EAAM,IAAI,EAAc,EACrC;;AAGT,WAAO;YACA,GAAK;AAGZ,WADA,EADc,aAAe,QAAQ,IAAM,gBAAI,MAAM,0BAA0B,CAChE,EACR;aACC;AACR,MAAW,GAAM;;KAGrB,CAAC,GAAc,EAAoB,CACpC;EA2KC,cAxKmB,EACnB,OAAO,MAAiC;AACtC,OAAI;AAcF,WAbA,EAAW,GAAK,EAChB,EAAS,KAAK,GAEC,MAAM,EAAa,OAAgC;KAChE,UAAU;KACV,WAAW,EAAE,OAAI;KAClB,CAAC,EAES,MAAM,0BACf,EAAsB,EAAG,EAClB,MAGF;YACA,GAAK;AAGZ,WADA,EADc,aAAe,QAAQ,IAAM,gBAAI,MAAM,0BAA0B,CAChE,EACR;aACC;AACR,MAAW,GAAM;;KAGrB,CAAC,GAAc,EAAsB,CACtC;EA+IC,iBA5IsB,EACtB,OAAO,GAAY,GAA8B,MAA8C;AAC7F,OAAI;AAEF,IADA,EAAW,GAAK,EAChB,EAAS,KAAK;IAId,IAAM,IADoB,EAAe,UAAU,CAAC,QACX,IAAI,EAAG;AAChD,QAAI,CAAC,EACH,OAAU,MAAM,qBAAqB,IAAK;AAG5C,MAAoB,uCAAuC;KACzD,aAAa,EAAe,eAAe;KAC3C,QAAQ,EAAe;KACvB,gBAAgB;KAChB,YAAY,EAAe;KAC5B,CAAC;IAEF,IAAM,IAAS,MAAM,EAAa,OAAgC;KAChE,UAAU;KACV,WAAW;MACT;MACA,aAAa,IACT;OACE,GAAG,EAAY;OACf,GAAG,EAAY;OACf,OAAO,EAAY;OACnB,QAAQ,EAAY;OACrB,GACD,KAAA;MACJ;MACD;KACF,CAAC;AAEF,QAAI,EAAO,MACT,OAAM,EAAO;IAGf,IAAM,IAAY,EAAO,MAAM;AAC/B,QAAI,GAAW;KACb,IAAM,IAAmB,EAAgB,EAAU;AASnD,YARA,EAAiB,EAAiB,EAClC,EAAoB,gCAAgC;MAClD,aAAa,EAAiB,eAAe,EAAe,eAAe;MAC3E,QAAQ,EAAiB;MACzB,UAAU,EAAiB;MAC3B,gBAAgB;MAChB,YAAY,EAAiB;MAC9B,CAAC,EACK;;AAGT,WAAO;YACA,GAAK;AAGZ,WADA,EADc,aAAe,QAAQ,IAAM,gBAAI,MAAM,6BAA6B,CACnE,EACR;aACC;AACR,MAAW,GAAM;;KAGrB;GAAC;GAAc;GAAkB;GAAa;GAAoB,CACnE;EA6EC,sBA1E2B,EAC3B,OAAO,MAA+E;AACpF,OAAI;AAkCF,WAjCA,EAAW,GAAK,EAChB,EAAS,KAAK,GAEC,MAAM,EAAa,OAAgC;KAChE,UAAU;KACV,WAAW,EACT,SAAS,EAAQ,KAAK,OAAO;MAC3B,IAAI,EAAE;MACN,UAAU;OACR,GAAG,EAAE,SAAS;OACd,GAAG,EAAE,SAAS;OACd,OAAO,EAAE,SAAS;OAClB,QAAQ,EAAE,SAAS;OACpB;MACF,EAAE,EACJ;KACF,CAAC,EAES,MAAM,8BAEf,EAAQ,SAAS,MAAW;AAC1B,OAAoB,EAAO,IAAI;MAC7B,UAAU,EAAO;MACjB,WAAW,EAAO,SAAS;MAC3B,WAAW,EAAO,SAAS;MAC3B,eAAe,EAAO,SAAS;MAC/B,gBAAgB,EAAO,SAAS;MAChC,4BAAW,IAAI,MAAM,EAAC,aAAa;MACpC,CAAC;MACF,EACK,MAGF;YACA,GAAK;AAGZ,WADA,EADc,aAAe,QAAQ,IAAM,gBAAI,MAAM,mCAAmC,CACzE,EACR;aACC;AACR,MAAW,GAAM;;KAGrB,CAAC,GAAc,EAAoB,CACpC;EA6BC,mBA1BwB,EACxB,OAAO,MAAoC;AACzC,OAAI;AACF,UAAM,EAAa,OAAgC;KACjD,UAAU;KACV,WAAW,EAAE,aAAU;KACxB,CAAC;WACU;KAEhB,CAAC,EAAa,CACf;EAiBC,SAdc,EAAY,YAA2B;AACrD,SAAM,GAAc;KACnB,CAAC,EAAa,CAAC;EAajB"}