@burdenoff/microfe-bigconsole 2026.804.2 → 2026.805.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/bigconsole/AdminRoot.js +21 -16
  2. package/dist/bigconsole/AdminRoot.js.map +1 -1
  3. package/dist/bigconsole/BigConsoleRoot.js +44 -39
  4. package/dist/bigconsole/BigConsoleRoot.js.map +1 -1
  5. package/dist/bigconsole/components/dashboard/DashboardToolbar.js +115 -100
  6. package/dist/bigconsole/components/dashboard/DashboardToolbar.js.map +1 -1
  7. package/dist/bigconsole/components/dashboard/PageTabsManager/PageTabsManager.js +100 -98
  8. package/dist/bigconsole/components/dashboard/PageTabsManager/PageTabsManager.js.map +1 -1
  9. package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js +164 -161
  10. package/dist/bigconsole/components/dashboard/ShareDialog/EmbedTab.js.map +1 -1
  11. package/dist/bigconsole/components/datasink/DataSinkCard.js +20 -20
  12. package/dist/bigconsole/components/datasink/DataSinkCard.js.map +1 -1
  13. package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js +216 -49
  14. package/dist/bigconsole/components/datasink/ImportDataGuideDialog.js.map +1 -1
  15. package/dist/bigconsole/components/widgets/ActionConfigDialog.js +96 -90
  16. package/dist/bigconsole/components/widgets/ActionConfigDialog.js.map +1 -1
  17. package/dist/bigconsole/components/widgets/WidgetMenu.js +87 -76
  18. package/dist/bigconsole/components/widgets/WidgetMenu.js.map +1 -1
  19. package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js +120 -118
  20. package/dist/bigconsole/components/widgets/widget-actions/WidgetActions.js.map +1 -1
  21. package/dist/bigconsole/pages/DashboardBuilderPage.js +113 -104
  22. package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
  23. package/dist/bigconsole/pages/DashboardViewPage.js +208 -199
  24. package/dist/bigconsole/pages/DashboardViewPage.js.map +1 -1
  25. package/dist/bigconsole/pages/DashboardsPage.js +376 -325
  26. package/dist/bigconsole/pages/DashboardsPage.js.map +1 -1
  27. package/dist/bigconsole/pages/DataParsersPage.js +135 -125
  28. package/dist/bigconsole/pages/DataParsersPage.js.map +1 -1
  29. package/dist/bigconsole/pages/DataSinkBuilderPage.js +182 -172
  30. package/dist/bigconsole/pages/DataSinkBuilderPage.js.map +1 -1
  31. package/dist/bigconsole/pages/DataSinkViewPage.js +233 -198
  32. package/dist/bigconsole/pages/DataSinkViewPage.js.map +1 -1
  33. package/dist/bigconsole/pages/DataSinksPage.js +281 -268
  34. package/dist/bigconsole/pages/DataSinksPage.js.map +1 -1
  35. package/dist/bigconsole/pages/ParserBuilderPage.js +238 -223
  36. package/dist/bigconsole/pages/ParserBuilderPage.js.map +1 -1
  37. package/dist/bigconsole/pages/ParserViewPage.js +162 -144
  38. package/dist/bigconsole/pages/ParserViewPage.js.map +1 -1
  39. package/dist/bigconsole/pages/PipelineBuilderPage.js +212 -204
  40. package/dist/bigconsole/pages/PipelineBuilderPage.js.map +1 -1
  41. package/dist/bigconsole/pages/PipelineViewPage.js +191 -183
  42. package/dist/bigconsole/pages/PipelineViewPage.js.map +1 -1
  43. package/dist/bigconsole/pages/PipelinesPage.js +128 -103
  44. package/dist/bigconsole/pages/PipelinesPage.js.map +1 -1
  45. package/dist/bigconsole/pages/WorkflowViewPage.js +238 -217
  46. package/dist/bigconsole/pages/WorkflowViewPage.js.map +1 -1
  47. package/dist/bigconsole/pages/WorkflowsPage.js +116 -102
  48. package/dist/bigconsole/pages/WorkflowsPage.js.map +1 -1
  49. package/dist/bigconsole/utils/csv.js +91 -0
  50. package/dist/bigconsole/utils/csv.js.map +1 -0
  51. package/dist/constants/permissions.js +83 -0
  52. package/dist/constants/permissions.js.map +1 -0
  53. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"WorkflowViewPage.js","names":[],"sources":["../../../src/bigconsole/pages/WorkflowViewPage.tsx"],"sourcesContent":["/**\n * Workflow View Page\n *\n * Page for viewing FluidGrids Workflow details, linked DataSinks,\n * read-only workflow design preview, and real execution with polling.\n */\n\nimport { memo, useState, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router';\nimport { GlassCard, IllustratedEmptyState, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeCopyText, nativeNotify, nativeToast } from '../../utils/nativeBridge';\nimport { useBigConsole } from '../context';\nimport { formatDistanceToNow } from 'date-fns';\nimport { useWorkflowOperations } from '../hooks/useWorkflowOperations';\nimport { WorkflowStatusBadge } from '../components/workflow/WorkflowStatusBadge';\nimport { WorkflowDeleteDialog } from '../components/workflow/WorkflowDeleteDialog';\nimport type { FluidGridsWorkflow } from '../types';\n\n// FluidGrids FE SDK — read-only embedded builder preview\nimport { WorkflowBuilder } from '@burdenoff/fluidgrids-fe-sdk';\nimport { BOOTSTRAP_RETRY_DELAY_MS } from '../constants';\n\n// ============================================================================\n// ICONS\n// ============================================================================\n\nconst ArrowLeftIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M15 19l-7-7 7-7\" />\n </svg>\n);\n\nconst EditIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"\n />\n </svg>\n);\n\nconst TrashIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n);\n\nconst PlayIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z\"\n />\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n);\n\nconst CopyIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n);\n\nconst CheckIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M5 13l4 4L19 7\" />\n </svg>\n);\n\nconst DatabaseIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4\"\n />\n </svg>\n);\n\nconst ErrorIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n);\n\nconst ChevronDownIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n);\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const WorkflowViewPage = memo(function WorkflowViewPage() {\n const navigate = useAppNavigate();\n const { workflowId } = useParams<{ workflowId: string }>();\n\n // BigConsole context for apiGatewayUrl and authToken\n const { apiGatewayUrl, authToken, workspaceId } = useBigConsole();\n\n // Local state\n const [workflow, setWorkflow] = useState<FluidGridsWorkflow | null>(null);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n const [, setIsDeleting] = useState(false);\n const [isExecuting, setIsExecuting] = useState(false);\n const [webhookCopied, setWebhookCopied] = useState(false);\n const [showDesignPreview, setShowDesignPreview] = useState(false);\n // Track whether initial fetch has completed — prevents \"not found\" flash\n // before data has even been requested (loading starts as false with skipFetch: true)\n const [isReady, setIsReady] = useState(false);\n const [executionResult, setExecutionResult] = useState<{\n success: boolean;\n output: unknown;\n error: string | null;\n executionTimeMs: number;\n runId?: string;\n } | null>(null);\n\n // Operations\n const { loading, error, fetchWorkflow, deleteWorkflow, executeWorkflow, pollWorkflowExecution } =\n useWorkflowOperations({\n skipFetch: true,\n });\n\n // Load workflow on mount.\n // On a full page reload the workspace token may not be issued yet when this\n // effect first runs, causing the gateway to lack workspace context and\n // the resolver to return null. We retry once after a short delay to give\n // the auth bootstrap time to complete.\n useEffect(() => {\n if (!workflowId) {\n setIsReady(true);\n return;\n }\n\n let cancelled = false;\n\n const load = async (attempt: number) => {\n try {\n const result = await fetchWorkflow(workflowId);\n if (cancelled) return;\n\n if (result) {\n setWorkflow(result);\n setIsReady(true);\n } else if (attempt === 0) {\n // First attempt failed — likely workspace token race on full-page load.\n // Retry once after a short delay.\n setTimeout(() => {\n if (!cancelled) load(1);\n }, BOOTSTRAP_RETRY_DELAY_MS);\n } else {\n // Second attempt also failed — genuinely not found\n setIsReady(true);\n }\n } catch {\n // Network error, GraphQL error, etc. — surface the \"not found\" UI\n // rather than spinning forever with no escape hatch.\n if (!cancelled) setIsReady(true);\n }\n };\n\n load(0);\n return () => {\n cancelled = true;\n };\n }, [workflowId, fetchWorkflow]);\n\n // Handlers\n const handleBack = useCallback(() => {\n navigate(`/workflows`);\n }, [navigate]);\n\n const handleEdit = useCallback(() => {\n navigate(`/workflows/${workflowId}/edit`);\n }, [navigate, workflowId]);\n\n const handleDelete = useCallback(async () => {\n if (!workflowId) return;\n setIsDeleting(true);\n try {\n const success = await deleteWorkflow(workflowId);\n if (success) {\n if (isNative()) void nativeNotify('success');\n navigate(`/workflows`);\n } else if (isNative()) {\n void nativeNotify('error');\n }\n } catch {\n if (isNative()) void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n setShowDeleteConfirm(false);\n }\n }, [workflowId, deleteWorkflow, navigate]);\n\n const handleExecute = useCallback(async () => {\n if (!workflowId) return;\n setIsExecuting(true);\n setExecutionResult(null);\n\n const startTime = Date.now();\n\n try {\n const result = await executeWorkflow(workflowId);\n\n if (result) {\n const executionTimeMs = Date.now() - startTime;\n\n // If workflow has external FluidGrids ID, the execution is async\n if (result.externalWorkflowId) {\n setExecutionResult({\n success: true,\n output: {\n externalWorkflowId: result.externalWorkflowId,\n executionCount: result.executionCount,\n status: 'Triggered — polling for completion...',\n },\n error: null,\n executionTimeMs,\n });\n\n // Poll for completion in the background\n pollWorkflowExecution(workflowId, {\n intervalMs: 3000,\n timeoutMs: 120000,\n onProgress: (updated) => {\n setWorkflow(updated);\n },\n }).then((final) => {\n if (final) {\n setWorkflow(final);\n setExecutionResult((prev) =>\n prev\n ? {\n ...prev,\n output: {\n ...((prev.output as Record<string, unknown>) || {}),\n status: final.status === 'error' ? 'Failed' : 'Completed',\n executionCount: final.executionCount,\n lastExecutedAt: final.lastExecutedAt,\n },\n executionTimeMs: Date.now() - startTime,\n }\n : prev\n );\n }\n });\n } else {\n // Local execution (no external FluidGrids workflow)\n setExecutionResult({\n success: true,\n output: {\n executionCount: result.executionCount,\n lastExecutedAt: result.lastExecutedAt,\n status: 'Completed',\n },\n error: null,\n executionTimeMs,\n });\n }\n\n // Refresh workflow\n const updated = await fetchWorkflow(workflowId);\n if (updated) {\n setWorkflow(updated);\n }\n }\n } catch (err) {\n setExecutionResult({\n success: false,\n output: null,\n error: err instanceof Error ? err.message : 'Execution failed',\n executionTimeMs: Date.now() - startTime,\n });\n } finally {\n setIsExecuting(false);\n }\n }, [workflowId, executeWorkflow, fetchWorkflow, pollWorkflowExecution]);\n\n const handleCopyWebhook = useCallback(async () => {\n if (!workflow?.webhookUrl) return;\n try {\n await nativeCopyText(workflow.webhookUrl);\n setWebhookCopied(true);\n if (isNative()) void nativeToast('Webhook copied');\n setTimeout(() => setWebhookCopied(false), 2000);\n } catch {\n // Clipboard not available\n }\n }, [workflow?.webhookUrl]);\n\n const handleViewDataSink = useCallback(\n (dataSinkId: string) => {\n navigate(`/datasinks/${dataSinkId}`);\n },\n [navigate]\n );\n\n // Loading state — show spinner until initial fetch completes OR during a refetch\n if (!isReady || (loading && !workflow)) {\n return (\n <div className=\"p-6 flex items-center justify-center min-h-[400px]\">\n <svg className=\"w-8 h-8 text-action-primary-bg animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n </div>\n );\n }\n\n // Error or not found state — only shown AFTER initial fetch completed\n if (error || !workflow) {\n return (\n <div className=\"p-6\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"inline-flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary mb-4\"\n >\n <ArrowLeftIcon className=\"w-4 h-4\" />\n Back to Workflows\n </button>\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"Workflow not found\"\n description={error?.message || 'The workflow you are looking for does not exist.'}\n />\n </div>\n );\n }\n\n const linkedDataSinkCount = workflow.dataSinkLinks?.length || 0;\n\n return (\n <div className=\"p-6 max-w-5xl mx-auto\">\n {/* Header */}\n <div className=\"mb-8\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"inline-flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary mb-4\"\n >\n <ArrowLeftIcon className=\"w-4 h-4\" />\n Back to Workflows\n </button>\n\n <div className=\"flex items-start justify-between\">\n <div>\n <div className=\"flex items-center gap-3 mb-2\">\n <h1 className=\"text-2xl font-bold text-text-primary\">{workflow.name}</h1>\n <WorkflowStatusBadge status={workflow.status} />\n </div>\n <p className=\"text-sm text-text-secondary font-mono\">{workflow.key}</p>\n {workflow.externalWorkflowId && (\n <p className=\"text-xs text-text-tertiary mt-1\">\n FluidGrids ID: <span className=\"font-mono\">{workflow.externalWorkflowId}</span>\n </p>\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleExecute}\n disabled={isExecuting}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-status-success-bg hover:bg-status-success-bg/90 rounded-md transition-colors disabled:opacity-50\"\n >\n <PlayIcon className=\"w-4 h-4\" />\n {isExecuting ? 'Executing...' : 'Execute'}\n </button>\n <button\n type=\"button\"\n onClick={handleEdit}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken hover:bg-border-default rounded-md transition-colors\"\n >\n <EditIcon className=\"w-4 h-4\" />\n Edit\n </button>\n <button\n type=\"button\"\n onClick={() => setShowDeleteConfirm(true)}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-status-error-text bg-status-error-bg/10 hover:bg-status-error-bg/20 rounded-md transition-colors\"\n >\n <TrashIcon className=\"w-4 h-4\" />\n Delete\n </button>\n </div>\n </div>\n </div>\n\n <PagePurpose className=\"mb-6\">\n A workflow is the visual ETL job that feeds this BigConsole — it fetches, transforms, and delivers data into\n linked data sinks. From here you can run it on demand, inspect its design and execution history, copy its\n webhook trigger, and edit it in the visual builder.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"bigconsole-workflow-view\"\n steps={[\n {\n id: 'execute',\n label: 'Run the workflow',\n description: 'Trigger an execution to pull fresh data now.',\n onClick: handleExecute,\n },\n {\n id: 'design',\n label: 'Inspect the design',\n description: 'Expand the read-only preview to see each step.',\n onClick: () => setShowDesignPreview(true),\n },\n {\n id: 'edit',\n label: 'Edit in the builder',\n description: 'Open the FluidGrids visual builder to change the flow.',\n onClick: handleEdit,\n },\n {\n id: 'sinks',\n label: 'Check the linked data sinks',\n description: 'See where this workflow writes its output.',\n onClick: () => navigate('/datasinks'),\n },\n ]}\n />\n\n {/* Execution Result */}\n {executionResult && (\n <div\n className={`mb-6 p-4 rounded-lg border ${\n executionResult.success\n ? 'bg-status-success-bg/10 border-status-success-border/20'\n : 'bg-status-error-bg/10 border-status-error-border/20'\n }`}\n >\n <div className=\"flex items-center justify-between mb-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {executionResult.success ? 'Execution Triggered' : 'Execution Failed'}\n </h3>\n <span className=\"text-xs text-text-secondary\">{executionResult.executionTimeMs}ms</span>\n </div>\n {executionResult.error ? (\n <p className=\"text-sm text-status-error-text\">{executionResult.error}</p>\n ) : (\n <pre className=\"text-xs text-text-secondary font-mono bg-bg-sunken p-2 rounded overflow-auto max-h-[200px]\">\n {JSON.stringify(executionResult.output, null, 2)}\n </pre>\n )}\n {executionResult.runId && (\n <p className=\"text-xs text-text-tertiary mt-2\">\n Run ID: <span className=\"font-mono\">{executionResult.runId}</span>\n </p>\n )}\n </div>\n )}\n\n {/* Info Grid */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6 mb-8\">\n {/* Description */}\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Description</h3>\n <p className=\"text-sm text-text-primary\">{workflow.description || 'No description'}</p>\n </div>\n\n {/* Execution Stats — primary emphasis zone */}\n <GlassCard treatment=\"glass\" glow className=\"p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Execution Stats</h3>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-2xl font-bold text-text-primary\">{workflow.executionCount}</p>\n <p className=\"text-xs text-text-secondary\">Total Executions</p>\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {workflow.lastExecutedAt\n ? formatDistanceToNow(new Date(workflow.lastExecutedAt), { addSuffix: true })\n : 'Never'}\n </p>\n <p className=\"text-xs text-text-secondary\">Last Executed</p>\n </div>\n </div>\n </GlassCard>\n </div>\n\n {/* Webhook URL */}\n {workflow.webhookUrl && (\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4 mb-8\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Webhook URL</h3>\n <div className=\"flex items-center gap-2\">\n <code className=\"flex-1 text-sm text-text-primary font-mono bg-bg-sunken px-3 py-2 rounded overflow-auto\">\n {workflow.webhookUrl}\n </code>\n <button\n type=\"button\"\n onClick={handleCopyWebhook}\n className=\"flex items-center gap-1 px-3 py-2 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n {webhookCopied ? (\n <>\n <CheckIcon className=\"w-4 h-4 text-status-success-text\" />\n Copied!\n </>\n ) : (\n <>\n <CopyIcon className=\"w-4 h-4\" />\n Copy\n </>\n )}\n </button>\n </div>\n </div>\n )}\n\n {/* Workflow Design Preview (collapsible) */}\n {workflow.externalWorkflowId && (\n <div className=\"bg-bg-surface border border-border-default rounded-lg mb-8 overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowDesignPreview((prev) => !prev)}\n className=\"w-full flex items-center justify-between px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <span>Workflow Design Preview</span>\n <ChevronDownIcon\n className={`w-4 h-4 text-text-secondary transition-transform ${showDesignPreview ? 'rotate-180' : ''}`}\n />\n </button>\n {showDesignPreview && (\n <div className=\"border-t border-border-default\" style={{ height: '450px' }}>\n <WorkflowBuilder\n apiGatewayUrl={apiGatewayUrl}\n workspaceId={workspaceId || 'default'}\n authToken={authToken}\n workflowId={workflow.externalWorkflowId}\n readonly\n hideHeader\n className=\"h-full\"\n />\n </div>\n )}\n </div>\n )}\n\n {/* Linked DataSinks */}\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4 mb-8\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-4\">Linked DataSinks ({linkedDataSinkCount})</h3>\n {linkedDataSinkCount === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No data sinks linked\"\n description=\"This workflow has no data sinks linked yet — its output isn't being stored.\"\n />\n ) : (\n <div className=\"space-y-2\">\n {workflow.dataSinkLinks?.map((link) => (\n <div key={link.id} className=\"flex items-center justify-between px-3 py-2 bg-bg-sunken rounded-md\">\n <div className=\"flex items-center gap-3\">\n <DatabaseIcon className=\"w-4 h-4 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {link.dataSink?.name || link.dataSink?.key || link.dataSinkId}\n </p>\n {link.dataSink?.key && <p className=\"text-xs text-text-secondary font-mono\">{link.dataSink.key}</p>}\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center gap-2\">\n {link.isEnabled ? (\n <span className=\"text-xs text-status-success-text\">Enabled</span>\n ) : (\n <span className=\"text-xs text-text-secondary\">Disabled</span>\n )}\n <span className=\"text-xs text-text-secondary\">Priority: {link.priority}</span>\n </div>\n {link.dataSink && (\n <button\n type=\"button\"\n onClick={() => handleViewDataSink(link.dataSinkId)}\n className=\"text-xs text-action-primary-bg hover:underline\"\n >\n View\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Timestamps */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Created</h3>\n <p className=\"text-sm text-text-primary\">\n {formatDistanceToNow(new Date(workflow.createdAt), { addSuffix: true })}\n </p>\n <p className=\"text-xs text-text-secondary\">by {workflow.createdBy}</p>\n </div>\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Last Updated</h3>\n <p className=\"text-sm text-text-primary\">\n {formatDistanceToNow(new Date(workflow.updatedAt), { addSuffix: true })}\n </p>\n <p className=\"text-xs text-text-secondary\">by {workflow.updatedBy || workflow.createdBy}</p>\n </div>\n </div>\n\n {/* Delete Confirmation Dialog */}\n {workflow && (\n <WorkflowDeleteDialog\n workflow={workflow}\n isOpen={showDeleteConfirm}\n onClose={() => setShowDeleteConfirm(false)}\n onConfirm={handleDelete}\n />\n )}\n </div>\n );\n});\n\nexport default WorkflowViewPage;\n"],"mappings":"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"}
