@shipfox/client-triggers 5.0.0 → 6.0.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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +50 -0
- package/dist/components/events-filter-bar.d.ts +3 -4
- package/dist/components/events-filter-bar.d.ts.map +1 -1
- package/dist/components/events-filter-bar.js +8 -16
- package/dist/components/events-filter-bar.js.map +1 -1
- package/dist/components/events-list.d.ts.map +1 -1
- package/dist/components/events-list.js +2 -4
- package/dist/components/events-list.js.map +1 -1
- package/dist/components/events-list.stories.d.ts +1 -16
- package/dist/components/events-list.stories.d.ts.map +1 -1
- package/dist/components/events-list.stories.js +2 -1
- package/dist/components/events-list.stories.js.map +1 -1
- package/dist/components/trigger-event-detail.d.ts +2 -2
- package/dist/components/trigger-event-detail.d.ts.map +1 -1
- package/dist/components/trigger-event-detail.js +7 -7
- package/dist/components/trigger-event-detail.js.map +1 -1
- package/dist/components/trigger-event-detail.stories.d.ts +1 -36
- package/dist/components/trigger-event-detail.stories.d.ts.map +1 -1
- package/dist/components/trigger-event-detail.stories.js +6 -3
- package/dist/components/trigger-event-detail.stories.js.map +1 -1
- package/dist/components/trigger-event-result.d.ts +2 -2
- package/dist/components/trigger-event-result.d.ts.map +1 -1
- package/dist/components/trigger-event-result.js +8 -16
- package/dist/components/trigger-event-result.js.map +1 -1
- package/dist/components/trigger-event-row.d.ts +2 -2
- package/dist/components/trigger-event-row.d.ts.map +1 -1
- package/dist/components/trigger-event-row.js +1 -1
- package/dist/components/trigger-event-row.js.map +1 -1
- package/dist/components/trigger-source-icon.d.ts +8 -6
- package/dist/components/trigger-source-icon.d.ts.map +1 -1
- package/dist/components/trigger-source-icon.js +4 -3
- package/dist/components/trigger-source-icon.js.map +1 -1
- package/dist/components/types.d.ts +3 -4
- package/dist/components/types.d.ts.map +1 -1
- package/dist/components/types.js.map +1 -1
- package/dist/core/trigger-event.d.ts +78 -0
- package/dist/core/trigger-event.d.ts.map +1 -0
- package/dist/core/trigger-event.js +71 -0
- package/dist/core/trigger-event.js.map +1 -0
- package/dist/feature.d.ts +7 -0
- package/dist/feature.d.ts.map +1 -1
- package/dist/feature.js +10 -9
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/trigger-event-mapper.d.ts +7 -0
- package/dist/hooks/api/trigger-event-mapper.d.ts.map +1 -0
- package/dist/hooks/api/trigger-event-mapper.js +57 -0
- package/dist/hooks/api/trigger-event-mapper.js.map +1 -0
- package/dist/hooks/api/trigger-events.d.ts +15 -138
- package/dist/hooks/api/trigger-events.d.ts.map +1 -1
- package/dist/hooks/api/trigger-events.js +44 -47
- package/dist/hooks/api/trigger-events.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/pages/events-page.d.ts +1 -1
- package/dist/pages/events-page.d.ts.map +1 -1
- package/dist/pages/events-page.js +1 -1
- package/dist/pages/events-page.js.map +1 -1
- package/dist/routes/events-settings.js.map +1 -1
- package/dist/search.d.ts +3 -3
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +3 -4
- package/dist/search.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/components/events-filter-bar.tsx +16 -11
- package/src/components/events-list.stories.tsx +3 -2
- package/src/components/events-list.test.tsx +4 -3
- package/src/components/events-list.tsx +2 -12
- package/src/components/trigger-event-detail.stories.tsx +7 -3
- package/src/components/trigger-event-detail.test.tsx +6 -1
- package/src/components/trigger-event-detail.tsx +21 -18
- package/src/components/trigger-event-result.test.ts +6 -6
- package/src/components/trigger-event-result.ts +9 -13
- package/src/components/trigger-event-row.tsx +5 -5
- package/src/components/trigger-source-icon.tsx +9 -7
- package/src/components/types.ts +7 -7
- package/src/core/trigger-event.test.ts +40 -0
- package/src/core/trigger-event.ts +126 -0
- package/src/feature.ts +6 -4
- package/src/hooks/api/trigger-event-mapper.test.ts +88 -0
- package/src/hooks/api/trigger-event-mapper.ts +76 -0
- package/src/hooks/api/trigger-events.test.ts +49 -12
- package/src/hooks/api/trigger-events.ts +96 -56
- package/src/index.ts +1 -0
- package/src/pages/events-page.test.tsx +4 -4
- package/src/pages/events-page.tsx +3 -4
- package/src/routes/events-settings.tsx +1 -1
- package/src/search.ts +4 -5
- package/tsconfig.build.tsbuildinfo +1 -1
- package/vercel.json +5 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/client-triggers",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "6.0.2",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -10,9 +10,6 @@
|
|
|
10
10
|
"type": "module",
|
|
11
11
|
"main": "dist/index.js",
|
|
12
12
|
"types": "dist/index.d.ts",
|
|
13
|
-
"imports": {
|
|
14
|
-
"#*": "./dist/*"
|
|
15
|
-
},
|
|
16
13
|
"exports": {
|
|
17
14
|
".": {
|
|
18
15
|
"types": "./dist/index.d.ts",
|
|
@@ -29,12 +26,12 @@
|
|
|
29
26
|
},
|
|
30
27
|
"dependencies": {
|
|
31
28
|
"@swc/helpers": "^0.5.17",
|
|
32
|
-
"@shipfox/
|
|
33
|
-
"@shipfox/client-
|
|
34
|
-
"@shipfox/client-shell": "
|
|
35
|
-
"@shipfox/client-ui": "
|
|
36
|
-
"@shipfox/
|
|
37
|
-
"@shipfox/react-ui": "0.3.
|
|
29
|
+
"@shipfox/api-triggers-dto": "9.0.2",
|
|
30
|
+
"@shipfox/client-api": "6.0.1",
|
|
31
|
+
"@shipfox/client-shell": "6.0.2",
|
|
32
|
+
"@shipfox/client-ui": "6.0.2",
|
|
33
|
+
"@shipfox/integration-icons": "0.2.2",
|
|
34
|
+
"@shipfox/react-ui": "0.3.7"
|
|
38
35
|
},
|
|
39
36
|
"peerDependencies": {
|
|
40
37
|
"@tanstack/react-query": "^5.101.0",
|
|
@@ -43,6 +40,9 @@
|
|
|
43
40
|
"react-dom": "^19.0.0",
|
|
44
41
|
"zod": "^4.4.3"
|
|
45
42
|
},
|
|
43
|
+
"imports": {
|
|
44
|
+
"#*": "./dist/*"
|
|
45
|
+
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "shipfox-swc",
|
|
48
48
|
"check": "shipfox-biome-check",
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {TriggerEventFacetItemDto, TriggerEventOutcomeDto} from '@shipfox/api-triggers-dto';
|
|
2
1
|
import {Button} from '@shipfox/react-ui/button';
|
|
3
2
|
import {Collapsible, CollapsibleContent, CollapsibleTrigger} from '@shipfox/react-ui/collapsible';
|
|
4
3
|
import {Combobox, type ComboboxOption} from '@shipfox/react-ui/combobox';
|
|
@@ -6,24 +5,30 @@ import {type DateRange, DateRangePicker} from '@shipfox/react-ui/date-range-pick
|
|
|
6
5
|
import {Text} from '@shipfox/react-ui/typography';
|
|
7
6
|
import {cn} from '@shipfox/react-ui/utils';
|
|
8
7
|
import {type ReactNode, useState} from 'react';
|
|
9
|
-
import
|
|
8
|
+
import {
|
|
9
|
+
type TriggerEventFacetItem,
|
|
10
|
+
type TriggerEventFilters,
|
|
11
|
+
type TriggerEventOutcome,
|
|
12
|
+
type TriggerEventResultKind,
|
|
13
|
+
triggerEventResultFilterOutcomes,
|
|
14
|
+
} from '#core/trigger-event.js';
|
|
10
15
|
|
|
11
16
|
// Filters expose the same plain-language results shown in the table. `failed`
|
|
12
17
|
// includes `errored` so the Failed filter does not hide rows rendered as failed.
|
|
13
18
|
const RESULT_FILTERS: {
|
|
14
|
-
value:
|
|
15
|
-
selects:
|
|
19
|
+
value: TriggerEventResultKind;
|
|
20
|
+
selects: readonly TriggerEventOutcome[];
|
|
16
21
|
label: string;
|
|
17
22
|
}[] = [
|
|
18
|
-
{value: '
|
|
19
|
-
{value: '
|
|
20
|
-
{value: 'failed', selects:
|
|
21
|
-
{value: '
|
|
23
|
+
{value: 'triggered', selects: triggerEventResultFilterOutcomes.triggered, label: 'Triggered'},
|
|
24
|
+
{value: 'no-match', selects: triggerEventResultFilterOutcomes['no-match'], label: 'No match'},
|
|
25
|
+
{value: 'failed', selects: triggerEventResultFilterOutcomes.failed, label: 'Failed'},
|
|
26
|
+
{value: 'evaluating', selects: triggerEventResultFilterOutcomes.evaluating, label: 'Evaluating'},
|
|
22
27
|
];
|
|
23
28
|
|
|
24
29
|
const RESULT_OPTIONS: ComboboxOption[] = RESULT_FILTERS.map(({value, label}) => ({value, label}));
|
|
25
30
|
|
|
26
|
-
function toOptions(facets:
|
|
31
|
+
function toOptions(facets: TriggerEventFacetItem[] | undefined): ComboboxOption[] {
|
|
27
32
|
return (facets ?? []).map((facet) => ({
|
|
28
33
|
value: facet.value,
|
|
29
34
|
label: `${facet.value} (${facet.count})`,
|
|
@@ -33,8 +38,8 @@ function toOptions(facets: TriggerEventFacetItemDto[] | undefined): ComboboxOpti
|
|
|
33
38
|
interface EventsFilterBarProps {
|
|
34
39
|
filters: TriggerEventFilters;
|
|
35
40
|
onFiltersChange: (patch: Partial<TriggerEventFilters>) => void;
|
|
36
|
-
sources:
|
|
37
|
-
events:
|
|
41
|
+
sources: TriggerEventFacetItem[] | undefined;
|
|
42
|
+
events: TriggerEventFacetItem[] | undefined;
|
|
38
43
|
hasActiveFilters: boolean;
|
|
39
44
|
onClear: () => void;
|
|
40
45
|
}
|
|
@@ -7,6 +7,7 @@ import {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';
|
|
|
7
7
|
import {Code} from '@shipfox/react-ui/typography';
|
|
8
8
|
import type {Decorator, Meta, StoryObj} from '@storybook/react';
|
|
9
9
|
import type {ReactNode} from 'react';
|
|
10
|
+
import {toTriggerEventSummary} from '#hooks/api/trigger-event-mapper.js';
|
|
10
11
|
import {EventsList} from './events-list.js';
|
|
11
12
|
import type {TriggerEventsListQuery} from './types.js';
|
|
12
13
|
|
|
@@ -62,13 +63,13 @@ function makeEvent(
|
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
const SAMPLE_EVENTS
|
|
66
|
+
const SAMPLE_EVENTS = [
|
|
66
67
|
makeEvent('received', 'github', 'github_acme', 'push', 0, 0),
|
|
67
68
|
makeEvent('routed', 'github', 'github_acme', 'push', 2, 3),
|
|
68
69
|
makeEvent('routed', 'gitea', 'gitea_acme', 'pull_request', 1, 12),
|
|
69
70
|
makeEvent('discarded', 'github', 'github_acme', 'issue_comment', 0, 38),
|
|
70
71
|
makeEvent('failed', 'gitlab', 'gitlab_acme', 'push', 1, 95),
|
|
71
|
-
];
|
|
72
|
+
].map(toTriggerEventSummary);
|
|
72
73
|
|
|
73
74
|
const SAMPLE_FACETS: TriggerEventFacetsResponseDto = {
|
|
74
75
|
sources: [
|
|
@@ -2,11 +2,12 @@ import type {TriggerEventListItemDto} from '@shipfox/api-triggers-dto';
|
|
|
2
2
|
import {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';
|
|
3
3
|
import {render, screen} from '@testing-library/react';
|
|
4
4
|
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import {toTriggerEventSummary} from '#hooks/api/trigger-event-mapper.js';
|
|
5
6
|
import {EventsList} from './events-list.js';
|
|
6
7
|
import type {EventsListProps} from './types.js';
|
|
7
8
|
|
|
8
|
-
function makeEvent(overrides: Partial<TriggerEventListItemDto> = {})
|
|
9
|
-
return {
|
|
9
|
+
function makeEvent(overrides: Partial<TriggerEventListItemDto> = {}) {
|
|
10
|
+
return toTriggerEventSummary({
|
|
10
11
|
id: 'evt-1',
|
|
11
12
|
event_ref: 'ref-1',
|
|
12
13
|
origin: 'integration',
|
|
@@ -22,7 +23,7 @@ function makeEvent(overrides: Partial<TriggerEventListItemDto> = {}): TriggerEve
|
|
|
22
23
|
processed_at: '2026-06-01T00:00:00.000Z',
|
|
23
24
|
created_at: '2026-06-01T00:00:00.000Z',
|
|
24
25
|
...overrides,
|
|
25
|
-
};
|
|
26
|
+
});
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
function makeProps(overrides: Partial<EventsListProps> = {}): EventsListProps {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {QueryLoadError} from '@shipfox/client-ui';
|
|
2
2
|
import {Button} from '@shipfox/react-ui/button';
|
|
3
3
|
import {Table, TableBody, TableHead, TableHeader, TableRow} from '@shipfox/react-ui/table';
|
|
4
|
-
import
|
|
4
|
+
import {hasTriggerEventFilters} from '#core/trigger-event.js';
|
|
5
5
|
import {EventsFilterBar} from './events-filter-bar.js';
|
|
6
6
|
import {
|
|
7
7
|
EventsListEmpty,
|
|
@@ -12,16 +12,6 @@ import {
|
|
|
12
12
|
import {TriggerEventRow} from './trigger-event-row.js';
|
|
13
13
|
import type {EventsListProps} from './types.js';
|
|
14
14
|
|
|
15
|
-
function hasAnyFilter(filters: TriggerEventFilters): boolean {
|
|
16
|
-
return Boolean(
|
|
17
|
-
(filters.source && filters.source.length > 0) ||
|
|
18
|
-
(filters.event && filters.event.length > 0) ||
|
|
19
|
-
filters.from ||
|
|
20
|
-
filters.to ||
|
|
21
|
-
(filters.outcome && filters.outcome.length > 0),
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
15
|
export function EventsList({
|
|
26
16
|
events,
|
|
27
17
|
query,
|
|
@@ -35,7 +25,7 @@ export function EventsList({
|
|
|
35
25
|
selectedEventId,
|
|
36
26
|
onSelectEvent,
|
|
37
27
|
}: EventsListProps) {
|
|
38
|
-
const activeFilters =
|
|
28
|
+
const activeFilters = hasTriggerEventFilters(filters);
|
|
39
29
|
const refreshFailed = query.isError && query.data !== undefined;
|
|
40
30
|
const showEmptyState = !query.isPending && !query.isError && events.length === 0;
|
|
41
31
|
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
RouterProvider,
|
|
12
12
|
} from '@tanstack/react-router';
|
|
13
13
|
import {screen} from 'storybook/test';
|
|
14
|
+
import {toTriggerEventDetail} from '#hooks/api/trigger-event-mapper.js';
|
|
14
15
|
import {TriggerEventDetailView} from './trigger-event-detail.js';
|
|
15
16
|
|
|
16
17
|
const WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';
|
|
@@ -53,7 +54,7 @@ const withRouter: Decorator = (Story) => {
|
|
|
53
54
|
return <RouterProvider router={router} />;
|
|
54
55
|
};
|
|
55
56
|
|
|
56
|
-
const
|
|
57
|
+
const routedEventDto: TriggerEventDetailResponseDto = {
|
|
57
58
|
id: '44444444-4444-4444-8444-444444444444',
|
|
58
59
|
event_ref: 'github:delivery-179:push',
|
|
59
60
|
origin: 'integration',
|
|
@@ -114,8 +115,8 @@ const routedEvent: TriggerEventDetailResponseDto = {
|
|
|
114
115
|
],
|
|
115
116
|
};
|
|
116
117
|
|
|
117
|
-
const
|
|
118
|
-
...
|
|
118
|
+
const discardedEventDto: TriggerEventDetailResponseDto = {
|
|
119
|
+
...routedEventDto,
|
|
119
120
|
id: 'dddddddd-dddd-4ddd-8ddd-dddddddddddd',
|
|
120
121
|
event_ref: 'github:delivery-180:issue_comment',
|
|
121
122
|
event: 'issue_comment',
|
|
@@ -127,6 +128,9 @@ const discardedEvent: TriggerEventDetailResponseDto = {
|
|
|
127
128
|
decisions: [],
|
|
128
129
|
};
|
|
129
130
|
|
|
131
|
+
const routedEvent = toTriggerEventDetail(routedEventDto);
|
|
132
|
+
const discardedEvent = toTriggerEventDetail(discardedEventDto);
|
|
133
|
+
|
|
130
134
|
const meta = {
|
|
131
135
|
title: 'Triggers/EventDetail',
|
|
132
136
|
component: TriggerEventDetailView,
|
|
@@ -3,6 +3,7 @@ import {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';
|
|
|
3
3
|
import {fireEvent, render, screen} from '@testing-library/react';
|
|
4
4
|
import userEvent from '@testing-library/user-event';
|
|
5
5
|
import type {ReactElement} from 'react';
|
|
6
|
+
import {toTriggerEventDetail} from '#hooks/api/trigger-event-mapper.js';
|
|
6
7
|
import {useTriggerEventQuery} from '#hooks/api/trigger-events.js';
|
|
7
8
|
import {TriggerEventDetail, TriggerEventDetailView} from './trigger-event-detail.js';
|
|
8
9
|
|
|
@@ -65,7 +66,11 @@ function renderWithProviders(ui: ReactElement) {
|
|
|
65
66
|
|
|
66
67
|
function renderDetailView(event: TriggerEventDetailResponseDto, onBack = vi.fn()) {
|
|
67
68
|
return renderWithProviders(
|
|
68
|
-
<TriggerEventDetailView
|
|
69
|
+
<TriggerEventDetailView
|
|
70
|
+
workspaceId={WORKSPACE_ID}
|
|
71
|
+
event={toTriggerEventDetail(event)}
|
|
72
|
+
onBack={onBack}
|
|
73
|
+
/>,
|
|
69
74
|
);
|
|
70
75
|
}
|
|
71
76
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {TriggerDecisionDto, TriggerEventDetailResponseDto} from '@shipfox/api-triggers-dto';
|
|
2
1
|
import {Badge} from '@shipfox/react-ui/badge';
|
|
3
2
|
import {Button} from '@shipfox/react-ui/button';
|
|
4
3
|
import {Callout} from '@shipfox/react-ui/callout';
|
|
@@ -21,6 +20,10 @@ import {Code, Text} from '@shipfox/react-ui/typography';
|
|
|
21
20
|
import {cn} from '@shipfox/react-ui/utils';
|
|
22
21
|
import {Link} from '@tanstack/react-router';
|
|
23
22
|
import {useMemo} from 'react';
|
|
23
|
+
import type {
|
|
24
|
+
TriggerEventDetail as TriggerEventDetailModel,
|
|
25
|
+
TriggerEventMatchedWorkflowResult,
|
|
26
|
+
} from '#core/trigger-event.js';
|
|
24
27
|
import {useTriggerEventQuery} from '#hooks/api/trigger-events.js';
|
|
25
28
|
import {triggerEventResult} from './trigger-event-result.js';
|
|
26
29
|
import {TriggerSourceIcon} from './trigger-source-icon.js';
|
|
@@ -52,7 +55,7 @@ export function TriggerEventDetailView({
|
|
|
52
55
|
onBack,
|
|
53
56
|
}: {
|
|
54
57
|
workspaceId: string;
|
|
55
|
-
event:
|
|
58
|
+
event: TriggerEventDetailModel;
|
|
56
59
|
onBack: () => void;
|
|
57
60
|
}) {
|
|
58
61
|
const result = triggerEventResult(event);
|
|
@@ -106,7 +109,7 @@ export function TriggerEventDetailView({
|
|
|
106
109
|
</TooltipContent>
|
|
107
110
|
</Tooltip>
|
|
108
111
|
<Text size="xs" className="truncate text-foreground-neutral-muted">
|
|
109
|
-
<RelativeTime value={event.
|
|
112
|
+
<RelativeTime value={event.receivedAt} />
|
|
110
113
|
</Text>
|
|
111
114
|
</div>
|
|
112
115
|
<Badge variant={result.badge} size="xs">
|
|
@@ -123,11 +126,11 @@ export function TriggerEventDetailView({
|
|
|
123
126
|
);
|
|
124
127
|
}
|
|
125
128
|
|
|
126
|
-
function triggerEventDisplayLabel(event: Pick<
|
|
129
|
+
function triggerEventDisplayLabel(event: Pick<TriggerEventDetailModel, 'event' | 'source'>) {
|
|
127
130
|
return event.event || event.source;
|
|
128
131
|
}
|
|
129
132
|
|
|
130
|
-
function triggerEventFullLabel(event: Pick<
|
|
133
|
+
function triggerEventFullLabel(event: Pick<TriggerEventDetailModel, 'event' | 'source'>) {
|
|
131
134
|
return [event.source, event.event].filter(Boolean).join(' · ');
|
|
132
135
|
}
|
|
133
136
|
|
|
@@ -196,13 +199,7 @@ function TriggerEventDetailError({onBack, onRetry}: {onBack: () => void; onRetry
|
|
|
196
199
|
);
|
|
197
200
|
}
|
|
198
201
|
|
|
199
|
-
function EventRuns({
|
|
200
|
-
workspaceId,
|
|
201
|
-
event,
|
|
202
|
-
}: {
|
|
203
|
-
workspaceId: string;
|
|
204
|
-
event: TriggerEventDetailResponseDto;
|
|
205
|
-
}) {
|
|
202
|
+
function EventRuns({workspaceId, event}: {workspaceId: string; event: TriggerEventDetailModel}) {
|
|
206
203
|
if (event.decisions.length === 0) {
|
|
207
204
|
if (event.outcome === 'discarded') {
|
|
208
205
|
return (
|
|
@@ -228,8 +225,14 @@ function EventRuns({
|
|
|
228
225
|
);
|
|
229
226
|
}
|
|
230
227
|
|
|
231
|
-
function DecisionRow({
|
|
232
|
-
|
|
228
|
+
function DecisionRow({
|
|
229
|
+
workspaceId,
|
|
230
|
+
decision,
|
|
231
|
+
}: {
|
|
232
|
+
workspaceId: string;
|
|
233
|
+
decision: TriggerEventMatchedWorkflowResult;
|
|
234
|
+
}) {
|
|
235
|
+
if (decision.decision !== 'triggered' || !decision.runId || !decision.runName) {
|
|
233
236
|
return (
|
|
234
237
|
<li className="flex min-w-0 items-start gap-8 rounded-6 px-8 py-6">
|
|
235
238
|
<Icon
|
|
@@ -239,7 +242,7 @@ function DecisionRow({workspaceId, decision}: {workspaceId: string; decision: Tr
|
|
|
239
242
|
/>
|
|
240
243
|
<div className="flex min-w-0 flex-col gap-1">
|
|
241
244
|
<Text size="sm" className="min-w-0 truncate text-foreground-neutral-base">
|
|
242
|
-
{decision.
|
|
245
|
+
{decision.subscriptionName}
|
|
243
246
|
</Text>
|
|
244
247
|
{decision.reason ? (
|
|
245
248
|
<Text size="xs" className="text-foreground-highlight-error">
|
|
@@ -259,7 +262,7 @@ function DecisionRow({workspaceId, decision}: {workspaceId: string; decision: Tr
|
|
|
259
262
|
<li>
|
|
260
263
|
<Link
|
|
261
264
|
to="/workspaces/$wid/projects/$pid/runs/$workflowRunId"
|
|
262
|
-
params={{wid: workspaceId, pid: decision.
|
|
265
|
+
params={{wid: workspaceId, pid: decision.projectId ?? '', workflowRunId: decision.runId}}
|
|
263
266
|
className="flex min-w-0 items-start gap-8 rounded-6 px-8 py-6 transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none"
|
|
264
267
|
>
|
|
265
268
|
<Icon
|
|
@@ -269,10 +272,10 @@ function DecisionRow({workspaceId, decision}: {workspaceId: string; decision: Tr
|
|
|
269
272
|
/>
|
|
270
273
|
<span className="flex min-w-0 flex-col gap-1">
|
|
271
274
|
<Text as="span" size="sm" className="min-w-0 truncate text-foreground-neutral-base">
|
|
272
|
-
{decision.
|
|
275
|
+
{decision.subscriptionName}
|
|
273
276
|
</Text>
|
|
274
277
|
<Code as="span" variant="label" className="truncate text-foreground-neutral-muted">
|
|
275
|
-
{decision.
|
|
278
|
+
{decision.runName}
|
|
276
279
|
</Code>
|
|
277
280
|
</span>
|
|
278
281
|
</Link>
|
|
@@ -2,23 +2,23 @@ import {triggerEventResult} from './trigger-event-result.js';
|
|
|
2
2
|
|
|
3
3
|
describe('triggerEventResult', () => {
|
|
4
4
|
test('routed reads as triggered workflows and pluralizes', () => {
|
|
5
|
-
expect(triggerEventResult({outcome: 'routed',
|
|
5
|
+
expect(triggerEventResult({outcome: 'routed', matchedCount: 2}).label).toBe(
|
|
6
6
|
'Triggered 2 workflows',
|
|
7
7
|
);
|
|
8
|
-
expect(triggerEventResult({outcome: 'routed',
|
|
8
|
+
expect(triggerEventResult({outcome: 'routed', matchedCount: 1}).label).toBe(
|
|
9
9
|
'Triggered 1 workflow',
|
|
10
10
|
);
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
test('routed uses the info badge and is not failed', () => {
|
|
14
|
-
const result = triggerEventResult({outcome: 'routed',
|
|
14
|
+
const result = triggerEventResult({outcome: 'routed', matchedCount: 1});
|
|
15
15
|
|
|
16
16
|
expect(result.badge).toBe('info');
|
|
17
17
|
expect(result.isFailure).toBe(false);
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
test('discarded reads as no workflows triggered', () => {
|
|
21
|
-
const result = triggerEventResult({outcome: 'discarded',
|
|
21
|
+
const result = triggerEventResult({outcome: 'discarded', matchedCount: 0});
|
|
22
22
|
|
|
23
23
|
expect(result.label).toBe('No workflows triggered');
|
|
24
24
|
expect(result.badge).toBe('neutral');
|
|
@@ -26,7 +26,7 @@ describe('triggerEventResult', () => {
|
|
|
26
26
|
|
|
27
27
|
test('failed and errored read as failed with the error badge', () => {
|
|
28
28
|
for (const outcome of ['failed', 'errored'] as const) {
|
|
29
|
-
const result = triggerEventResult({outcome,
|
|
29
|
+
const result = triggerEventResult({outcome, matchedCount: 1});
|
|
30
30
|
|
|
31
31
|
expect(result.label).toBe('Failed');
|
|
32
32
|
expect(result.badge).toBe('error');
|
|
@@ -35,6 +35,6 @@ describe('triggerEventResult', () => {
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
test('received reads as evaluating', () => {
|
|
38
|
-
expect(triggerEventResult({outcome: 'received',
|
|
38
|
+
expect(triggerEventResult({outcome: 'received', matchedCount: 0}).label).toBe('Evaluating…');
|
|
39
39
|
});
|
|
40
40
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {TriggerEventListItemDto} from '@shipfox/api-triggers-dto';
|
|
2
1
|
import type {BadgeVariant} from '@shipfox/react-ui/badge';
|
|
2
|
+
import {getTriggerEventResult, type TriggerEventSummary} from '#core/trigger-event.js';
|
|
3
3
|
|
|
4
4
|
export interface TriggerEventResult {
|
|
5
5
|
label: string;
|
|
@@ -14,27 +14,23 @@ export interface TriggerEventResult {
|
|
|
14
14
|
* only by match count.
|
|
15
15
|
*/
|
|
16
16
|
export function triggerEventResult(
|
|
17
|
-
event: Pick<
|
|
17
|
+
event: Pick<TriggerEventSummary, 'outcome' | 'matchedCount'>,
|
|
18
18
|
): TriggerEventResult {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const result = getTriggerEventResult(event);
|
|
20
|
+
switch (result.kind) {
|
|
21
|
+
case 'triggered': {
|
|
22
|
+
const plural = result.matchedWorkflowCount === 1 ? '' : 's';
|
|
22
23
|
return {
|
|
23
|
-
label: `Triggered ${
|
|
24
|
+
label: `Triggered ${result.matchedWorkflowCount} workflow${plural}`,
|
|
24
25
|
badge: 'info',
|
|
25
26
|
isFailure: false,
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
|
-
case '
|
|
29
|
+
case 'no-match':
|
|
29
30
|
return {label: 'No workflows triggered', badge: 'neutral', isFailure: false};
|
|
30
31
|
case 'failed':
|
|
31
|
-
case 'errored':
|
|
32
32
|
return {label: 'Failed', badge: 'error', isFailure: true};
|
|
33
|
-
case '
|
|
33
|
+
case 'evaluating':
|
|
34
34
|
return {label: 'Evaluating…', badge: 'neutral', isFailure: false};
|
|
35
|
-
default: {
|
|
36
|
-
const _exhaustive: never = event.outcome;
|
|
37
|
-
return {label: 'Unknown', badge: 'warning', isFailure: false};
|
|
38
|
-
}
|
|
39
35
|
}
|
|
40
36
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type {TriggerEventListItemDto} from '@shipfox/api-triggers-dto';
|
|
2
1
|
import {RelativeTime} from '@shipfox/react-ui/relative-time';
|
|
3
2
|
import {TableCell, TableRow} from '@shipfox/react-ui/table';
|
|
4
3
|
import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';
|
|
5
4
|
import {Code, Text} from '@shipfox/react-ui/typography';
|
|
6
5
|
import {cn} from '@shipfox/react-ui/utils';
|
|
7
6
|
import type {MouseEvent} from 'react';
|
|
7
|
+
import type {TriggerEventSummary} from '#core/trigger-event.js';
|
|
8
8
|
import {triggerEventResult} from './trigger-event-result.js';
|
|
9
9
|
import {TriggerSourceIcon} from './trigger-source-icon.js';
|
|
10
10
|
|
|
11
11
|
interface TriggerEventRowProps {
|
|
12
|
-
event:
|
|
12
|
+
event: TriggerEventSummary;
|
|
13
13
|
selected: boolean;
|
|
14
14
|
onSelect: (eventId: string) => void;
|
|
15
15
|
}
|
|
@@ -70,17 +70,17 @@ export function TriggerEventRow({event, selected, onSelect}: TriggerEventRowProp
|
|
|
70
70
|
</TableCell>
|
|
71
71
|
<TableCell className="text-right">
|
|
72
72
|
<Code as="span" variant="label" className="text-foreground-neutral-muted">
|
|
73
|
-
<RelativeTime value={event.
|
|
73
|
+
<RelativeTime value={event.receivedAt} />
|
|
74
74
|
</Code>
|
|
75
75
|
</TableCell>
|
|
76
76
|
</TableRow>
|
|
77
77
|
);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function triggerEventDisplayLabel(event: Pick<
|
|
80
|
+
function triggerEventDisplayLabel(event: Pick<TriggerEventSummary, 'event' | 'source'>) {
|
|
81
81
|
return event.event || event.source;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function triggerEventFullLabel(event: Pick<
|
|
84
|
+
function triggerEventFullLabel(event: Pick<TriggerEventSummary, 'event' | 'source'>) {
|
|
85
85
|
return [event.source, event.event].filter(Boolean).join(' · ');
|
|
86
86
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {getIntegrationIcon} from '@shipfox/
|
|
1
|
+
import {getIntegrationIcon} from '@shipfox/integration-icons';
|
|
2
2
|
import {Icon, type IconName} from '@shipfox/react-ui/icon';
|
|
3
3
|
import type {ComponentProps} from 'react';
|
|
4
|
+
import type {TriggerEventSource} from '#core/trigger-event.js';
|
|
4
5
|
|
|
5
6
|
const SYSTEM_TRIGGER_SOURCE_ICONS: Record<string, IconName> = {
|
|
6
7
|
manual: 'cursorLine',
|
|
@@ -8,23 +9,24 @@ const SYSTEM_TRIGGER_SOURCE_ICONS: Record<string, IconName> = {
|
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* Stable presentation seam for trigger-source icons. System sources have
|
|
13
|
+
* first-class icons; integration sources delegate to the shared provider
|
|
14
|
+
* catalog so a new provider's icon shows up here without a second edit.
|
|
13
15
|
*/
|
|
14
16
|
export function getTriggerSourceIcon({
|
|
15
17
|
provider,
|
|
16
18
|
source,
|
|
17
19
|
}: {
|
|
18
|
-
provider
|
|
19
|
-
source
|
|
20
|
+
provider?: TriggerEventSource['provider'] | undefined;
|
|
21
|
+
source?: TriggerEventSource['source'] | undefined;
|
|
20
22
|
}): IconName {
|
|
21
23
|
const systemIcon = source ? SYSTEM_TRIGGER_SOURCE_ICONS[source] : undefined;
|
|
22
24
|
return systemIcon ?? getIntegrationIcon(provider);
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export interface TriggerSourceIconProps extends Omit<ComponentProps<typeof Icon>, 'name'> {
|
|
26
|
-
provider
|
|
27
|
-
source
|
|
28
|
+
provider?: TriggerEventSource['provider'] | undefined;
|
|
29
|
+
source?: TriggerEventSource['source'] | undefined;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
/**
|
package/src/components/types.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
TriggerEventFacetsResponseDto,
|
|
3
|
-
TriggerEventListItemDto,
|
|
4
|
-
} from '@shipfox/api-triggers-dto';
|
|
5
1
|
import type {QueryLoadErrorQuery} from '@shipfox/client-ui';
|
|
6
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
TriggerEventFacets,
|
|
4
|
+
TriggerEventFilters,
|
|
5
|
+
TriggerEventSummary,
|
|
6
|
+
} from '#core/trigger-event.js';
|
|
7
7
|
|
|
8
8
|
export type TriggerEventsListQuery = QueryLoadErrorQuery & {isPending: boolean};
|
|
9
9
|
|
|
10
10
|
export interface EventsListProps {
|
|
11
|
-
events:
|
|
11
|
+
events: TriggerEventSummary[];
|
|
12
12
|
query: TriggerEventsListQuery;
|
|
13
|
-
facets:
|
|
13
|
+
facets: TriggerEventFacets | undefined;
|
|
14
14
|
filters: TriggerEventFilters;
|
|
15
15
|
onFiltersChange: (patch: Partial<TriggerEventFilters>) => void;
|
|
16
16
|
workspaceId: string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getTriggerEventResult,
|
|
3
|
+
hasTriggerEventFilters,
|
|
4
|
+
normalizeTriggerEventFilters,
|
|
5
|
+
triggerEventResultFilterOutcomes,
|
|
6
|
+
} from './trigger-event.js';
|
|
7
|
+
|
|
8
|
+
describe('trigger event policies', () => {
|
|
9
|
+
test('normalizes unordered duplicate filters for query identity', () => {
|
|
10
|
+
const filters = normalizeTriggerEventFilters({
|
|
11
|
+
source: ['github', 'gitea', 'github'],
|
|
12
|
+
event: ['push', 'pull_request', 'push'],
|
|
13
|
+
outcome: ['routed', 'failed', 'routed'],
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
expect(filters).toEqual({
|
|
17
|
+
source: ['gitea', 'github'],
|
|
18
|
+
event: ['pull_request', 'push'],
|
|
19
|
+
outcome: ['failed', 'routed'],
|
|
20
|
+
from: null,
|
|
21
|
+
to: null,
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('treats empty arrays as no active filters', () => {
|
|
26
|
+
expect(hasTriggerEventFilters({source: [], event: [], outcome: []})).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('preserves the failed result filter as both failure outcomes', () => {
|
|
30
|
+
expect(triggerEventResultFilterOutcomes.failed).toEqual(['failed', 'errored']);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('derives failure semantics without presentation details', () => {
|
|
34
|
+
expect(getTriggerEventResult({outcome: 'errored', matchedCount: 1})).toEqual({
|
|
35
|
+
kind: 'failed',
|
|
36
|
+
matchedWorkflowCount: 1,
|
|
37
|
+
isFailure: true,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|