@shipfox/client-workflows 21.0.0 → 22.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +33 -0
- package/dist/components/job-detail/job-detail-view.js +1 -2
- package/dist/components/job-detail/job-detail-view.js.map +1 -1
- package/dist/components/job-detail/job-empty-states.js +2 -2
- package/dist/components/job-detail/job-empty-states.js.map +1 -1
- package/dist/components/job-detail/job-execution-time-text.d.ts.map +1 -1
- package/dist/components/job-detail/job-execution-time-text.js +2 -15
- package/dist/components/job-detail/job-execution-time-text.js.map +1 -1
- package/dist/components/job-graph/job-duration-format.js +1 -1
- package/dist/components/job-graph/job-duration-format.js.map +1 -1
- package/dist/components/job-graph/job-graph-content.js +2 -2
- package/dist/components/job-graph/job-graph-content.js.map +1 -1
- package/dist/components/step-list/step-list.js +1 -2
- package/dist/components/step-list/step-list.js.map +1 -1
- 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 +13 -7
- 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.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-list-states.js +42 -37
- package/dist/components/workflow-run-list/workflow-run-list-states.js.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.d.ts.map +1 -1
- package/dist/components/workflow-run-list/workflow-run-row.js +14 -10
- package/dist/components/workflow-run-list/workflow-run-row.js.map +1 -1
- package/dist/components/workflow-run-summary/workflow-run-summary.js +3 -3
- package/dist/components/workflow-run-summary/workflow-run-summary.js.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.d.ts.map +1 -1
- package/dist/components/workflow-run-view/run-workspace-nav.js +20 -7
- package/dist/components/workflow-run-view/run-workspace-nav.js.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.d.ts.map +1 -1
- package/dist/components/workflow-run-view/workflow-run-view.js +68 -50
- package/dist/components/workflow-run-view/workflow-run-view.js.map +1 -1
- package/dist/pages/project-workflows-page.js +181 -172
- package/dist/pages/project-workflows-page.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/components/job-detail/job-detail-view.tsx +1 -2
- package/src/components/job-detail/job-empty-states.tsx +2 -2
- package/src/components/job-detail/job-execution-time-text.test.ts +46 -0
- package/src/components/job-detail/job-execution-time-text.tsx +2 -23
- package/src/components/job-graph/job-duration-format.ts +1 -1
- package/src/components/job-graph/job-graph-content.tsx +2 -2
- package/src/components/step-list/step-list.tsx +1 -2
- package/src/components/workflow-run-list/workflow-run-list-content.tsx +7 -2
- package/src/components/workflow-run-list/workflow-run-list-states.tsx +41 -40
- package/src/components/workflow-run-list/workflow-run-row.tsx +11 -8
- package/src/components/workflow-run-summary/workflow-run-summary.stories.tsx +1 -1
- package/src/components/workflow-run-summary/workflow-run-summary.test.tsx +9 -1
- package/src/components/workflow-run-summary/workflow-run-summary.tsx +3 -3
- package/src/components/workflow-run-view/run-workspace-nav.test.tsx +40 -2
- package/src/components/workflow-run-view/run-workspace-nav.tsx +28 -9
- package/src/components/workflow-run-view/workflow-run-view.stories.tsx +3 -1
- package/src/components/workflow-run-view/workflow-run-view.test.tsx +11 -8
- package/src/components/workflow-run-view/workflow-run-view.tsx +78 -70
- package/src/pages/project-workflows-page.test.tsx +14 -4
- package/src/pages/project-workflows-page.tsx +138 -132
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {ApiError} from '@shipfox/client-api';
|
|
2
2
|
import {QueryLoadError} from '@shipfox/client-ui';
|
|
3
3
|
import {EmptyState} from '@shipfox/react-ui/empty-state';
|
|
4
|
+
import {Panel, PanelBody, PanelHeader} from '@shipfox/react-ui/panel';
|
|
4
5
|
import {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';
|
|
5
6
|
import {
|
|
6
7
|
Select,
|
|
@@ -11,7 +12,6 @@ import {
|
|
|
11
12
|
} from '@shipfox/react-ui/select';
|
|
12
13
|
import {toast} from '@shipfox/react-ui/toast';
|
|
13
14
|
import {Text} from '@shipfox/react-ui/typography';
|
|
14
|
-
import {cn} from '@shipfox/react-ui/utils';
|
|
15
15
|
import {useNavigate} from '@tanstack/react-router';
|
|
16
16
|
import {type ReactNode, useEffect, useMemo} from 'react';
|
|
17
17
|
import {buildRunAnnotationList, type RunAnnotationSummary} from '#core/run-annotation.js';
|
|
@@ -54,7 +54,6 @@ import {
|
|
|
54
54
|
} from './workflow-run-states.js';
|
|
55
55
|
|
|
56
56
|
type RunWorkspaceSection = Exclude<WorkflowRunTab, 'jobs'>;
|
|
57
|
-
const RUN_WORKSPACE_FRAME_CLASS_NAME = 'mx-auto w-full max-w-[calc(240px_+_1120px)]';
|
|
58
57
|
|
|
59
58
|
export interface WorkflowRunViewProps {
|
|
60
59
|
projectId: string;
|
|
@@ -159,7 +158,6 @@ function RunViewContent({
|
|
|
159
158
|
const activeSection = runWorkspaceSection(tab);
|
|
160
159
|
const cancelMutation = useCancelWorkflowRunMutation(runData);
|
|
161
160
|
const sourceSnapshot = runData?.sourceSnapshot ?? null;
|
|
162
|
-
const hasJobContent = Boolean(jobContent);
|
|
163
161
|
const resolvedSelection =
|
|
164
162
|
runData && selection ? resolveWorkflowRunSelection({run: runData, selection}) : undefined;
|
|
165
163
|
const highlightedLineRange = resolvedSelection?.step?.sourceLocation ?? null;
|
|
@@ -316,10 +314,7 @@ function RunViewContent({
|
|
|
316
314
|
>
|
|
317
315
|
<div
|
|
318
316
|
data-run-workspace-frame
|
|
319
|
-
className=
|
|
320
|
-
'flex min-h-0 min-w-0 w-full flex-1 flex-col min-[768px]:flex-row',
|
|
321
|
-
!hasJobContent && RUN_WORKSPACE_FRAME_CLASS_NAME,
|
|
322
|
-
)}
|
|
317
|
+
className="flex min-h-0 min-w-0 w-full flex-1 flex-col min-[768px]:flex-row"
|
|
323
318
|
>
|
|
324
319
|
{runData && workspaceSlug && projectSlug ? (
|
|
325
320
|
<RunWorkspaceNav
|
|
@@ -403,16 +398,20 @@ function RunSectionContent({
|
|
|
403
398
|
aria-label="All jobs summary"
|
|
404
399
|
className="min-h-0 flex-1 overflow-auto pb-panel pt-[16px]"
|
|
405
400
|
>
|
|
406
|
-
<div className="
|
|
401
|
+
<div className="flex w-full flex-col gap-group">
|
|
407
402
|
<Text as="h2" className="sr-only">
|
|
408
403
|
All jobs summary
|
|
409
404
|
</Text>
|
|
410
|
-
<
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
405
|
+
<Panel className="min-h-160">
|
|
406
|
+
<PanelBody className="min-h-160 bg-background-components-base p-0">
|
|
407
|
+
<JobGraph
|
|
408
|
+
run={run}
|
|
409
|
+
selectedJobId={selectedJobId}
|
|
410
|
+
onSelectedJobChange={onSelectGraphJob}
|
|
411
|
+
className="min-h-160 overflow-hidden"
|
|
412
|
+
/>
|
|
413
|
+
</PanelBody>
|
|
414
|
+
</Panel>
|
|
416
415
|
</div>
|
|
417
416
|
</section>
|
|
418
417
|
);
|
|
@@ -439,28 +438,33 @@ function RunSectionContent({
|
|
|
439
438
|
aria-label="Workflow source"
|
|
440
439
|
className="min-h-0 flex-1 overflow-auto pb-panel pt-[16px]"
|
|
441
440
|
>
|
|
442
|
-
<div className="
|
|
441
|
+
<div className="flex min-h-full w-full flex-col">
|
|
443
442
|
<Text as="h2" className="sr-only">
|
|
444
443
|
Workflow source
|
|
445
444
|
</Text>
|
|
446
|
-
|
|
447
|
-
<
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
445
|
+
<Panel className="min-h-160 flex-1">
|
|
446
|
+
<PanelBody className="min-h-160 flex-1 p-0">
|
|
447
|
+
{sourceSnapshot ? (
|
|
448
|
+
<WorkflowSourceContent
|
|
449
|
+
source={sourceSnapshot}
|
|
450
|
+
highlightedLineRange={highlightedLineRange}
|
|
451
|
+
scrollHighlightedIntoView
|
|
452
|
+
/>
|
|
453
|
+
) : (
|
|
454
|
+
<EmptyState
|
|
455
|
+
variant="panel"
|
|
456
|
+
className="min-h-160"
|
|
457
|
+
icon="fileDamageLine"
|
|
458
|
+
title="Source snapshot unavailable"
|
|
459
|
+
description={
|
|
460
|
+
run.isTemporary
|
|
461
|
+
? 'Temporary runs do not capture workflow source.'
|
|
462
|
+
: 'This run was created before workflow source snapshots were captured.'
|
|
463
|
+
}
|
|
464
|
+
/>
|
|
465
|
+
)}
|
|
466
|
+
</PanelBody>
|
|
467
|
+
</Panel>
|
|
464
468
|
</div>
|
|
465
469
|
</section>
|
|
466
470
|
);
|
|
@@ -534,45 +538,49 @@ function RunAnnotationsSection({
|
|
|
534
538
|
aria-label="Run annotations"
|
|
535
539
|
className="min-h-0 flex-1 overflow-auto pb-panel pt-[16px]"
|
|
536
540
|
>
|
|
537
|
-
<div className="
|
|
541
|
+
<div className="flex w-full flex-col">
|
|
538
542
|
<Text as="h2" className="sr-only">
|
|
539
543
|
Annotations
|
|
540
544
|
</Text>
|
|
541
|
-
<
|
|
542
|
-
<
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
(
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
545
|
+
<Panel>
|
|
546
|
+
<PanelHeader className="flex-wrap">
|
|
547
|
+
<RunAnnotationSummaryLine
|
|
548
|
+
summary={annotationSummary}
|
|
549
|
+
workspaceSlug={workspaceSlug}
|
|
550
|
+
projectSlug={projectSlug}
|
|
551
|
+
workflowRunId={run.id}
|
|
552
|
+
search={selection}
|
|
553
|
+
/>
|
|
554
|
+
<AnnotationJobFilter
|
|
555
|
+
jobs={run.jobs}
|
|
556
|
+
selectedJobId={selectedJob?.id}
|
|
557
|
+
onSelect={onSelectAnnotationJob}
|
|
558
|
+
/>
|
|
559
|
+
</PanelHeader>
|
|
560
|
+
<PanelBody className="gap-group p-panel">
|
|
561
|
+
<RunAnnotationList
|
|
562
|
+
// Remounting on a filter or run-attempt change resets the render window, so the next
|
|
563
|
+
// list never inherits a "show more" position from different data.
|
|
564
|
+
key={`${run.id}:${run.runAttempt.attempt}:${severity ?? 'all'}:${selectedJob?.id ?? 'all'}`}
|
|
565
|
+
query={annotations.query}
|
|
566
|
+
entries={entries}
|
|
567
|
+
derivedAnnotations={derivedAnnotations}
|
|
568
|
+
workspaceSlug={workspaceSlug}
|
|
569
|
+
projectSlug={projectSlug}
|
|
570
|
+
workflowRunId={run.id}
|
|
571
|
+
runAttempt={run.runAttempt.attempt}
|
|
572
|
+
// A run with no annotations at all offers no filter to clear, whatever the URL says.
|
|
573
|
+
filtered={Boolean(
|
|
574
|
+
(severity || selectedJob || selection?.annotation) &&
|
|
575
|
+
((annotationSummary?.total ?? 0) > 0 || hasSynthesizedJobAnnotations),
|
|
576
|
+
)}
|
|
577
|
+
filteredJobName={selectedJob?.displayName}
|
|
578
|
+
filteredSeverity={severity}
|
|
579
|
+
onClearFilters={onClearAnnotationFilters}
|
|
580
|
+
selectedAnnotationId={selection?.annotation}
|
|
581
|
+
/>
|
|
582
|
+
</PanelBody>
|
|
583
|
+
</Panel>
|
|
576
584
|
</div>
|
|
577
585
|
</section>
|
|
578
586
|
);
|
|
@@ -674,7 +682,7 @@ function RunWorkspaceNavSkeleton() {
|
|
|
674
682
|
return (
|
|
675
683
|
<aside
|
|
676
684
|
aria-label="Loading run navigation"
|
|
677
|
-
className="hidden w-240 shrink-0
|
|
685
|
+
className="hidden w-240 shrink-0 p-panel-compact min-[768px]:block"
|
|
678
686
|
>
|
|
679
687
|
<div className="flex flex-col gap-group">
|
|
680
688
|
<div className="h-32 rounded-4 bg-background-components-subtle" />
|
|
@@ -12,15 +12,25 @@ const PROJECT_ID = '44444444-4444-4444-8444-444444444444';
|
|
|
12
12
|
const CONNECTION_ID = '33333333-3333-4333-8333-333333333333';
|
|
13
13
|
|
|
14
14
|
describe('ProjectWorkflowsPage', () => {
|
|
15
|
-
test('renders workflow definitions and
|
|
15
|
+
test('renders workflow definitions and their panel regions', async () => {
|
|
16
16
|
configureApiClient({fetchImpl: createProjectDetailFetch()});
|
|
17
17
|
|
|
18
18
|
renderWorkflowsPage();
|
|
19
19
|
|
|
20
|
-
expect(await screen.
|
|
21
|
-
expect(screen.
|
|
20
|
+
expect((await screen.findAllByText('Deploy production'))[0]).toBeInTheDocument();
|
|
21
|
+
expect(screen.getByRole('heading', {name: 'Workflows'})).toHaveClass('sr-only');
|
|
22
|
+
expect(
|
|
23
|
+
screen.queryByText('Synced workflow definitions for this project source.'),
|
|
24
|
+
).not.toBeInTheDocument();
|
|
22
25
|
expect(screen.getAllByText('.shipfox/workflows/deploy.yml')[0]).toBeInTheDocument();
|
|
23
|
-
|
|
26
|
+
const sourcePanel = screen
|
|
27
|
+
.getByRole('region', {name: 'Project source'})
|
|
28
|
+
.closest('[data-slot="panel"]');
|
|
29
|
+
const definitionsPanel = screen.getByRole('region', {name: 'Workflow definitions'});
|
|
30
|
+
expect(sourcePanel).toBeInTheDocument();
|
|
31
|
+
expect(definitionsPanel).toBeInTheDocument();
|
|
32
|
+
expect(sourcePanel).not.toBe(definitionsPanel);
|
|
33
|
+
expect(screen.getByRole('table').closest('[data-slot="panel"]')).toBe(definitionsPanel);
|
|
24
34
|
// Source strip resolves connection display_name from the integrations
|
|
25
35
|
// workspace cache; external_repository_id renders as a Code chip.
|
|
26
36
|
expect(await screen.findByText('Acme GitHub')).toBeInTheDocument();
|
|
@@ -68,6 +68,10 @@ function ProjectWorkflowsPageInner({projectId}: {projectId: string}) {
|
|
|
68
68
|
|
|
69
69
|
return (
|
|
70
70
|
<div className="flex w-full flex-col gap-section">
|
|
71
|
+
<Header variant="h1" className="sr-only">
|
|
72
|
+
Workflows
|
|
73
|
+
</Header>
|
|
74
|
+
|
|
71
75
|
{projectQuery.isPending ? (
|
|
72
76
|
<div className="flex flex-col gap-cluster">
|
|
73
77
|
<Skeleton className="h-28 w-1/3" />
|
|
@@ -89,13 +93,6 @@ function ProjectWorkflowsPageInner({projectId}: {projectId: string}) {
|
|
|
89
93
|
|
|
90
94
|
{projectQuery.data ? (
|
|
91
95
|
<>
|
|
92
|
-
<header className="flex flex-col gap-tight">
|
|
93
|
-
<Header variant="h2">Workflows</Header>
|
|
94
|
-
<Text size="sm" className="text-foreground-neutral-muted">
|
|
95
|
-
Synced workflow definitions for this project source.
|
|
96
|
-
</Text>
|
|
97
|
-
</header>
|
|
98
|
-
|
|
99
96
|
<SourceStrip
|
|
100
97
|
connectionId={projectQuery.data.source.connectionId}
|
|
101
98
|
externalRepositoryId={projectQuery.data.source.externalRepositoryId}
|
|
@@ -171,152 +168,161 @@ function WorkflowDefinitionsList({
|
|
|
171
168
|
}) {
|
|
172
169
|
if (isPending) {
|
|
173
170
|
return (
|
|
174
|
-
<
|
|
175
|
-
<Skeleton className="h-
|
|
176
|
-
<Skeleton className="h-
|
|
177
|
-
<Skeleton className="h-
|
|
178
|
-
</
|
|
171
|
+
<Panel role="status" aria-label="Loading workflows" className="divide-y">
|
|
172
|
+
<Skeleton className="h-44 w-full rounded-none" />
|
|
173
|
+
<Skeleton className="h-44 w-full rounded-none" />
|
|
174
|
+
<Skeleton className="h-44 w-full rounded-none" />
|
|
175
|
+
</Panel>
|
|
179
176
|
);
|
|
180
177
|
}
|
|
181
178
|
|
|
182
179
|
if (isError && definitions.length === 0) {
|
|
183
180
|
return (
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
181
|
+
<Panel role="region" aria-label="Workflow definitions">
|
|
182
|
+
<LoadErrorState
|
|
183
|
+
title="Couldn't load workflows"
|
|
184
|
+
description="Definitions could not be loaded. Source metadata remains visible."
|
|
185
|
+
onRetry={onRetry}
|
|
186
|
+
retryLabel="Retry loading workflows"
|
|
187
|
+
variant="panel"
|
|
188
|
+
/>
|
|
189
|
+
</Panel>
|
|
190
190
|
);
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
if (definitions.length === 0) {
|
|
194
|
-
return
|
|
194
|
+
return (
|
|
195
|
+
<Panel role="region" aria-label="Workflow definitions">
|
|
196
|
+
<WorkflowEmptyState sync={sync} />
|
|
197
|
+
</Panel>
|
|
198
|
+
);
|
|
195
199
|
}
|
|
196
200
|
|
|
197
201
|
return (
|
|
198
202
|
<>
|
|
199
|
-
<Panel
|
|
200
|
-
<
|
|
201
|
-
<
|
|
202
|
-
<
|
|
203
|
-
<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
203
|
+
<Panel role="region" aria-label="Workflow definitions">
|
|
204
|
+
<div className="hidden md:block">
|
|
205
|
+
<Table>
|
|
206
|
+
<TableHeader>
|
|
207
|
+
<TableRow>
|
|
208
|
+
<TableHead className="w-40"></TableHead>
|
|
209
|
+
<TableHead>Workflow</TableHead>
|
|
210
|
+
<TableHead className="w-180">Updated</TableHead>
|
|
211
|
+
<TableHead className="w-80 text-right"></TableHead>
|
|
212
|
+
</TableRow>
|
|
213
|
+
</TableHeader>
|
|
214
|
+
<TableBody>
|
|
215
|
+
{definitions.map((definition) => {
|
|
216
|
+
const runErrorMessage =
|
|
217
|
+
runError?.definitionId === definition.id ? runError.message : null;
|
|
218
|
+
const isRunning = runningDefinitionId === definition.id;
|
|
214
219
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
</div>
|
|
251
|
-
</TableCell>
|
|
252
|
-
<TableCell className="text-foreground-neutral-muted">
|
|
253
|
-
<RelativeTime value={definition.updatedAt} />
|
|
254
|
-
</TableCell>
|
|
255
|
-
<TableCell>
|
|
256
|
-
{definition.manualTrigger ? (
|
|
257
|
-
<div className="flex justify-end opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100">
|
|
258
|
-
<Button size="xs" isLoading={isRunning} onClick={() => onRun(definition)}>
|
|
259
|
-
Run
|
|
260
|
-
</Button>
|
|
220
|
+
return (
|
|
221
|
+
// The workflow-name cell holds a `<button>` so the row is
|
|
222
|
+
// keyboard-reachable (Tab focuses, Enter/Space activates
|
|
223
|
+
// via native button semantics). The TableRow itself is no
|
|
224
|
+
// longer clickable: a row-level onClick would be invisible
|
|
225
|
+
// to keyboard users and require custom keydown handling.
|
|
226
|
+
// The `group` class on the row still drives the Run button
|
|
227
|
+
// reveal on hover or focus-within.
|
|
228
|
+
<TableRow key={definition.id} className="group">
|
|
229
|
+
<TableCell>
|
|
230
|
+
<Icon
|
|
231
|
+
name={sourceIcon(definition.source)}
|
|
232
|
+
className="size-16 text-foreground-neutral-muted"
|
|
233
|
+
aria-hidden="true"
|
|
234
|
+
/>
|
|
235
|
+
</TableCell>
|
|
236
|
+
<TableCell className="max-w-260">
|
|
237
|
+
<div className="flex min-w-0 flex-col gap-tight">
|
|
238
|
+
<button
|
|
239
|
+
type="button"
|
|
240
|
+
onClick={() => onOpenDefinition(definition)}
|
|
241
|
+
className="flex min-w-0 flex-col gap-tight rounded-4 text-left outline-none focus-visible:shadow-border-interactive-with-active"
|
|
242
|
+
>
|
|
243
|
+
<Text size="sm" bold className="truncate">
|
|
244
|
+
{definition.name}
|
|
245
|
+
</Text>
|
|
246
|
+
<Code className="truncate text-foreground-neutral-muted">
|
|
247
|
+
{definition.configPath ?? 'Manual definition'}
|
|
248
|
+
</Code>
|
|
249
|
+
</button>
|
|
250
|
+
{runErrorMessage ? (
|
|
251
|
+
<Text size="xs" className="text-tag-error-text">
|
|
252
|
+
{runErrorMessage}
|
|
253
|
+
</Text>
|
|
254
|
+
) : null}
|
|
261
255
|
</div>
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
256
|
+
</TableCell>
|
|
257
|
+
<TableCell className="text-foreground-neutral-muted">
|
|
258
|
+
<RelativeTime value={definition.updatedAt} />
|
|
259
|
+
</TableCell>
|
|
260
|
+
<TableCell>
|
|
261
|
+
{definition.manualTrigger ? (
|
|
262
|
+
<div className="flex justify-end opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100">
|
|
263
|
+
<Button size="xs" isLoading={isRunning} onClick={() => onRun(definition)}>
|
|
264
|
+
Run
|
|
265
|
+
</Button>
|
|
266
|
+
</div>
|
|
267
|
+
) : null}
|
|
268
|
+
</TableCell>
|
|
269
|
+
</TableRow>
|
|
270
|
+
);
|
|
271
|
+
})}
|
|
272
|
+
</TableBody>
|
|
273
|
+
</Table>
|
|
274
|
+
</div>
|
|
270
275
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
+
<div className="flex flex-col md:hidden">
|
|
277
|
+
{definitions.map((definition) => {
|
|
278
|
+
const runErrorMessage =
|
|
279
|
+
runError?.definitionId === definition.id ? runError.message : null;
|
|
280
|
+
const isRunning = runningDefinitionId === definition.id;
|
|
276
281
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
>
|
|
282
|
-
<button
|
|
283
|
-
type="button"
|
|
284
|
-
className="flex min-w-0 items-start gap-inline text-left"
|
|
285
|
-
onClick={() => onOpenDefinition(definition)}
|
|
282
|
+
return (
|
|
283
|
+
<div
|
|
284
|
+
key={definition.id}
|
|
285
|
+
className="flex flex-col gap-inline border-b border-border-neutral-base p-panel-compact last:border-b-0"
|
|
286
286
|
>
|
|
287
|
-
<
|
|
288
|
-
|
|
289
|
-
className="
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
287
|
+
<button
|
|
288
|
+
type="button"
|
|
289
|
+
className="flex min-w-0 items-start gap-inline text-left"
|
|
290
|
+
onClick={() => onOpenDefinition(definition)}
|
|
291
|
+
>
|
|
292
|
+
<Icon
|
|
293
|
+
name={sourceIcon(definition.source)}
|
|
294
|
+
className="size-16 shrink-0 text-foreground-neutral-muted"
|
|
295
|
+
aria-hidden="true"
|
|
296
|
+
/>
|
|
297
|
+
<div className="flex min-w-0 flex-col gap-tight">
|
|
298
|
+
<Text size="sm" bold className="break-words">
|
|
299
|
+
{definition.name}
|
|
300
|
+
</Text>
|
|
301
|
+
<Code className="break-words text-foreground-neutral-muted">
|
|
302
|
+
{definition.configPath ?? 'Manual definition'}
|
|
303
|
+
</Code>
|
|
304
|
+
</div>
|
|
305
|
+
</button>
|
|
306
|
+
<div className="flex items-center justify-between gap-inline">
|
|
307
|
+
<Text size="xs" className="text-foreground-neutral-muted">
|
|
308
|
+
Updated <RelativeTime value={definition.updatedAt} />
|
|
295
309
|
</Text>
|
|
296
|
-
|
|
297
|
-
{
|
|
298
|
-
|
|
310
|
+
{definition.manualTrigger ? (
|
|
311
|
+
<Button size="sm" isLoading={isRunning} onClick={() => onRun(definition)}>
|
|
312
|
+
Run
|
|
313
|
+
</Button>
|
|
314
|
+
) : null}
|
|
299
315
|
</div>
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
</Text>
|
|
305
|
-
{definition.manualTrigger ? (
|
|
306
|
-
<Button size="sm" isLoading={isRunning} onClick={() => onRun(definition)}>
|
|
307
|
-
Run
|
|
308
|
-
</Button>
|
|
316
|
+
{runErrorMessage ? (
|
|
317
|
+
<Text size="xs" className="text-tag-error-text">
|
|
318
|
+
{runErrorMessage}
|
|
319
|
+
</Text>
|
|
309
320
|
) : null}
|
|
310
321
|
</div>
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
) : null}
|
|
316
|
-
</div>
|
|
317
|
-
);
|
|
318
|
-
})}
|
|
319
|
-
</div>
|
|
322
|
+
);
|
|
323
|
+
})}
|
|
324
|
+
</div>
|
|
325
|
+
</Panel>
|
|
320
326
|
|
|
321
327
|
{isFetchNextPageError ? (
|
|
322
328
|
<Callout role="alert" type="error">
|
|
@@ -356,7 +362,7 @@ function WorkflowEmptyState({sync}: {sync: DefinitionSyncSummary | null}) {
|
|
|
356
362
|
? 'No workflow definitions found.'
|
|
357
363
|
: 'Workflow sync has not reported yet.';
|
|
358
364
|
|
|
359
|
-
return <EmptyState icon="flowChart" title="No workflows" description={message} />;
|
|
365
|
+
return <EmptyState icon="flowChart" title="No workflows" description={message} variant="panel" />;
|
|
360
366
|
}
|
|
361
367
|
|
|
362
368
|
function WorkflowSyncAlert({sync}: {sync: DefinitionSyncSummary | null | undefined}) {
|