1
+ {"version":3,"file":"WorkflowViewPage.js","names":[],"sources":["../../../src/bigconsole/pages/WorkflowViewPage.tsx"],"sourcesContent":["/**\n * Workflow View Page\n *\n * Page for viewing FluidGrids Workflow details, linked DataSinks,\n * read-only workflow design preview, and real execution with polling.\n */\n\nimport { memo, useState, useEffect, useCallback } from 'react';\nimport { useParams } from 'react-router';\nimport { GlassCard, IllustratedEmptyState, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport type { NextStep } from '@burdenoff/fe-libs/ui';\nimport { useAppNavigate } from '../contexts';\nimport { isNative, nativeCopyText, nativeNotify, nativeToast } from '../../utils/nativeBridge';\nimport { useBigConsole } from '../context';\nimport { formatDistanceToNow } from 'date-fns';\nimport { useWorkflowOperations } from '../hooks/useWorkflowOperations';\nimport { WorkflowStatusBadge } from '../components/workflow/WorkflowStatusBadge';\nimport { WorkflowDeleteDialog } from '../components/workflow/WorkflowDeleteDialog';\nimport type { FluidGridsWorkflow } from '../types';\n\n// FluidGrids FE SDK — read-only embedded builder preview\nimport { WorkflowBuilder } from '@burdenoff/fluidgrids-fe-sdk';\nimport { BOOTSTRAP_RETRY_DELAY_MS } from '../constants';\n\n// RBAC\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { AccessDenied, PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { BIGCONSOLE_PERMISSIONS } from '../../constants/permissions';\n\n// ============================================================================\n// ICONS\n// ============================================================================\n\nconst ArrowLeftIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M15 19l-7-7 7-7\" />\n </svg>\n);\n\nconst EditIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"\n />\n </svg>\n);\n\nconst TrashIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n);\n\nconst PlayIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z\"\n />\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n);\n\nconst CopyIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"\n />\n </svg>\n);\n\nconst CheckIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M5 13l4 4L19 7\" />\n </svg>\n);\n\nconst DatabaseIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4\"\n />\n </svg>\n);\n\nconst ErrorIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n);\n\nconst ChevronDownIcon = ({ className }: { className?: string }) => (\n <svg className={className} fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n);\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport const WorkflowViewPage = memo(function WorkflowViewPage() {\n const navigate = useAppNavigate();\n const { workflowId } = useParams<{ workflowId: string }>();\n\n // BigConsole context for apiGatewayUrl and authToken\n const { apiGatewayUrl, authToken, workspaceId } = useBigConsole();\n\n // RBAC\n const { hasPermission, isLoading: permissionsLoading } = usePermissions();\n const canRead = hasPermission(BIGCONSOLE_PERMISSIONS.WORKFLOW_READ);\n const canUpdate = hasPermission(BIGCONSOLE_PERMISSIONS.WORKFLOW_UPDATE);\n const canDelete = hasPermission(BIGCONSOLE_PERMISSIONS.WORKFLOW_DELETE);\n const canExecute = hasPermission(BIGCONSOLE_PERMISSIONS.WORKFLOW_EXECUTE);\n\n // Local state\n const [workflow, setWorkflow] = useState<FluidGridsWorkflow | null>(null);\n const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);\n const [, setIsDeleting] = useState(false);\n const [isExecuting, setIsExecuting] = useState(false);\n const [webhookCopied, setWebhookCopied] = useState(false);\n const [showDesignPreview, setShowDesignPreview] = useState(false);\n // Track whether initial fetch has completed — prevents \"not found\" flash\n // before data has even been requested (loading starts as false with skipFetch: true)\n const [isReady, setIsReady] = useState(false);\n const [executionResult, setExecutionResult] = useState<{\n success: boolean;\n output: unknown;\n error: string | null;\n executionTimeMs: number;\n runId?: string;\n } | null>(null);\n\n // Operations\n const { loading, error, fetchWorkflow, deleteWorkflow, executeWorkflow, pollWorkflowExecution } =\n useWorkflowOperations({\n skipFetch: true,\n });\n\n // Load workflow on mount.\n // On a full page reload the workspace token may not be issued yet when this\n // effect first runs, causing the gateway to lack workspace context and\n // the resolver to return null. We retry once after a short delay to give\n // the auth bootstrap time to complete.\n useEffect(() => {\n if (!workflowId) {\n setIsReady(true);\n return;\n }\n\n let cancelled = false;\n\n const load = async (attempt: number) => {\n try {\n const result = await fetchWorkflow(workflowId);\n if (cancelled) return;\n\n if (result) {\n setWorkflow(result);\n setIsReady(true);\n } else if (attempt === 0) {\n // First attempt failed — likely workspace token race on full-page load.\n // Retry once after a short delay.\n setTimeout(() => {\n if (!cancelled) load(1);\n }, BOOTSTRAP_RETRY_DELAY_MS);\n } else {\n // Second attempt also failed — genuinely not found\n setIsReady(true);\n }\n } catch {\n // Network error, GraphQL error, etc. — surface the \"not found\" UI\n // rather than spinning forever with no escape hatch.\n if (!cancelled) setIsReady(true);\n }\n };\n\n load(0);\n return () => {\n cancelled = true;\n };\n }, [workflowId, fetchWorkflow]);\n\n // Handlers\n const handleBack = useCallback(() => {\n navigate(`/workflows`);\n }, [navigate]);\n\n const handleEdit = useCallback(() => {\n navigate(`/workflows/${workflowId}/edit`);\n }, [navigate, workflowId]);\n\n const handleDelete = useCallback(async () => {\n if (!workflowId) return;\n setIsDeleting(true);\n try {\n const success = await deleteWorkflow(workflowId);\n if (success) {\n if (isNative()) void nativeNotify('success');\n navigate(`/workflows`);\n } else if (isNative()) {\n void nativeNotify('error');\n }\n } catch {\n if (isNative()) void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n setShowDeleteConfirm(false);\n }\n }, [workflowId, deleteWorkflow, navigate]);\n\n const handleExecute = useCallback(async () => {\n if (!workflowId) return;\n setIsExecuting(true);\n setExecutionResult(null);\n\n const startTime = Date.now();\n\n try {\n const result = await executeWorkflow(workflowId);\n\n if (result) {\n const executionTimeMs = Date.now() - startTime;\n\n // If workflow has external FluidGrids ID, the execution is async\n if (result.externalWorkflowId) {\n setExecutionResult({\n success: true,\n output: {\n externalWorkflowId: result.externalWorkflowId,\n executionCount: result.executionCount,\n status: 'Triggered — polling for completion...',\n },\n error: null,\n executionTimeMs,\n });\n\n // Poll for completion in the background\n pollWorkflowExecution(workflowId, {\n intervalMs: 3000,\n timeoutMs: 120000,\n onProgress: (updated) => {\n setWorkflow(updated);\n },\n }).then((final) => {\n if (final) {\n setWorkflow(final);\n setExecutionResult((prev) =>\n prev\n ? {\n ...prev,\n output: {\n ...((prev.output as Record<string, unknown>) || {}),\n status: final.status === 'error' ? 'Failed' : 'Completed',\n executionCount: final.executionCount,\n lastExecutedAt: final.lastExecutedAt,\n },\n executionTimeMs: Date.now() - startTime,\n }\n : prev\n );\n }\n });\n } else {\n // Local execution (no external FluidGrids workflow)\n setExecutionResult({\n success: true,\n output: {\n executionCount: result.executionCount,\n lastExecutedAt: result.lastExecutedAt,\n status: 'Completed',\n },\n error: null,\n executionTimeMs,\n });\n }\n\n // Refresh workflow\n const updated = await fetchWorkflow(workflowId);\n if (updated) {\n setWorkflow(updated);\n }\n }\n } catch (err) {\n setExecutionResult({\n success: false,\n output: null,\n error: err instanceof Error ? err.message : 'Execution failed',\n executionTimeMs: Date.now() - startTime,\n });\n } finally {\n setIsExecuting(false);\n }\n }, [workflowId, executeWorkflow, fetchWorkflow, pollWorkflowExecution]);\n\n const handleCopyWebhook = useCallback(async () => {\n if (!workflow?.webhookUrl) return;\n try {\n await nativeCopyText(workflow.webhookUrl);\n setWebhookCopied(true);\n if (isNative()) void nativeToast('Webhook copied');\n setTimeout(() => setWebhookCopied(false), 2000);\n } catch {\n // Clipboard not available\n }\n }, [workflow?.webhookUrl]);\n\n const handleViewDataSink = useCallback(\n (dataSinkId: string) => {\n navigate(`/datasinks/${dataSinkId}`);\n },\n [navigate]\n );\n\n // Loading state — show spinner until initial fetch completes OR during a\n // refetch, and while permissions are still loading (avoids a read-gate flash).\n if (!isReady || (loading && !workflow) || permissionsLoading) {\n return (\n <div className=\"p-6 flex items-center justify-center min-h-[400px]\">\n <svg className=\"w-8 h-8 text-action-primary-bg animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n />\n </svg>\n </div>\n );\n }\n\n // Read gate — no permission to view this workflow\n if (!canRead) {\n return <AccessDenied title=\"Access Denied\" description=\"You don't have permission to view this workflow.\" />;\n }\n\n // Error or not found state — only shown AFTER initial fetch completed\n if (error || !workflow) {\n return (\n <div className=\"p-6\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"inline-flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary mb-4\"\n >\n <ArrowLeftIcon className=\"w-4 h-4\" />\n Back to Workflows\n </button>\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"Workflow not found\"\n description={error?.message || 'The workflow you are looking for does not exist.'}\n />\n </div>\n );\n }\n\n const linkedDataSinkCount = workflow.dataSinkLinks?.length || 0;\n\n // Next-step guidance — omit steps that trigger a gated action the caller\n // cannot perform (execute → WORKFLOW_EXECUTE, edit → WORKFLOW_UPDATE).\n const nextSteps: NextStep[] = [\n ...(canExecute\n ? [\n {\n id: 'execute',\n label: 'Run the workflow',\n description: 'Trigger an execution to pull fresh data now.',\n onClick: handleExecute,\n },\n ]\n : []),\n {\n id: 'design',\n label: 'Inspect the design',\n description: 'Expand the read-only preview to see each step.',\n onClick: () => setShowDesignPreview(true),\n },\n ...(canUpdate\n ? [\n {\n id: 'edit',\n label: 'Edit in the builder',\n description: 'Open the FluidGrids visual builder to change the flow.',\n onClick: handleEdit,\n },\n ]\n : []),\n {\n id: 'sinks',\n label: 'Check the linked data sinks',\n description: 'See where this workflow writes its output.',\n onClick: () => navigate('/datasinks'),\n },\n ];\n\n return (\n <div className=\"p-6 max-w-5xl mx-auto\">\n {/* Header */}\n <div className=\"mb-8\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"inline-flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary mb-4\"\n >\n <ArrowLeftIcon className=\"w-4 h-4\" />\n Back to Workflows\n </button>\n\n <div className=\"flex items-start justify-between\">\n <div>\n <div className=\"flex items-center gap-3 mb-2\">\n <h1 className=\"text-2xl font-bold text-text-primary\">{workflow.name}</h1>\n <WorkflowStatusBadge status={workflow.status} />\n </div>\n <p className=\"text-sm text-text-secondary font-mono\">{workflow.key}</p>\n {workflow.externalWorkflowId && (\n <p className=\"text-xs text-text-tertiary mt-1\">\n FluidGrids ID: <span className=\"font-mono\">{workflow.externalWorkflowId}</span>\n </p>\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.WORKFLOW_EXECUTE} deniedBehavior=\"disable\">\n <button\n type=\"button\"\n onClick={handleExecute}\n disabled={isExecuting}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-status-success-bg hover:bg-status-success-bg/90 rounded-md transition-colors disabled:opacity-50\"\n >\n <PlayIcon className=\"w-4 h-4\" />\n {isExecuting ? 'Executing...' : 'Execute'}\n </button>\n </PermissionButton>\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.WORKFLOW_UPDATE} deniedBehavior=\"disable\">\n <button\n type=\"button\"\n onClick={handleEdit}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-text-primary bg-bg-sunken hover:bg-border-default rounded-md transition-colors\"\n >\n <EditIcon className=\"w-4 h-4\" />\n Edit\n </button>\n </PermissionButton>\n <PermissionButton permission={BIGCONSOLE_PERMISSIONS.WORKFLOW_DELETE} deniedBehavior=\"hide\">\n <button\n type=\"button\"\n onClick={() => setShowDeleteConfirm(true)}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-status-error-text bg-status-error-bg/10 hover:bg-status-error-bg/20 rounded-md transition-colors\"\n >\n <TrashIcon className=\"w-4 h-4\" />\n Delete\n </button>\n </PermissionButton>\n </div>\n </div>\n </div>\n\n <PagePurpose className=\"mb-6\">\n A workflow is the visual ETL job that feeds this BigConsole — it fetches, transforms, and delivers data into\n linked data sinks. From here you can run it on demand, inspect its design and execution history, copy its\n webhook trigger, and edit it in the visual builder.\n </PagePurpose>\n\n <NextSteps storageKey=\"bigconsole-workflow-view\" steps={nextSteps} />\n\n {/* Execution Result */}\n {executionResult && (\n <div\n className={`mb-6 p-4 rounded-lg border ${\n executionResult.success\n ? 'bg-status-success-bg/10 border-status-success-border/20'\n : 'bg-status-error-bg/10 border-status-error-border/20'\n }`}\n >\n <div className=\"flex items-center justify-between mb-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {executionResult.success ? 'Execution Triggered' : 'Execution Failed'}\n </h3>\n <span className=\"text-xs text-text-secondary\">{executionResult.executionTimeMs}ms</span>\n </div>\n {executionResult.error ? (\n <p className=\"text-sm text-status-error-text\">{executionResult.error}</p>\n ) : (\n <pre className=\"text-xs text-text-secondary font-mono bg-bg-sunken p-2 rounded overflow-auto max-h-[200px]\">\n {JSON.stringify(executionResult.output, null, 2)}\n </pre>\n )}\n {executionResult.runId && (\n <p className=\"text-xs text-text-tertiary mt-2\">\n Run ID: <span className=\"font-mono\">{executionResult.runId}</span>\n </p>\n )}\n </div>\n )}\n\n {/* Info Grid */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6 mb-8\">\n {/* Description */}\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Description</h3>\n <p className=\"text-sm text-text-primary\">{workflow.description || 'No description'}</p>\n </div>\n\n {/* Execution Stats — primary emphasis zone */}\n <GlassCard treatment=\"glass\" glow className=\"p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Execution Stats</h3>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-2xl font-bold text-text-primary\">{workflow.executionCount}</p>\n <p className=\"text-xs text-text-secondary\">Total Executions</p>\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {workflow.lastExecutedAt\n ? formatDistanceToNow(new Date(workflow.lastExecutedAt), { addSuffix: true })\n : 'Never'}\n </p>\n <p className=\"text-xs text-text-secondary\">Last Executed</p>\n </div>\n </div>\n </GlassCard>\n </div>\n\n {/* Webhook URL */}\n {workflow.webhookUrl && (\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4 mb-8\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Webhook URL</h3>\n <div className=\"flex items-center gap-2\">\n <code className=\"flex-1 text-sm text-text-primary font-mono bg-bg-sunken px-3 py-2 rounded overflow-auto\">\n {workflow.webhookUrl}\n </code>\n <button\n type=\"button\"\n onClick={handleCopyWebhook}\n className=\"flex items-center gap-1 px-3 py-2 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n {webhookCopied ? (\n <>\n <CheckIcon className=\"w-4 h-4 text-status-success-text\" />\n Copied!\n </>\n ) : (\n <>\n <CopyIcon className=\"w-4 h-4\" />\n Copy\n </>\n )}\n </button>\n </div>\n </div>\n )}\n\n {/* Workflow Design Preview (collapsible) */}\n {workflow.externalWorkflowId && (\n <div className=\"bg-bg-surface border border-border-default rounded-lg mb-8 overflow-hidden\">\n <button\n type=\"button\"\n onClick={() => setShowDesignPreview((prev) => !prev)}\n className=\"w-full flex items-center justify-between px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <span>Workflow Design Preview</span>\n <ChevronDownIcon\n className={`w-4 h-4 text-text-secondary transition-transform ${showDesignPreview ? 'rotate-180' : ''}`}\n />\n </button>\n {showDesignPreview && (\n <div className=\"border-t border-border-default\" style={{ height: '450px' }}>\n <WorkflowBuilder\n apiGatewayUrl={apiGatewayUrl}\n workspaceId={workspaceId || 'default'}\n authToken={authToken}\n workflowId={workflow.externalWorkflowId}\n readonly\n hideHeader\n className=\"h-full\"\n />\n </div>\n )}\n </div>\n )}\n\n {/* Linked DataSinks */}\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4 mb-8\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-4\">Linked DataSinks ({linkedDataSinkCount})</h3>\n {linkedDataSinkCount === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No data sinks linked\"\n description=\"This workflow has no data sinks linked yet — its output isn't being stored.\"\n />\n ) : (\n <div className=\"space-y-2\">\n {workflow.dataSinkLinks?.map((link) => (\n <div key={link.id} className=\"flex items-center justify-between px-3 py-2 bg-bg-sunken rounded-md\">\n <div className=\"flex items-center gap-3\">\n <DatabaseIcon className=\"w-4 h-4 text-text-secondary\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {link.dataSink?.name || link.dataSink?.key || link.dataSinkId}\n </p>\n {link.dataSink?.key && <p className=\"text-xs text-text-secondary font-mono\">{link.dataSink.key}</p>}\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center gap-2\">\n {link.isEnabled ? (\n <span className=\"text-xs text-status-success-text\">Enabled</span>\n ) : (\n <span className=\"text-xs text-text-secondary\">Disabled</span>\n )}\n <span className=\"text-xs text-text-secondary\">Priority: {link.priority}</span>\n </div>\n {link.dataSink && (\n <button\n type=\"button\"\n onClick={() => handleViewDataSink(link.dataSinkId)}\n className=\"text-xs text-action-primary-bg hover:underline\"\n >\n View\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Timestamps */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Created</h3>\n <p className=\"text-sm text-text-primary\">\n {formatDistanceToNow(new Date(workflow.createdAt), { addSuffix: true })}\n </p>\n <p className=\"text-xs text-text-secondary\">by {workflow.createdBy}</p>\n </div>\n <div className=\"bg-bg-surface border border-border-default rounded-lg p-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">Last Updated</h3>\n <p className=\"text-sm text-text-primary\">\n {formatDistanceToNow(new Date(workflow.updatedAt), { addSuffix: true })}\n </p>\n <p className=\"text-xs text-text-secondary\">by {workflow.updatedBy || workflow.createdBy}</p>\n </div>\n </div>\n\n {/* Delete Confirmation Dialog */}\n {workflow && (\n <WorkflowDeleteDialog\n workflow={workflow}\n isOpen={showDeleteConfirm}\n onClose={() => setShowDeleteConfirm(false)}\n onConfirm={handleDelete}\n />\n )}\n </div>\n );\n});\n\nexport default WorkflowViewPage;\n"],"mappings":"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"}
@@ -3,59 +3,65 @@ import { useAppNavigate as t } from "../contexts/NavigationContext.js";
3
3
  import "../contexts/index.js";
