@shipfox/client-workflows 12.0.2 → 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/main.ts +16 -1
- package/.storybook/preview.tsx +3 -3
- package/.storybook/workspace-source.ts +31 -0
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +79 -0
- package/dist/components/job-graph/job-duration-format.d.ts +2 -2
- package/dist/components/job-graph/job-duration-format.d.ts.map +1 -1
- package/dist/components/job-graph/job-duration-format.js +4 -2
- package/dist/components/job-graph/job-duration-format.js.map +1 -1
- package/dist/components/job-graph/job-duration-label.js +1 -1
- package/dist/components/job-graph/job-duration-label.js.map +1 -1
- package/dist/components/job-graph/job-graph-content.d.ts +2 -1
- package/dist/components/job-graph/job-graph-content.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-content.js +3 -3
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/job-graph/job-graph-view.d.ts +2 -1
- package/dist/components/job-graph/job-graph-view.d.ts.map +1 -1
- package/dist/components/job-graph/job-graph-view.js +2 -2
- package/dist/components/job-graph/job-graph-view.js.map +1 -1
- package/dist/components/job-graph/job-node.d.ts +1 -1
- package/dist/components/job-graph/job-node.d.ts.map +1 -1
- package/dist/components/job-graph/job-node.js +10 -9
- package/dist/components/job-graph/job-node.js.map +1 -1
- package/dist/components/job-graph/types.d.ts +2 -1
- package/dist/components/job-graph/types.d.ts.map +1 -1
- package/dist/components/job-graph/types.js.map +1 -1
- package/dist/components/step-list/step-list.d.ts +2 -2
- package/dist/components/step-list/step-list.d.ts.map +1 -1
- package/dist/components/step-list/step-list.js.map +1 -1
- package/dist/components/workflow-run-duration-label.js +1 -1
- package/dist/components/workflow-run-duration-label.js.map +1 -1
- package/dist/components/workflow-run-list/job-status-strip.d.ts +21 -0
- package/dist/components/workflow-run-list/job-status-strip.d.ts.map +1 -0
- package/dist/components/workflow-run-list/job-status-strip.js +185 -0
- package/dist/components/workflow-run-list/job-status-strip.js.map +1 -0
- package/dist/components/workflow-run-list/run-display.d.ts +27 -3
- package/dist/components/workflow-run-list/run-display.d.ts.map +1 -1
- package/dist/components/workflow-run-list/run-display.js +78 -7
- package/dist/components/workflow-run-list/run-display.js.map +1 -1
- package/dist/components/workflow-run-list/types.d.ts +23 -24
- package/dist/components/workflow-run-list/types.d.ts.map +1 -1
- package/dist/components/workflow-run-list/types.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts +22 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.d.ts.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js +68 -0
- package/dist/components/workflow-run-list/workflow-run-filter-menu.js.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filters.d.ts +18 -0
- package/dist/components/workflow-run-list/workflow-run-filters.d.ts.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-filters.js +236 -0
- package/dist/components/workflow-run-list/workflow-run-filters.js.map +1 -0
- package/dist/components/workflow-run-list/workflow-run-list-content.d.ts +8 -4
- package/dist/components/workflow-run-list/workflow-run-list-content.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-content.js +40 -12
- package/dist/components/workflow-run-list/workflow-run-list-content.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.d.ts +29 -2
- package/dist/components/workflow-run-list/workflow-run-list-states.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +103 -44
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-view.js +60 -43
- package/dist/components/workflow-run-list/workflow-run-list-view.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list.js +20 -8
- package/dist/components/workflow-run-list/workflow-run-list.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts +19 -9
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +179 -112
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-number-label.d.ts +15 -0
- package/dist/components/workflow-run-number-label.d.ts.map +1 -0
- package/dist/components/workflow-run-number-label.js +41 -0
- package/dist/components/workflow-run-number-label.js.map +1 -0
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts +3 -3
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js +10 -9
- package/dist/components/workflow-run-summary/workflow-run-attempt-switcher.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts +4 -9
- package/dist/components/workflow-run-summary/workflow-run-summary.d.ts.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +107 -74
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-tabs/index.d.ts +6 -0
- package/dist/components/workflow-run-tabs/index.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/index.js +7 -0
- package/dist/components/workflow-run-tabs/index.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts +11 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js +71 -0
- package/dist/components/workflow-run-tabs/run-annotation-summary-line.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts +2 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.js +11 -0
- package/dist/components/workflow-run-tabs/run-annotations-empty.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.d.ts +18 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.js +107 -0
- package/dist/components/workflow-run-tabs/run-jobs-list.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-count.d.ts +6 -0
- package/dist/components/workflow-run-tabs/run-tab-count.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-count.js +27 -0
- package/dist/components/workflow-run-tabs/run-tab-count.js.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.d.ts +8 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.d.ts.map +1 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.js +82 -0
- package/dist/components/workflow-run-tabs/run-tab-strip.js.map +1 -0
- package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts +2 -2
- package/dist/components/workflow-run-view/agent-config-failure-callout.d.ts.map +1 -1
- package/dist/components/workflow-run-view/agent-config-failure-callout.js +4 -4
- package/dist/components/workflow-run-view/agent-config-failure-callout.js.map +1 -1
- package/dist/components/workflow-run-view/job-card.d.ts +2 -2
- package/dist/components/workflow-run-view/job-card.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-card.js +51 -32
- package/dist/components/workflow-run-view/job-card.js.map +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.d.ts.map +1 -1
- package/dist/components/workflow-run-view/job-execution-switcher.js +20 -8
- package/dist/components/workflow-run-view/job-execution-switcher.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-selection.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-selection.js +40 -1
- package/dist/components/workflow-run-view/workflow-run-selection.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.d.ts +1 -0
- package/dist/components/workflow-run-view/workflow-run-states.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-states.js +10 -1
- package/dist/components/workflow-run-view/workflow-run-states.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts +10 -5
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +136 -85
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/components/workflow-source-panel/index.d.ts +1 -0
- package/dist/components/workflow-source-panel/index.d.ts.map +1 -1
- package/dist/components/workflow-source-panel/index.js +1 -0
- package/dist/components/workflow-source-panel/index.js.map +1 -1
- package/dist/components/workflow-source-panel/workflow-source-content.d.ts +12 -0
- package/dist/components/workflow-source-panel/workflow-source-content.d.ts.map +1 -0
- package/dist/components/workflow-source-panel/workflow-source-content.js +53 -0
- package/dist/components/workflow-source-panel/workflow-source-content.js.map +1 -0
- package/dist/components/workflow-source-panel/workflow-source-panel.d.ts +1 -1
- package/dist/components/workflow-source-panel/workflow-source-panel.d.ts.map +1 -1
- package/dist/components/workflow-source-panel/workflow-source-panel.js +7 -54
- package/dist/components/workflow-source-panel/workflow-source-panel.js.map +1 -1
- package/dist/components/workflow-status/status-visuals.d.ts +4 -4
- package/dist/components/workflow-status/status-visuals.d.ts.map +1 -1
- package/dist/components/workflow-status/status-visuals.js +7 -0
- package/dist/components/workflow-status/status-visuals.js.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.d.ts +3 -4
- package/dist/components/workflow-status/workflow-status-icon.d.ts.map +1 -1
- package/dist/components/workflow-status/workflow-status-icon.js +6 -4
- package/dist/components/workflow-status/workflow-status-icon.js.map +1 -1
- package/dist/core/entities/job-execution.d.ts +10 -0
- package/dist/core/entities/job-execution.d.ts.map +1 -1
- package/dist/core/entities/job-execution.js +11 -0
- package/dist/core/entities/job-execution.js.map +1 -1
- package/dist/core/entities/job.d.ts +4 -2
- package/dist/core/entities/job.d.ts.map +1 -1
- package/dist/core/entities/job.js +8 -1
- package/dist/core/entities/job.js.map +1 -1
- package/dist/core/entities/step.d.ts +1 -1
- package/dist/core/entities/step.d.ts.map +1 -1
- package/dist/core/entities/step.js +2 -0
- package/dist/core/entities/step.js.map +1 -1
- package/dist/core/entities/workflow-run.d.ts +52 -4
- package/dist/core/entities/workflow-run.d.ts.map +1 -1
- package/dist/core/entities/workflow-run.js +22 -3
- package/dist/core/entities/workflow-run.js.map +1 -1
- package/dist/core/workflow-run-tabs.d.ts +9 -0
- package/dist/core/workflow-run-tabs.d.ts.map +1 -0
- package/dist/core/workflow-run-tabs.js +3 -0
- package/dist/core/workflow-run-tabs.js.map +1 -0
- package/dist/core/workflow-run.d.ts +6 -6
- package/dist/core/workflow-run.d.ts.map +1 -1
- package/dist/core/workflow-run.js +3 -3
- package/dist/core/workflow-run.js.map +1 -1
- package/dist/feature.d.ts +7 -7
- package/dist/feature.js +5 -5
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.d.ts +4 -3
- package/dist/hooks/api/workflow-run-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workflow-run-mapper.js +31 -3
- package/dist/hooks/api/workflow-run-mapper.js.map +1 -1
- package/dist/hooks/api/workflow-runs.d.ts +4 -4
- package/dist/hooks/api/workflow-runs.d.ts.map +1 -1
- package/dist/hooks/api/workflow-runs.js +12 -4
- package/dist/hooks/api/workflow-runs.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/pages/project-workflows-page.js +46 -1
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/pages/workflow-run-detail-page.d.ts +11 -0
- package/dist/pages/workflow-run-detail-page.d.ts.map +1 -0
- package/dist/pages/workflow-run-detail-page.js +52 -0
- package/dist/pages/workflow-run-detail-page.js.map +1 -0
- package/dist/pages/workflow-run-list-page.d.ts +10 -0
- package/dist/pages/workflow-run-list-page.d.ts.map +1 -0
- package/dist/pages/workflow-run-list-page.js +43 -0
- package/dist/pages/workflow-run-list-page.js.map +1 -0
- package/dist/routes/inputs.d.ts +94 -5
- package/dist/routes/inputs.d.ts.map +1 -1
- package/dist/routes/inputs.js +199 -13
- package/dist/routes/inputs.js.map +1 -1
- package/dist/routes/project-route.d.ts +6 -0
- package/dist/routes/project-route.d.ts.map +1 -0
- package/dist/routes/project-route.js +25 -0
- package/dist/routes/project-route.js.map +1 -0
- package/dist/routes/run-detail.d.ts.map +1 -1
- package/dist/routes/run-detail.js +12 -7
- package/dist/routes/run-detail.js.map +1 -1
- package/dist/routes/runs.d.ts.map +1 -1
- package/dist/routes/runs.js +11 -6
- package/dist/routes/runs.js.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +6 -5
- package/dist/routes/workflows.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/components/job-graph/job-duration-format.ts +11 -2
- package/src/components/job-graph/job-duration-label.tsx +5 -1
- package/src/components/job-graph/job-graph-content.tsx +7 -4
- package/src/components/job-graph/job-graph-view.test.tsx +19 -8
- package/src/components/job-graph/job-graph-view.tsx +6 -3
- package/src/components/job-graph/job-graph.stories.tsx +24 -20
- package/src/components/job-graph/job-node.stories.tsx +11 -6
- package/src/components/job-graph/job-node.test.tsx +82 -12
- package/src/components/job-graph/job-node.tsx +22 -15
- package/src/components/job-graph/types.ts +5 -1
- package/src/components/step-list/step-list.stories.tsx +2 -3
- package/src/components/step-list/step-list.tsx +3 -2
- package/src/components/workflow-run-duration-label.tsx +1 -1
- package/src/components/workflow-run-list/job-status-strip.tsx +189 -0
- package/src/components/workflow-run-list/run-display.test.ts +169 -30
- package/src/components/workflow-run-list/run-display.ts +116 -9
- package/src/components/workflow-run-list/types.ts +21 -17
- package/src/components/workflow-run-list/workflow-run-filter-menu.tsx +100 -0
- package/src/components/workflow-run-list/workflow-run-filters.tsx +243 -0
- package/src/components/workflow-run-list/workflow-run-list-content.tsx +44 -16
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +128 -27
- package/src/components/workflow-run-list/workflow-run-list-view.test.tsx +441 -104
- package/src/components/workflow-run-list/workflow-run-list-view.tsx +73 -43
- package/src/components/workflow-run-list/workflow-run-list.stories.tsx +140 -20
- package/src/components/workflow-run-list/workflow-run-list.tsx +20 -10
- package/src/components/workflow-run-list/workflow-run-row.tsx +173 -106
- package/src/components/workflow-run-number-label.test.tsx +45 -0
- package/src/components/workflow-run-number-label.tsx +47 -0
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.test.tsx +7 -8
- package/src/components/workflow-run-summary/workflow-run-attempt-switcher.tsx +15 -14
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +29 -38
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +30 -24
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +102 -77
- package/src/components/workflow-run-tabs/index.ts +5 -0
- package/src/components/workflow-run-tabs/run-annotation-summary-line.test.tsx +69 -0
- package/src/components/workflow-run-tabs/run-annotation-summary-line.tsx +97 -0
- package/src/components/workflow-run-tabs/run-annotations-empty.tsx +11 -0
- package/src/components/workflow-run-tabs/run-jobs-list.tsx +151 -0
- package/src/components/workflow-run-tabs/run-tab-count.tsx +28 -0
- package/src/components/workflow-run-tabs/run-tab-strip.stories.tsx +52 -0
- package/src/components/workflow-run-tabs/run-tab-strip.test.tsx +117 -0
- package/src/components/workflow-run-tabs/run-tab-strip.tsx +97 -0
- package/src/components/workflow-run-view/agent-config-failure-callout.stories.tsx +2 -3
- package/src/components/workflow-run-view/agent-config-failure-callout.tsx +4 -4
- package/src/components/workflow-run-view/job-card.stories.tsx +11 -10
- package/src/components/workflow-run-view/job-card.test.tsx +115 -0
- package/src/components/workflow-run-view/job-card.tsx +74 -35
- package/src/components/workflow-run-view/job-execution-switcher.tsx +31 -9
- package/src/components/workflow-run-view/workflow-run-selection.test.ts +48 -0
- package/src/components/workflow-run-view/workflow-run-selection.ts +48 -1
- package/src/components/workflow-run-view/workflow-run-states.tsx +12 -1
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +152 -94
- package/src/components/workflow-run-view/workflow-run-view.tsx +219 -95
- package/src/components/workflow-source-panel/index.ts +1 -0
- package/src/components/workflow-source-panel/workflow-source-content.tsx +84 -0
- package/src/components/workflow-source-panel/workflow-source-panel.tsx +14 -81
- package/src/components/workflow-status/status-visuals.test.ts +9 -0
- package/src/components/workflow-status/status-visuals.ts +6 -4
- package/src/components/workflow-status/workflow-status-icon.stories.tsx +10 -2
- package/src/components/workflow-status/workflow-status-icon.test.tsx +18 -0
- package/src/components/workflow-status/workflow-status-icon.tsx +17 -24
- package/src/core/entities/job-execution.ts +22 -0
- package/src/core/entities/job-status.test.ts +104 -0
- package/src/core/entities/job.ts +18 -3
- package/src/core/entities/step.ts +4 -0
- package/src/core/entities/workflow-run.ts +76 -7
- package/src/core/workflow-run-tabs.ts +8 -0
- package/src/core/workflow-run.test.ts +24 -9
- package/src/core/workflow-run.ts +17 -2
- package/src/feature.ts +5 -5
- package/src/hooks/api/workflow-run-mapper.ts +30 -3
- package/src/hooks/api/workflow-runs.test.tsx +2 -0
- package/src/hooks/api/workflow-runs.ts +12 -7
- package/src/index.ts +7 -2
- package/src/page-harness-budget.test.ts +1 -1
- package/src/pages/project-workflows-page.test.tsx +59 -5
- package/src/pages/project-workflows-page.tsx +34 -1
- package/src/pages/workflow-run-detail-page.tsx +72 -0
- package/src/pages/workflow-run-list-page.tsx +65 -0
- package/src/pages/workflow-run-pages.test.tsx +823 -0
- package/src/routes/inputs.test.ts +240 -12
- package/src/routes/inputs.ts +215 -11
- package/src/routes/project-route.tsx +25 -0
- package/src/routes/run-detail.tsx +15 -8
- package/src/routes/runs.tsx +14 -7
- package/src/routes/workflows.tsx +5 -4
- package/src/storybook-config.test.ts +27 -0
- package/test/fixtures/workflow-run.ts +76 -7
- package/test/pages.tsx +18 -8
- package/test/render.tsx +8 -2
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.test.json +7 -1
- package/dist/components/workflow-run-list/workflow-run-list-header.d.ts +0 -10
- package/dist/components/workflow-run-list/workflow-run-list-header.d.ts.map +0 -1
- package/dist/components/workflow-run-list/workflow-run-list-header.js +0 -55
- package/dist/components/workflow-run-list/workflow-run-list-header.js.map +0 -1
- package/dist/pages/workflow-run-first-time-use.d.ts +0 -6
- package/dist/pages/workflow-run-first-time-use.d.ts.map +0 -1
- package/dist/pages/workflow-run-first-time-use.js +0 -17
- package/dist/pages/workflow-run-first-time-use.js.map +0 -1
- package/dist/pages/workflow-run-page.d.ts +0 -10
- package/dist/pages/workflow-run-page.d.ts.map +0 -1
- package/dist/pages/workflow-run-page.js +0 -90
- package/dist/pages/workflow-run-page.js.map +0 -1
- package/src/components/workflow-run-list/workflow-run-list-header.tsx +0 -53
- package/src/pages/workflow-run-first-time-use.tsx +0 -17
- package/src/pages/workflow-run-page.test.tsx +0 -519
- package/src/pages/workflow-run-page.tsx +0 -94
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {Button} from '@shipfox/react-ui/button';
|
|
2
|
+
import {
|
|
3
|
+
DropdownMenu,
|
|
4
|
+
DropdownMenuCheckboxItem,
|
|
5
|
+
DropdownMenuContent,
|
|
6
|
+
DropdownMenuTrigger,
|
|
7
|
+
} from '@shipfox/react-ui/dropdown-menu';
|
|
8
|
+
import {Text} from '@shipfox/react-ui/typography';
|
|
9
|
+
import {cn} from '@shipfox/react-ui/utils';
|
|
10
|
+
|
|
11
|
+
export interface WorkflowRunFilterOption {
|
|
12
|
+
value: string;
|
|
13
|
+
label: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface WorkflowRunFilterMenuProps {
|
|
17
|
+
label: string;
|
|
18
|
+
options: readonly WorkflowRunFilterOption[];
|
|
19
|
+
selected: readonly string[];
|
|
20
|
+
onChange: (next: string[]) => void;
|
|
21
|
+
/** Shown in place of the option list when nothing is loaded to filter by. */
|
|
22
|
+
emptyMessage: string;
|
|
23
|
+
className?: string | undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* One multi-select filter.
|
|
28
|
+
*
|
|
29
|
+
* The trigger states its own value rather than only a count, because a toolbar of five
|
|
30
|
+
* buttons that all read "Branch" forces the user to open each one to find out what is on.
|
|
31
|
+
* Checked items keep the menu open: choosing two branches should not cost two round trips.
|
|
32
|
+
*/
|
|
33
|
+
export function WorkflowRunFilterMenu({
|
|
34
|
+
label,
|
|
35
|
+
options,
|
|
36
|
+
selected,
|
|
37
|
+
onChange,
|
|
38
|
+
emptyMessage,
|
|
39
|
+
className,
|
|
40
|
+
}: WorkflowRunFilterMenuProps) {
|
|
41
|
+
const active = selected.length > 0;
|
|
42
|
+
const triggerText = triggerLabel(label, options, selected);
|
|
43
|
+
|
|
44
|
+
function toggle(value: string) {
|
|
45
|
+
onChange(
|
|
46
|
+
selected.includes(value) ? selected.filter((entry) => entry !== value) : [...selected, value],
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<DropdownMenu>
|
|
52
|
+
<DropdownMenuTrigger asChild>
|
|
53
|
+
<Button
|
|
54
|
+
type="button"
|
|
55
|
+
variant="secondary"
|
|
56
|
+
size="sm"
|
|
57
|
+
iconRight="arrowDownSLine"
|
|
58
|
+
// Mirrors the visible label so the selected value is not sighted-only; an aria-label
|
|
59
|
+
// replaces the button's contents as its accessible name rather than adding to them.
|
|
60
|
+
aria-label={`${triggerText} filter`}
|
|
61
|
+
className={cn('max-w-[200px]', !active && 'text-foreground-neutral-subtle', className)}
|
|
62
|
+
>
|
|
63
|
+
<span className="min-w-0 truncate">{triggerText}</span>
|
|
64
|
+
</Button>
|
|
65
|
+
</DropdownMenuTrigger>
|
|
66
|
+
<DropdownMenuContent align="start" className="max-h-[320px] w-[240px] overflow-y-auto">
|
|
67
|
+
{options.length === 0 ? (
|
|
68
|
+
<Text as="p" size="xs" className="px-8 py-6 text-foreground-neutral-muted">
|
|
69
|
+
{emptyMessage}
|
|
70
|
+
</Text>
|
|
71
|
+
) : (
|
|
72
|
+
options.map((option) => (
|
|
73
|
+
<DropdownMenuCheckboxItem
|
|
74
|
+
key={option.value}
|
|
75
|
+
checked={selected.includes(option.value)}
|
|
76
|
+
closeOnSelect={false}
|
|
77
|
+
onSelect={() => toggle(option.value)}
|
|
78
|
+
>
|
|
79
|
+
{option.label}
|
|
80
|
+
</DropdownMenuCheckboxItem>
|
|
81
|
+
))
|
|
82
|
+
)}
|
|
83
|
+
</DropdownMenuContent>
|
|
84
|
+
</DropdownMenu>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function triggerLabel(
|
|
89
|
+
label: string,
|
|
90
|
+
options: readonly WorkflowRunFilterOption[],
|
|
91
|
+
selected: readonly string[],
|
|
92
|
+
): string {
|
|
93
|
+
if (selected.length === 0) return label;
|
|
94
|
+
if (selected.length === 1) {
|
|
95
|
+
const [value] = selected;
|
|
96
|
+
const option = options.find((entry) => entry.value === value);
|
|
97
|
+
return `${label}: ${option?.label ?? value}`;
|
|
98
|
+
}
|
|
99
|
+
return `${label} · ${selected.length}`;
|
|
100
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import {Button} from '@shipfox/react-ui/button';
|
|
2
|
+
import {DateRangePicker} from '@shipfox/react-ui/date-range-picker';
|
|
3
|
+
import {Icon} from '@shipfox/react-ui/icon';
|
|
4
|
+
import {Input} from '@shipfox/react-ui/input';
|
|
5
|
+
import {Label} from '@shipfox/react-ui/label';
|
|
6
|
+
import {
|
|
7
|
+
Sheet,
|
|
8
|
+
SheetBody,
|
|
9
|
+
SheetContent,
|
|
10
|
+
SheetFooter,
|
|
11
|
+
SheetHeader,
|
|
12
|
+
SheetTitle,
|
|
13
|
+
SheetTrigger,
|
|
14
|
+
} from '@shipfox/react-ui/sheet';
|
|
15
|
+
import {useState} from 'react';
|
|
16
|
+
import {getWorkflowStatusVisual} from '#components/workflow-status/status-visuals.js';
|
|
17
|
+
import {
|
|
18
|
+
WORKFLOW_RUN_LIST_STATUSES,
|
|
19
|
+
type WorkflowRunFilterPatch,
|
|
20
|
+
type WorkflowRunListStatus,
|
|
21
|
+
type WorkflowRunsSearch,
|
|
22
|
+
} from '#routes/inputs.js';
|
|
23
|
+
import type {WorkflowRunFacets} from './run-display.js';
|
|
24
|
+
import {WorkflowRunFilterMenu, type WorkflowRunFilterOption} from './workflow-run-filter-menu.js';
|
|
25
|
+
|
|
26
|
+
const STATUS_OPTIONS: WorkflowRunFilterOption[] = WORKFLOW_RUN_LIST_STATUSES.map((status) => ({
|
|
27
|
+
value: status,
|
|
28
|
+
label: getWorkflowStatusVisual(status).label,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
export interface WorkflowRunFiltersProps {
|
|
32
|
+
search: WorkflowRunsSearch;
|
|
33
|
+
facets: WorkflowRunFacets;
|
|
34
|
+
onChange: (patch: WorkflowRunFilterPatch) => void;
|
|
35
|
+
onClear: () => void;
|
|
36
|
+
hasActiveFilters: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The run list filter row.
|
|
41
|
+
*
|
|
42
|
+
* Inline from `md` up, and behind a sheet below it, where a five-control toolbar would eat
|
|
43
|
+
* the viewport the list is supposed to fill. Both layouts render the same controls, so the
|
|
44
|
+
* narrow surface is the full filter set rather than a reduced one.
|
|
45
|
+
*/
|
|
46
|
+
export function WorkflowRunFilters({
|
|
47
|
+
search,
|
|
48
|
+
facets,
|
|
49
|
+
onChange,
|
|
50
|
+
onClear,
|
|
51
|
+
hasActiveFilters,
|
|
52
|
+
}: WorkflowRunFiltersProps) {
|
|
53
|
+
const [sheetOpen, setSheetOpen] = useState(false);
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div className="flex flex-wrap items-center gap-8">
|
|
57
|
+
<Input
|
|
58
|
+
value={search.search ?? ''}
|
|
59
|
+
onChange={(event) => onChange({search: event.target.value})}
|
|
60
|
+
placeholder="Search runs by name, number, branch, or commit"
|
|
61
|
+
aria-label="Search runs"
|
|
62
|
+
size="small"
|
|
63
|
+
className="min-w-[240px] flex-1"
|
|
64
|
+
iconLeft={<Icon name="searchLine" className="size-14 text-foreground-neutral-muted" />}
|
|
65
|
+
/>
|
|
66
|
+
|
|
67
|
+
<div className="hidden flex-wrap items-center gap-8 md:flex">
|
|
68
|
+
<WorkflowRunFilterControls search={search} facets={facets} onChange={onChange} />
|
|
69
|
+
{hasActiveFilters ? <ClearFiltersButton onClear={onClear} /> : null}
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<Sheet open={sheetOpen} onOpenChange={setSheetOpen}>
|
|
73
|
+
<SheetTrigger asChild>
|
|
74
|
+
<Button
|
|
75
|
+
type="button"
|
|
76
|
+
variant="secondary"
|
|
77
|
+
size="sm"
|
|
78
|
+
iconLeft="filterLine"
|
|
79
|
+
className="md:hidden"
|
|
80
|
+
>
|
|
81
|
+
Filters
|
|
82
|
+
</Button>
|
|
83
|
+
</SheetTrigger>
|
|
84
|
+
<SheetContent side="bottom" aria-describedby={undefined}>
|
|
85
|
+
<SheetHeader>
|
|
86
|
+
<SheetTitle>Filter runs</SheetTitle>
|
|
87
|
+
</SheetHeader>
|
|
88
|
+
<SheetBody className="flex flex-col gap-12">
|
|
89
|
+
<WorkflowRunFilterControls
|
|
90
|
+
search={search}
|
|
91
|
+
facets={facets}
|
|
92
|
+
onChange={onChange}
|
|
93
|
+
stacked
|
|
94
|
+
/>
|
|
95
|
+
</SheetBody>
|
|
96
|
+
<SheetFooter>
|
|
97
|
+
<Button
|
|
98
|
+
type="button"
|
|
99
|
+
variant="secondary"
|
|
100
|
+
size="sm"
|
|
101
|
+
disabled={!hasActiveFilters}
|
|
102
|
+
onClick={onClear}
|
|
103
|
+
>
|
|
104
|
+
Clear filters
|
|
105
|
+
</Button>
|
|
106
|
+
<Button type="button" variant="primary" size="sm" onClick={() => setSheetOpen(false)}>
|
|
107
|
+
Show runs
|
|
108
|
+
</Button>
|
|
109
|
+
</SheetFooter>
|
|
110
|
+
</SheetContent>
|
|
111
|
+
</Sheet>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function WorkflowRunFilterControls({
|
|
117
|
+
search,
|
|
118
|
+
facets,
|
|
119
|
+
onChange,
|
|
120
|
+
stacked = false,
|
|
121
|
+
}: {
|
|
122
|
+
search: WorkflowRunsSearch;
|
|
123
|
+
facets: WorkflowRunFacets;
|
|
124
|
+
onChange: (patch: WorkflowRunFilterPatch) => void;
|
|
125
|
+
stacked?: boolean;
|
|
126
|
+
}) {
|
|
127
|
+
const controlClassName = stacked ? 'w-full max-w-none' : undefined;
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<>
|
|
131
|
+
<WorkflowRunFilterMenu
|
|
132
|
+
label="Status"
|
|
133
|
+
options={STATUS_OPTIONS}
|
|
134
|
+
selected={search.status ?? []}
|
|
135
|
+
onChange={(next) => onChange({status: next as WorkflowRunListStatus[]})}
|
|
136
|
+
emptyMessage="No statuses available."
|
|
137
|
+
className={controlClassName}
|
|
138
|
+
/>
|
|
139
|
+
<WorkflowRunFilterMenu
|
|
140
|
+
label="Branch"
|
|
141
|
+
options={toOptions(facets.branch)}
|
|
142
|
+
selected={search.branch ?? []}
|
|
143
|
+
onChange={(branch) => onChange({branch})}
|
|
144
|
+
emptyMessage="No branches in the loaded runs."
|
|
145
|
+
className={controlClassName}
|
|
146
|
+
/>
|
|
147
|
+
<WorkflowRunFilterMenu
|
|
148
|
+
label="Actor"
|
|
149
|
+
options={toOptions(facets.actor)}
|
|
150
|
+
selected={search.actor ?? []}
|
|
151
|
+
onChange={(actor) => onChange({actor})}
|
|
152
|
+
emptyMessage="No actors in the loaded runs."
|
|
153
|
+
className={controlClassName}
|
|
154
|
+
/>
|
|
155
|
+
<WorkflowRunFilterMenu
|
|
156
|
+
label="Event"
|
|
157
|
+
options={toOptions(facets.event)}
|
|
158
|
+
selected={search.event ?? []}
|
|
159
|
+
onChange={(event) => onChange({event})}
|
|
160
|
+
emptyMessage="No events in the loaded runs."
|
|
161
|
+
className={controlClassName}
|
|
162
|
+
/>
|
|
163
|
+
{stacked ? (
|
|
164
|
+
<div className="flex flex-col gap-6">
|
|
165
|
+
<Label>Created between</Label>
|
|
166
|
+
<RunDateRangeFilter search={search} onChange={onChange} />
|
|
167
|
+
</div>
|
|
168
|
+
) : (
|
|
169
|
+
<RunDateRangeFilter search={search} onChange={onChange} />
|
|
170
|
+
)}
|
|
171
|
+
</>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function RunDateRangeFilter({
|
|
176
|
+
search,
|
|
177
|
+
onChange,
|
|
178
|
+
}: {
|
|
179
|
+
search: WorkflowRunsSearch;
|
|
180
|
+
onChange: (patch: WorkflowRunFilterPatch) => void;
|
|
181
|
+
}) {
|
|
182
|
+
return (
|
|
183
|
+
<DateRangePicker
|
|
184
|
+
size="small"
|
|
185
|
+
aria-label="Filter runs by creation date"
|
|
186
|
+
// The shared picker renders a value only when both bounds are set, so a one-sided filter
|
|
187
|
+
// would otherwise read as unset. Naming the active bound keeps it visible without
|
|
188
|
+
// inventing the bound the user did not choose.
|
|
189
|
+
placeholder={partialDateLabel(search) ?? 'Any date'}
|
|
190
|
+
dateRange={{
|
|
191
|
+
...(search.after ? {start: fromCalendarDate(search.after)} : {}),
|
|
192
|
+
...(search.before ? {end: fromCalendarDate(search.before)} : {}),
|
|
193
|
+
}}
|
|
194
|
+
onDateRangeSelect={(range) =>
|
|
195
|
+
onChange({
|
|
196
|
+
after: range?.start ? toCalendarDate(range.start) : undefined,
|
|
197
|
+
before: range?.end ? toCalendarDate(range.end) : undefined,
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
onClear={() => onChange({after: undefined, before: undefined})}
|
|
201
|
+
/>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function ClearFiltersButton({onClear}: {onClear: () => void}) {
|
|
206
|
+
return (
|
|
207
|
+
<Button
|
|
208
|
+
type="button"
|
|
209
|
+
variant="transparentMuted"
|
|
210
|
+
size="sm"
|
|
211
|
+
iconLeft="closeLine"
|
|
212
|
+
onClick={onClear}
|
|
213
|
+
>
|
|
214
|
+
Clear filters
|
|
215
|
+
</Button>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Describes a bound the picker cannot display on its own, or nothing when it can. */
|
|
220
|
+
function partialDateLabel(search: WorkflowRunsSearch): string | undefined {
|
|
221
|
+
if (search.after && search.before) return undefined;
|
|
222
|
+
if (search.after) return `From ${search.after}`;
|
|
223
|
+
if (search.before) return `Until ${search.before}`;
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function toOptions(values: readonly string[]): WorkflowRunFilterOption[] {
|
|
228
|
+
return values.map((value) => ({value, label: value}));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// The bounds are local calendar dates, matching how each row's date reads, so the picker and
|
|
232
|
+
// the URL agree with what the list shows rather than with UTC.
|
|
233
|
+
function toCalendarDate(date: Date): string {
|
|
234
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
235
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
236
|
+
return `${date.getFullYear()}-${month}-${day}`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function fromCalendarDate(value: string): Date | undefined {
|
|
240
|
+
const [year, month, day] = value.split('-').map(Number) as [number, number, number];
|
|
241
|
+
const date = new Date(year, month - 1, day);
|
|
242
|
+
return Number.isNaN(date.getTime()) ? undefined : date;
|
|
243
|
+
}
|
|
@@ -3,6 +3,7 @@ import type {WorkflowRunListItem} from '#core/workflow-run.js';
|
|
|
3
3
|
import type {WorkflowRunListQuery} from './types.js';
|
|
4
4
|
import {
|
|
5
5
|
WorkflowRunListEmpty,
|
|
6
|
+
WorkflowRunListLoadMore,
|
|
6
7
|
WorkflowRunListNoMatches,
|
|
7
8
|
WorkflowRunListSkeleton,
|
|
8
9
|
WorkflowRunListStaleError,
|
|
@@ -13,26 +14,42 @@ interface WorkflowRunListContentProps {
|
|
|
13
14
|
query: WorkflowRunListQuery;
|
|
14
15
|
totalRuns: number;
|
|
15
16
|
runs: WorkflowRunListItem[];
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
workspaceSlug?: string | undefined;
|
|
18
|
+
projectSlug?: string | undefined;
|
|
19
|
+
hasActiveFilters: boolean;
|
|
19
20
|
onClearFilters: () => void;
|
|
21
|
+
hasNextPage: boolean;
|
|
22
|
+
isFetchingNextPage: boolean;
|
|
23
|
+
isFetchNextPageError: boolean;
|
|
24
|
+
onLoadMore?: () => void;
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
export function WorkflowRunListContent({
|
|
23
28
|
query,
|
|
24
29
|
totalRuns,
|
|
25
30
|
runs,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
workspaceSlug,
|
|
32
|
+
projectSlug,
|
|
33
|
+
hasActiveFilters,
|
|
29
34
|
onClearFilters,
|
|
35
|
+
hasNextPage,
|
|
36
|
+
isFetchingNextPage,
|
|
37
|
+
isFetchNextPageError,
|
|
38
|
+
onLoadMore,
|
|
30
39
|
}: WorkflowRunListContentProps) {
|
|
31
40
|
const {isPending, isError} = query;
|
|
32
41
|
// A refetch that fails after a prior success keeps the rows on screen behind a slim
|
|
33
42
|
// banner. QueryLoadError owns the inverse case (errored before anything loaded) and
|
|
34
|
-
// self-gates to nothing here once data exists.
|
|
43
|
+
// self-gates to nothing here once data exists. A failed fetch never renders as an empty
|
|
44
|
+
// list: "nothing here" and "we could not find out" are different answers.
|
|
35
45
|
const refreshFailed = isError && query.data !== undefined;
|
|
46
|
+
// Keyed off "we have an answer" rather than "no error", so a stale refresh still reports
|
|
47
|
+
// whether the filters match anything instead of leaving the list blank under the banner.
|
|
48
|
+
const hasLoaded = !isPending && query.data !== undefined;
|
|
49
|
+
// An empty result is only an empty *list* when no filter is on. With filters active it is a
|
|
50
|
+
// no-matches state even at zero loaded runs, so the way out is clearing them.
|
|
51
|
+
const showEmptyState = hasLoaded && totalRuns === 0 && !hasActiveFilters;
|
|
52
|
+
const showNoMatches = hasLoaded && runs.length === 0 && !showEmptyState;
|
|
36
53
|
|
|
37
54
|
return (
|
|
38
55
|
<div className="min-h-0 flex-1 overflow-y-auto">
|
|
@@ -41,18 +58,29 @@ export function WorkflowRunListContent({
|
|
|
41
58
|
<QueryLoadError query={query} subject="workflow runs" icon="pulseLine" />
|
|
42
59
|
) : null}
|
|
43
60
|
{!isPending && refreshFailed ? <WorkflowRunListStaleError query={query} /> : null}
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
<WorkflowRunListNoMatches onClear={onClearFilters} />
|
|
61
|
+
{showEmptyState ? (
|
|
62
|
+
<WorkflowRunListEmpty workspaceSlug={workspaceSlug} projectSlug={projectSlug} />
|
|
47
63
|
) : null}
|
|
48
|
-
{
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
{showNoMatches ? (
|
|
65
|
+
<WorkflowRunListNoMatches
|
|
66
|
+
onClear={onClearFilters}
|
|
67
|
+
hasNextPage={hasNextPage}
|
|
68
|
+
isFetchingNextPage={isFetchingNextPage}
|
|
69
|
+
isFetchNextPageError={isFetchNextPageError}
|
|
70
|
+
{...(onLoadMore ? {onLoadMore} : {})}
|
|
54
71
|
/>
|
|
55
72
|
) : null}
|
|
73
|
+
{!isPending && runs.length > 0 ? (
|
|
74
|
+
<>
|
|
75
|
+
<WorkflowRunRowList runs={runs} workspaceSlug={workspaceSlug} projectSlug={projectSlug} />
|
|
76
|
+
<WorkflowRunListLoadMore
|
|
77
|
+
hasNextPage={hasNextPage}
|
|
78
|
+
isFetchingNextPage={isFetchingNextPage}
|
|
79
|
+
isFetchNextPageError={isFetchNextPageError}
|
|
80
|
+
{...(onLoadMore ? {onLoadMore} : {})}
|
|
81
|
+
/>
|
|
82
|
+
</>
|
|
83
|
+
) : null}
|
|
56
84
|
</div>
|
|
57
85
|
);
|
|
58
86
|
}
|
|
@@ -4,24 +4,33 @@ import {Callout} from '@shipfox/react-ui/callout';
|
|
|
4
4
|
import {EmptyState} from '@shipfox/react-ui/empty-state';
|
|
5
5
|
import {Skeleton} from '@shipfox/react-ui/skeleton';
|
|
6
6
|
import {Text} from '@shipfox/react-ui/typography';
|
|
7
|
+
import {Link} from '@tanstack/react-router';
|
|
7
8
|
|
|
9
|
+
const SKELETON_ROW_COUNT = 8;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Placeholder rows at the real row height, so the list does not resize under the cursor when
|
|
13
|
+
* the first page lands. Only the initial load renders these: a refetch keeps the rows it has,
|
|
14
|
+
* per the design system's rule against animating frequently-updating data.
|
|
15
|
+
*/
|
|
8
16
|
export function WorkflowRunListSkeleton() {
|
|
9
17
|
return (
|
|
10
|
-
<div
|
|
11
|
-
|
|
18
|
+
<div
|
|
19
|
+
className="@container divide-y divide-border-neutral-base border-t border-border-neutral-base"
|
|
20
|
+
role="status"
|
|
21
|
+
aria-label="Loading runs"
|
|
22
|
+
>
|
|
23
|
+
{Array.from({length: SKELETON_ROW_COUNT}).map((_, index) => (
|
|
12
24
|
<div
|
|
13
25
|
// biome-ignore lint/suspicious/noArrayIndexKey: skeleton row, stable position
|
|
14
26
|
key={index}
|
|
15
|
-
className="flex
|
|
27
|
+
className="flex items-center gap-10 px-12 py-8 @min-[976px]:h-44 @min-[976px]:py-0"
|
|
16
28
|
>
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
<Skeleton className="h-12 flex-1" />
|
|
23
|
-
<Skeleton className="h-12 w-40" />
|
|
24
|
-
</div>
|
|
29
|
+
<Skeleton className="size-14 shrink-0 rounded-full" />
|
|
30
|
+
<Skeleton className="h-12 w-[220px] max-w-[40%]" />
|
|
31
|
+
<Skeleton className="ml-auto hidden h-12 w-96 @min-[1040px]:block" />
|
|
32
|
+
<Skeleton className="h-12 w-48" />
|
|
33
|
+
<Skeleton className="h-12 w-48" />
|
|
25
34
|
</div>
|
|
26
35
|
))}
|
|
27
36
|
</div>
|
|
@@ -35,7 +44,7 @@ export function WorkflowRunListSkeleton() {
|
|
|
35
44
|
*/
|
|
36
45
|
export function WorkflowRunListStaleError({query}: {query: QueryLoadErrorQuery}) {
|
|
37
46
|
return (
|
|
38
|
-
<div className="
|
|
47
|
+
<div className="pb-8">
|
|
39
48
|
<Callout role="alert" type="error">
|
|
40
49
|
<div className="flex items-center justify-between gap-8">
|
|
41
50
|
<Text size="xs">Could not refresh workflow runs.</Text>
|
|
@@ -56,31 +65,123 @@ export function WorkflowRunListStaleError({query}: {query: QueryLoadErrorQuery})
|
|
|
56
65
|
);
|
|
57
66
|
}
|
|
58
67
|
|
|
59
|
-
export function WorkflowRunListEmpty(
|
|
68
|
+
export function WorkflowRunListEmpty({
|
|
69
|
+
workspaceSlug,
|
|
70
|
+
projectSlug,
|
|
71
|
+
}: {
|
|
72
|
+
workspaceSlug?: string | undefined;
|
|
73
|
+
projectSlug?: string | undefined;
|
|
74
|
+
}) {
|
|
60
75
|
return (
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
<EmptyState
|
|
77
|
+
icon="pulseLine"
|
|
78
|
+
title="No runs yet"
|
|
79
|
+
description="Runs from this project's workflows appear here the moment one is launched."
|
|
80
|
+
action={
|
|
81
|
+
workspaceSlug && projectSlug ? (
|
|
82
|
+
<Button asChild type="button" size="sm" variant="primary">
|
|
83
|
+
<Link
|
|
84
|
+
to="/w/$workspaceSlug/p/$projectSlug/workflows"
|
|
85
|
+
params={{workspaceSlug, projectSlug}}
|
|
86
|
+
>
|
|
87
|
+
View workflows
|
|
88
|
+
</Link>
|
|
89
|
+
</Button>
|
|
90
|
+
) : null
|
|
91
|
+
}
|
|
92
|
+
/>
|
|
68
93
|
);
|
|
69
94
|
}
|
|
70
95
|
|
|
71
|
-
|
|
96
|
+
/**
|
|
97
|
+
* The filtered-empty state, deliberately distinct from the unfiltered one: someone who
|
|
98
|
+
* filtered their way to nothing needs a way back out, not an onboarding call to action.
|
|
99
|
+
*
|
|
100
|
+
* Its reset is worded as its outcome rather than repeating the toolbar's "Clear filters"
|
|
101
|
+
* verbatim, so the two controls on screen read as one action offered twice rather than two
|
|
102
|
+
* different ones. It is also the only reset within reach below `md`, where the toolbar's
|
|
103
|
+
* lives inside the filter sheet.
|
|
104
|
+
*/
|
|
105
|
+
export function WorkflowRunListNoMatches({
|
|
106
|
+
onClear,
|
|
107
|
+
hasNextPage,
|
|
108
|
+
isFetchingNextPage,
|
|
109
|
+
isFetchNextPageError,
|
|
110
|
+
onLoadMore,
|
|
111
|
+
}: {
|
|
112
|
+
onClear: () => void;
|
|
113
|
+
hasNextPage: boolean;
|
|
114
|
+
isFetchingNextPage: boolean;
|
|
115
|
+
isFetchNextPageError: boolean;
|
|
116
|
+
onLoadMore?: () => void;
|
|
117
|
+
}) {
|
|
72
118
|
return (
|
|
73
|
-
<div className="
|
|
119
|
+
<div className="flex flex-col gap-8">
|
|
120
|
+
{isFetchNextPageError ? (
|
|
121
|
+
<Callout role="alert" type="error">
|
|
122
|
+
Could not load more workflow runs. Try again to continue searching older runs.
|
|
123
|
+
</Callout>
|
|
124
|
+
) : null}
|
|
74
125
|
<EmptyState
|
|
75
126
|
icon="filterOffLine"
|
|
76
|
-
title=
|
|
77
|
-
description=
|
|
127
|
+
title={hasNextPage ? 'No matches in loaded history' : 'No matching runs'}
|
|
128
|
+
description={
|
|
129
|
+
hasNextPage
|
|
130
|
+
? 'No loaded run matches these filters. Load more to search further back.'
|
|
131
|
+
: 'No run matches these filters.'
|
|
132
|
+
}
|
|
78
133
|
action={
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
134
|
+
<div className="flex flex-wrap justify-center gap-8">
|
|
135
|
+
{hasNextPage && onLoadMore ? (
|
|
136
|
+
<Button
|
|
137
|
+
type="button"
|
|
138
|
+
size="sm"
|
|
139
|
+
variant="primary"
|
|
140
|
+
isLoading={isFetchingNextPage}
|
|
141
|
+
onClick={onLoadMore}
|
|
142
|
+
>
|
|
143
|
+
Load more runs
|
|
144
|
+
</Button>
|
|
145
|
+
) : null}
|
|
146
|
+
<Button type="button" size="sm" variant="secondary" onClick={onClear}>
|
|
147
|
+
Show all runs
|
|
148
|
+
</Button>
|
|
149
|
+
</div>
|
|
82
150
|
}
|
|
83
151
|
/>
|
|
84
152
|
</div>
|
|
85
153
|
);
|
|
86
154
|
}
|
|
155
|
+
|
|
156
|
+
export function WorkflowRunListLoadMore({
|
|
157
|
+
hasNextPage,
|
|
158
|
+
isFetchingNextPage,
|
|
159
|
+
isFetchNextPageError,
|
|
160
|
+
onLoadMore,
|
|
161
|
+
}: {
|
|
162
|
+
hasNextPage: boolean;
|
|
163
|
+
isFetchingNextPage: boolean;
|
|
164
|
+
isFetchNextPageError: boolean;
|
|
165
|
+
onLoadMore?: () => void;
|
|
166
|
+
}) {
|
|
167
|
+
if (!hasNextPage || !onLoadMore) return null;
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<div className="flex flex-col items-center gap-8 border-t border-border-neutral-base py-12">
|
|
171
|
+
{isFetchNextPageError ? (
|
|
172
|
+
<Callout role="alert" type="error">
|
|
173
|
+
Could not load more workflow runs. Try again.
|
|
174
|
+
</Callout>
|
|
175
|
+
) : null}
|
|
176
|
+
<Button
|
|
177
|
+
type="button"
|
|
178
|
+
size="sm"
|
|
179
|
+
variant="secondary"
|
|
180
|
+
isLoading={isFetchingNextPage}
|
|
181
|
+
onClick={onLoadMore}
|
|
182
|
+
>
|
|
183
|
+
Load more runs
|
|
184
|
+
</Button>
|
|
185
|
+
</div>
|
|
186
|
+
);
|
|
187
|
+
}
|