4
4
  import n from "../hooks/useWorkflowOperations.js";
5
5
  import { newWorkflowPath as r } from "../BigConsoleRoutes.js";
6
- import { isNative as i, nativeNotify as a } from "../../utils/nativeBridge.js";
7
- import o from "../components/workflow/WorkflowDeleteDialog.js";
8
- import s from "../components/workflow/WorkflowCard.js";
9
- import { memo as c, useCallback as l, useMemo as u, useState as d } from "react";
10
- import { AlertCircle as f, Plus as p, Search as m, Workflow as h } from "lucide-react";
11
- import { AuroraBackground as g, Button as _, Card as v, CardContent as y, GlassCard as b, GradientText as x, IllustratedEmptyState as S, Input as C, PagePurpose as w, Spinner as T } from "@burdenoff/fe-libs/ui";
12
- import { jsx as E, jsxs as D } from "react/jsx-runtime";
6
+ import { BIGCONSOLE_PERMISSIONS as i } from "../../constants/permissions.js";
7
+ import { isNative as a, nativeNotify as o } from "../../utils/nativeBridge.js";
8
+ import s from "../components/workflow/WorkflowDeleteDialog.js";
9
+ import c from "../components/workflow/WorkflowCard.js";
10
+ import { memo as l, useCallback as u, useMemo as d, useState as f } from "react";
11
+ import { AccessDenied as p, PermissionButton as m } from "@burdenoff/fe-libs/shared/components";
12
+ import { usePermissions as h } from "@burdenoff/fe-libs/shared/providers/shell";
13
+ import { AlertCircle as g, Plus as _, Search as v, Workflow as y } from "lucide-react";
14
+ import { AuroraBackground as b, Button as x, Card as S, CardContent as C, GlassCard as w, GradientText as T, IllustratedEmptyState as E, Input as D, PagePurpose as O, Spinner as k } from "@burdenoff/fe-libs/ui";
15
+ import { jsx as A, jsxs as j } from "react/jsx-runtime";
13
16
  //#region src/bigconsole/pages/WorkflowsPage.tsx
14
- var O = c(function() {
15
- let c = t(), [O, k] = d("all"), A = e((e) => e.searchQuery), j = e((e) => e.setSearchQuery), M = e((e) => e.deleteDialogOpen), N = e((e) => e.currentWorkflow), P = e((e) => e.openDeleteDialog), F = e((e) => e.closeDeleteDialog), { loading: I, error: L, workflows: R, deleteWorkflow: z, refetch: B } = n(), V = u(() => {
16
- let e = [...R];
17
- if (O !== "all" && (e = e.filter((e) => e.status === O)), A.trim()) {
18
- let t = A.toLowerCase();
17
+ var M = l(function() {
18
+ let l = t(), { hasPermission: M, isLoading: N } = h(), P = M(i.WORKFLOW_READ), F = M(i.WORKFLOW_UPDATE), I = M(i.WORKFLOW_DELETE), [L, R] = f("all"), z = e((e) => e.searchQuery), B = e((e) => e.setSearchQuery), V = e((e) => e.deleteDialogOpen), H = e((e) => e.currentWorkflow), U = e((e) => e.openDeleteDialog), W = e((e) => e.closeDeleteDialog), { loading: G, error: K, workflows: q, deleteWorkflow: J, refetch: Y } = n(), X = d(() => {
19
+ let e = [...q];
20
+ if (L !== "all" && (e = e.filter((e) => e.status === L)), z.trim()) {
21
+ let t = z.toLowerCase();
19
22
  e = e.filter((e) => e.name.toLowerCase().includes(t) || e.key.toLowerCase().includes(t) || e.description?.toLowerCase().includes(t));
20
23
  }
21
24
  return e.sort((e, t) => new Date(t.updatedAt).getTime() - new Date(e.updatedAt).getTime());
22
25
  }, [
23
- R,
24
- O,
25
- A
26
- ]), H = l(() => {
27
- c(r());
28
- }, [c]), U = l((e) => {
29
- c(`/workflows/${e}`);
30
- }, [c]), W = l((e) => {
31
- c(`/workflows/${e}/edit`);
32
- }, [c]), G = l((e) => {
33
- let t = R.find((t) => t.id === e);
34
- t && P(t);
35
- }, [R, P]), K = l(async () => {
36
- if (N) {
26
+ q,
27
+ L,
28
+ z
29
+ ]), Z = u(() => {
30
+ l(r());
31
+ }, [l]), Q = u((e) => {
32
+ l(`/workflows/${e}`);
33
+ }, [l]), $ = u((e) => {
34
+ l(`/workflows/${e}/edit`);
35
+ }, [l]), ee = u((e) => {
36
+ let t = q.find((t) => t.id === e);
37
+ t && U(t);
38
+ }, [q, U]), te = u(async () => {
39
+ if (H) {
37
40
  try {
38
- await z(N.id), i() && a("success");
41
+ await J(H.id), a() && o("success");
39
42
  } catch (e) {
40
- throw i() && a("error"), e;
43
+ throw a() && o("error"), e;
41
44
  }
42
- F();
45
+ W();
43
46
  }
44
47
  }, [
45
- N,
46
- z,
47
- F
48
+ H,
49
+ J,
50
+ W
48
51
  ]);
49
- return L ? /* @__PURE__ */ E("div", {
52
+ return !N && !P ? /* @__PURE__ */ A(p, {
53
+ title: "Access Denied",
54
+ description: "You don't have permission to view workflows."
55
+ }) : K ? /* @__PURE__ */ A("div", {
50
56
  style: { padding: "var(--space-pagePadding)" },
51
- children: /* @__PURE__ */ E(v, {
57
+ children: /* @__PURE__ */ A(S, {
52
58
  style: {
53
59
  borderColor: "var(--color-status-error-border)",
54
60
  backgroundColor: "var(--color-status-error-bgSubtle)"
55
61
  },
56
- children: /* @__PURE__ */ E(y, {
62
+ children: /* @__PURE__ */ A(C, {
57
63
  style: { padding: "var(--space-pagePadding)" },
58
- children: /* @__PURE__ */ D("div", {
64
+ children: /* @__PURE__ */ j("div", {
59
65
  style: {
60
66
  display: "flex",
61
67
  flexDirection: "column",
@@ -63,20 +69,20 @@ var O = c(function() {
63
69
  textAlign: "center"
64
70
  },
65
71
  children: [
66
- /* @__PURE__ */ E("div", {
72
+ /* @__PURE__ */ A("div", {
67
73
  style: {
68
74
  padding: "var(--space-controlGap)",
69
75
  borderRadius: "50%",
70
76
  backgroundColor: "var(--color-status-error-bgSubtle)",
71
77
  marginBottom: "var(--space-formGap)"
72
78
  },
73
- children: /* @__PURE__ */ E(f, { style: {
79
+ children: /* @__PURE__ */ A(g, { style: {
74
80
  height: "var(--size-icon-xl)",
75
81
  width: "var(--size-icon-xl)",
76
82
  color: "var(--color-status-error-text)"
77
83
  } })
78
84
  }),
79
- /* @__PURE__ */ E("h3", {
85
+ /* @__PURE__ */ A("h3", {
80
86
  style: {
81
87
  fontSize: "var(--font-size-body-lg)",
82
88
  fontWeight: "var(--font-weight-medium)",
@@ -85,46 +91,46 @@ var O = c(function() {
85
91
  },
86
92
  children: "Failed to load Workflows"
87
93
  }),
88
- /* @__PURE__ */ E("p", {
94
+ /* @__PURE__ */ A("p", {
89
95
  style: {
90
96
  fontSize: "var(--font-size-body-sm)",
91
97
  color: "var(--color-text-secondary)",
92
98
  marginBottom: "var(--space-formGap)"
93
99
  },
94
- children: L.message
100
+ children: K.message
95
101
  }),
96
- /* @__PURE__ */ E(_, {
102
+ /* @__PURE__ */ A(x, {
97
103
  size: "sm",
98
- onClick: () => B(),
104
+ onClick: () => Y(),
99
105
  children: "Try Again"
100
106
  })
101
107
  ]
102
108
  })
103
109
  })
104
110
  })
105
- }) : /* @__PURE__ */ D("div", {
111
+ }) : /* @__PURE__ */ j("div", {
106
112
  style: {
107
113
  minHeight: "100vh",
108
114
  backgroundColor: "var(--color-bg-canvas)"
109
115
  },
110
116
  children: [
111
- /* @__PURE__ */ D("div", {
117
+ /* @__PURE__ */ j("div", {
112
118
  className: "relative overflow-hidden border-b border-border-seam bg-gloss-card",
113
- children: [/* @__PURE__ */ E(g, { intensity: "subtle" }), /* @__PURE__ */ E("div", {
119
+ children: [/* @__PURE__ */ A(b, { intensity: "subtle" }), /* @__PURE__ */ A("div", {
114
120
  style: { padding: "var(--space-pagePadding)" },
115
- children: /* @__PURE__ */ D("div", {
121
+ children: /* @__PURE__ */ j("div", {
116
122
  style: {
117
123
  display: "flex",
118
124
  alignItems: "center",
119
125
  justifyContent: "space-between"
120
126
  },
121
- children: [/* @__PURE__ */ D("div", {
127
+ children: [/* @__PURE__ */ j("div", {
122
128
  style: {
123
129
  display: "flex",
124
130
  alignItems: "center",
125
131
  gap: "var(--space-formGap)"
126
132
  },
127
- children: [/* @__PURE__ */ E("div", {
133
+ children: [/* @__PURE__ */ A("div", {
128
134
  style: {
129
135
  display: "flex",
130
136
  height: "var(--size-controlHeight-lg)",
@@ -134,20 +140,20 @@ var O = c(function() {
134
140
  borderRadius: "var(--radius-card)",
135
141
  backgroundColor: "var(--color-action-primary-bg)"
136
142
  },
137
- children: /* @__PURE__ */ E(h, { style: {
143
+ children: /* @__PURE__ */ A(y, { style: {
138
144
  height: "var(--size-icon-lg)",
139
145
  width: "var(--size-icon-lg)",
140
146
  color: "var(--color-action-primary-text)"
141
147
  } })
142
- }), /* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E("h1", {
148
+ }), /* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("h1", {
143
149
  style: {
144
150
  fontSize: "var(--font-size-heading-h4)",
145
151
  fontWeight: "var(--font-weight-bold)",
146
152
  margin: 0,
147
153
  color: "var(--color-text-primary)"
148
154
  },
149
- children: /* @__PURE__ */ E(x, { children: "Workflows" })
150
- }), /* @__PURE__ */ E("p", {
155
+ children: /* @__PURE__ */ A(T, { children: "Workflows" })
156
+ }), /* @__PURE__ */ A("p", {
151
157
  style: {
152
158
  fontSize: "var(--font-size-body-sm)",
153
159
  color: "var(--color-text-secondary)",
@@ -155,34 +161,38 @@ var O = c(function() {
155
161
  },
156
162
  children: "Design ETL workflows with FluidGrids and link them to DataSinks"
157
163
  })] })]
158
- }), /* @__PURE__ */ E("div", {
164
+ }), /* @__PURE__ */ A("div", {
159
165
  style: {
160
166
  display: "flex",
161
167
  alignItems: "center",
162
168
  gap: "var(--space-controlGap)"
163
169
  },
164
- children: /* @__PURE__ */ D(_, {
165
- onClick: H,
166
- children: [/* @__PURE__ */ E(p, { style: {
167
- marginRight: "var(--space-iconGap)",
168
- height: "var(--size-icon-sm)",
169
- width: "var(--size-icon-sm)"
170
- } }), "Design Workflow"]
170
+ children: /* @__PURE__ */ A(m, {
171
+ permission: i.WORKFLOW_CREATE,
172
+ deniedBehavior: "disable",
173
+ children: /* @__PURE__ */ j(x, {
174
+ onClick: Z,
175
+ children: [/* @__PURE__ */ A(_, { style: {
176
+ marginRight: "var(--space-iconGap)",
177
+ height: "var(--size-icon-sm)",
178
+ width: "var(--size-icon-sm)"
179
+ } }), "Design Workflow"]
180
+ })
171
181
  })
172
182
  })]
173
183
  })
174
184
  })]
175
185
  }),
176
- /* @__PURE__ */ D("div", {
186
+ /* @__PURE__ */ j("div", {
177
187
  style: { padding: "var(--space-pagePadding)" },
178
188
  children: [
179
- /* @__PURE__ */ E(w, {
189
+ /* @__PURE__ */ A(O, {
180
190
  className: "mb-[var(--space-sectionGap)]",
181
191
  children: "Workflows are visual ETL jobs built with the FluidGrids builder — they fetch data from external systems, transform it, and load it into your BigConsole DataSinks on a schedule or on demand. Use them to keep dashboards fed with fresh, shaped data without writing glue code."
182
192
  }),
183
- /* @__PURE__ */ E("div", {
193
+ /* @__PURE__ */ A("div", {
184
194
  style: { marginBottom: "var(--space-formGap)" },
185
- children: /* @__PURE__ */ E("div", {
195
+ children: /* @__PURE__ */ A("div", {
186
196
  style: {
187
197
  display: "flex",
188
198
  alignItems: "center",
@@ -217,9 +227,9 @@ var O = c(function() {
217
227
  value: "archived",
218
228
  label: "Archived"
219
229
  }
220
- ].map((e) => /* @__PURE__ */ E("button", {
230
+ ].map((e) => /* @__PURE__ */ A("button", {
221
231
  type: "button",
222
- onClick: () => k(e.value),
232
+ onClick: () => R(e.value),
223
233
  style: {
224
234
  padding: "var(--space-controlGap) var(--space-formGap)",
225
235
  fontSize: "var(--font-size-body-sm)",
@@ -228,22 +238,22 @@ var O = c(function() {
228
238
  border: "none",
229
239
  cursor: "pointer",
230
240
  transition: "all 0.2s ease",
231
- backgroundColor: O === e.value ? "var(--color-bg-surface)" : "transparent",
232
- color: O === e.value ? "var(--color-text-primary)" : "var(--color-text-secondary)",
233
- boxShadow: O === e.value ? "0 1px 2px rgba(0,0,0,0.05)" : "none"
241
+ backgroundColor: L === e.value ? "var(--color-bg-surface)" : "transparent",
242
+ color: L === e.value ? "var(--color-text-primary)" : "var(--color-text-secondary)",
243
+ boxShadow: L === e.value ? "0 1px 2px rgba(0,0,0,0.05)" : "none"
234
244
  },
235
245
  children: e.label
236
246
  }, e.value))
237
247
  })
238
248
  }),
239
- /* @__PURE__ */ E("div", {
249
+ /* @__PURE__ */ A("div", {
240
250
  style: { marginBottom: "var(--space-sectionGap)" },
241
- children: /* @__PURE__ */ D("div", {
251
+ children: /* @__PURE__ */ j("div", {
242
252
  style: {
243
253
  position: "relative",
244
254
  maxWidth: "400px"
245
255
  },
246
- children: [/* @__PURE__ */ E(m, { style: {
256
+ children: [/* @__PURE__ */ A(v, { style: {
247
257
  position: "absolute",
248
258
  left: "var(--space-controlGap)",
249
259
  top: "50%",
@@ -251,11 +261,11 @@ var O = c(function() {
251
261
  height: "var(--size-icon-sm)",
252
262
  width: "var(--size-icon-sm)",
253
263
  color: "var(--color-text-muted)"
254
- } }), /* @__PURE__ */ E(C, {
264
+ } }), /* @__PURE__ */ A(D, {
255
265
  type: "text",
256
266
  placeholder: "Search workflows...",
257
- value: A,
258
- onChange: (e) => j(e.target.value),
267
+ value: z,
268
+ onChange: (e) => B(e.target.value),
259
269
  style: {
260
270
  paddingLeft: "var(--size-controlHeight-md)",
261
271
  height: "var(--size-controlHeight-md)"
@@ -263,20 +273,20 @@ var O = c(function() {
263
273
  })]
264
274
  })
265
275
  }),
266
- I && R.length === 0 && /* @__PURE__ */ E("div", {
276
+ G && q.length === 0 && /* @__PURE__ */ A("div", {
267
277
  style: {
268
278
  display: "flex",
269
279
  alignItems: "center",
270
280
  justifyContent: "center",
271
281
  padding: "80px 0"
272
282
  },
273
- children: /* @__PURE__ */ D("div", {
283
+ children: /* @__PURE__ */ j("div", {
274
284
  style: { textAlign: "center" },
275
- children: [/* @__PURE__ */ E(T, { style: {
285
+ children: [/* @__PURE__ */ A(k, { style: {
276
286
  height: "var(--size-icon-xl)",
277
287
  width: "var(--size-icon-xl)",
278
288
  margin: "0 auto var(--space-formGap)"
279
- } }), /* @__PURE__ */ E("p", {
289
+ } }), /* @__PURE__ */ A("p", {
280
290
  style: {
281
291
  fontSize: "var(--font-size-body-sm)",
282
292
  color: "var(--color-text-muted)"
@@ -285,57 +295,61 @@ var O = c(function() {
285
295
  })]
286
296
  })
287
297
  }),
288
- !I && R.length === 0 && /* @__PURE__ */ E(b, {
298
+ !G && q.length === 0 && /* @__PURE__ */ A(w, {
289
299
  treatment: "insight",
290
300
  className: "border-border-seam",
291
- children: /* @__PURE__ */ E(S, {
301
+ children: /* @__PURE__ */ A(E, {
292
302
  illustration: "empty-data",
293
303
  title: "No Workflows yet",
294
304
  description: "Design ETL workflows with the FluidGrids visual builder to fetch, transform, and deliver data into BigConsole DataSinks.",
295
- action: /* @__PURE__ */ D(_, {
296
- onClick: H,
297
- children: [/* @__PURE__ */ E(p, { style: {
298
- marginRight: "var(--space-iconGap)",
299
- height: "var(--size-icon-sm)",
300
- width: "var(--size-icon-sm)"
301
- } }), "Design Workflow"]
305
+ action: /* @__PURE__ */ A(m, {
306
+ permission: i.WORKFLOW_CREATE,
307
+ deniedBehavior: "disable",
308
+ children: /* @__PURE__ */ j(x, {
309
+ onClick: Z,
310
+ children: [/* @__PURE__ */ A(_, { style: {
311
+ marginRight: "var(--space-iconGap)",
312
+ height: "var(--size-icon-sm)",
313
+ width: "var(--size-icon-sm)"
314
+ } }), "Design Workflow"]
315
+ })
302
316
  })
303
317
  })
304
318
  }),
305
- !I && V.length === 0 && R.length > 0 && /* @__PURE__ */ E(b, {
319
+ !G && X.length === 0 && q.length > 0 && /* @__PURE__ */ A(w, {
306
320
  treatment: "insight",
307
321
  className: "border-border-seam",
308
- children: /* @__PURE__ */ E(S, {
322
+ children: /* @__PURE__ */ A(E, {
309
323
  illustration: "empty-search",
310
324
  title: "No matching workflows",
311
325
  description: "No workflows match your filters"
312
326
  })
313
327
  }),
314
- V.length > 0 && /* @__PURE__ */ E("div", {
328
+ X.length > 0 && /* @__PURE__ */ A("div", {
315
329
  style: {
316
330
  display: "grid",
317
331
  gridTemplateColumns: "repeat(auto-fill, minmax(320px, 1fr))",
318
332
  gap: "var(--space-formGap)"
319
333
  },
320
- children: V.map((e) => /* @__PURE__ */ E(s, {
334
+ children: X.map((e) => /* @__PURE__ */ A(c, {
321
335
  workflow: e,
322
- onView: U,
323
- onEdit: W,
324
- onDelete: G
336
+ onView: Q,
337
+ onEdit: F ? $ : void 0,
338
+ onDelete: I ? ee : void 0
325
339
  }, e.id))
326
340
  })
327
341
  ]
328
342
  }),
329
- N && /* @__PURE__ */ E(o, {
330
- workflow: N,
331
- isOpen: M,
332
- onClose: F,
333
- onConfirm: K
343
+ H && /* @__PURE__ */ A(s, {
344
+ workflow: H,
345
+ isOpen: V,
346
+ onClose: W,
347
+ onConfirm: te
334
348
  })
335
349
  ]
336
350
  });
337
351
  });
338
352
  //#endregion
339
- export { O as WorkflowsPage };
353
+ export { M as WorkflowsPage };
340
354
 
341
355
  //# sourceMappingURL=WorkflowsPage.js.map