@shipfox/client-triggers 17.0.0 → 21.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +22 -0
- package/dist/components/events-filter-bar.d.ts.map +1 -1
- package/dist/components/events-filter-bar.js +98 -94
- 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 +3 -2
- package/dist/components/events-list.js.map +1 -1
- package/dist/components/trigger-event-detail.d.ts.map +1 -1
- package/dist/components/trigger-event-detail.js +173 -157
- package/dist/components/trigger-event-detail.js.map +1 -1
- package/dist/pages/events-page.d.ts.map +1 -1
- package/dist/pages/events-page.js +29 -47
- package/dist/pages/events-page.js.map +1 -1
- package/dist/routes/events-settings.d.ts +3 -0
- package/dist/routes/events-settings.js +3 -0
- package/dist/routes/events-settings.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/src/components/events-filter-bar.tsx +80 -77
- package/src/components/events-list.test.tsx +9 -0
- package/src/components/events-list.tsx +3 -2
- package/src/components/trigger-event-detail.test.tsx +3 -0
- package/src/components/trigger-event-detail.tsx +129 -126
- package/src/pages/events-page.test.tsx +7 -0
- package/src/pages/events-page.tsx +1 -15
- package/src/routes/events-settings.tsx +1 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/client-triggers",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "21.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"@swc/helpers": "^0.5.17",
|
|
29
29
|
"@shipfox/api-triggers-dto": "9.0.2",
|
|
30
30
|
"@shipfox/client-api": "6.0.1",
|
|
31
|
-
"@shipfox/client-projects": "
|
|
32
|
-
"@shipfox/client-shell": "
|
|
33
|
-
"@shipfox/client-ui": "
|
|
34
|
-
"@shipfox/integration-icons": "0.3.
|
|
35
|
-
"@shipfox/react-ui": "
|
|
31
|
+
"@shipfox/client-projects": "21.0.0",
|
|
32
|
+
"@shipfox/client-shell": "21.0.0",
|
|
33
|
+
"@shipfox/client-ui": "21.0.0",
|
|
34
|
+
"@shipfox/integration-icons": "0.3.4",
|
|
35
|
+
"@shipfox/react-ui": "2.0.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@tanstack/react-query": "^5.101.0",
|
|
@@ -2,6 +2,7 @@ import {Button} from '@shipfox/react-ui/button';
|
|
|
2
2
|
import {Collapsible, CollapsibleContent, CollapsibleTrigger} from '@shipfox/react-ui/collapsible';
|
|
3
3
|
import {Combobox, type ComboboxOption} from '@shipfox/react-ui/combobox';
|
|
4
4
|
import {type DateRange, DateRangePicker} from '@shipfox/react-ui/date-range-picker';
|
|
5
|
+
import {PanelHeader} from '@shipfox/react-ui/panel';
|
|
5
6
|
import {Text} from '@shipfox/react-ui/typography';
|
|
6
7
|
import {cn} from '@shipfox/react-ui/utils';
|
|
7
8
|
import {type ReactNode, useState} from 'react';
|
|
@@ -95,84 +96,86 @@ export function EventsFilterBar({
|
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
return (
|
|
98
|
-
<
|
|
99
|
-
<
|
|
100
|
-
<
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
<CollapsibleContent>
|
|
118
|
-
<div className="grid grid-cols-1 gap-x-cluster gap-y-inline border-b border-border-neutral-base px-row py-row min-[520px]:grid-cols-2">
|
|
119
|
-
<FilterField label="Date" className="col-span-full">
|
|
120
|
-
<DateRangePicker
|
|
121
|
-
size="small"
|
|
122
|
-
className="w-full"
|
|
123
|
-
{...(dateRange ? {dateRange} : {})}
|
|
124
|
-
onDateRangeSelect={handleDateRange}
|
|
125
|
-
onClear={() => onFiltersChange({from: undefined, to: undefined})}
|
|
126
|
-
placeholder="Any date"
|
|
127
|
-
/>
|
|
128
|
-
</FilterField>
|
|
129
|
-
<FilterField label="Result">
|
|
130
|
-
<Combobox
|
|
131
|
-
multiple
|
|
132
|
-
size="small"
|
|
133
|
-
aria-label="Filter by result"
|
|
134
|
-
options={RESULT_OPTIONS}
|
|
135
|
-
value={resultValue}
|
|
136
|
-
onValueChange={handleResultValue}
|
|
137
|
-
placeholder="All results"
|
|
138
|
-
emptyState="No results"
|
|
139
|
-
className="w-full"
|
|
140
|
-
maxVisibleChips={2}
|
|
141
|
-
/>
|
|
142
|
-
</FilterField>
|
|
143
|
-
<FilterField label="Source">
|
|
144
|
-
<Combobox
|
|
145
|
-
multiple
|
|
146
|
-
size="small"
|
|
147
|
-
options={toOptions(sources)}
|
|
148
|
-
value={filters.source ?? []}
|
|
149
|
-
onValueChange={(value) =>
|
|
150
|
-
onFiltersChange({source: value.length > 0 ? value : undefined})
|
|
151
|
-
}
|
|
152
|
-
placeholder="All sources"
|
|
153
|
-
emptyState="No sources yet"
|
|
154
|
-
className="w-full"
|
|
155
|
-
maxVisibleChips={2}
|
|
156
|
-
/>
|
|
157
|
-
</FilterField>
|
|
158
|
-
<FilterField label="Event">
|
|
159
|
-
<Combobox
|
|
160
|
-
multiple
|
|
161
|
-
size="small"
|
|
162
|
-
options={toOptions(events)}
|
|
163
|
-
value={filters.event ?? []}
|
|
164
|
-
onValueChange={(value) =>
|
|
165
|
-
onFiltersChange({event: value.length > 0 ? value : undefined})
|
|
166
|
-
}
|
|
167
|
-
placeholder="All events"
|
|
168
|
-
emptyState="No events yet"
|
|
169
|
-
className="w-full"
|
|
170
|
-
maxVisibleChips={2}
|
|
171
|
-
/>
|
|
172
|
-
</FilterField>
|
|
99
|
+
<PanelHeader className="min-h-48 flex-col items-stretch gap-0 p-0">
|
|
100
|
+
<Collapsible open={open} onOpenChange={setOpen}>
|
|
101
|
+
<div className="flex items-center justify-between gap-inline px-row py-row">
|
|
102
|
+
<CollapsibleTrigger asChild>
|
|
103
|
+
<Button
|
|
104
|
+
type="button"
|
|
105
|
+
variant="secondary"
|
|
106
|
+
size="sm"
|
|
107
|
+
iconLeft="filter3Line"
|
|
108
|
+
iconRight={open ? 'arrowUpSLine' : 'arrowDownSLine'}
|
|
109
|
+
>
|
|
110
|
+
{activeCount > 0 ? `Filters (${activeCount})` : 'Filters'}
|
|
111
|
+
</Button>
|
|
112
|
+
</CollapsibleTrigger>
|
|
113
|
+
{hasActiveFilters ? (
|
|
114
|
+
<Button type="button" size="2xs" variant="transparentMuted" onClick={onClear}>
|
|
115
|
+
Clear filters
|
|
116
|
+
</Button>
|
|
117
|
+
) : null}
|
|
173
118
|
</div>
|
|
174
|
-
|
|
175
|
-
|
|
119
|
+
<CollapsibleContent>
|
|
120
|
+
<div className="grid grid-cols-1 gap-x-cluster gap-y-inline px-row py-row min-[520px]:grid-cols-2">
|
|
121
|
+
<FilterField label="Date" className="col-span-full">
|
|
122
|
+
<DateRangePicker
|
|
123
|
+
size="small"
|
|
124
|
+
className="w-full"
|
|
125
|
+
{...(dateRange ? {dateRange} : {})}
|
|
126
|
+
onDateRangeSelect={handleDateRange}
|
|
127
|
+
onClear={() => onFiltersChange({from: undefined, to: undefined})}
|
|
128
|
+
placeholder="Any date"
|
|
129
|
+
/>
|
|
130
|
+
</FilterField>
|
|
131
|
+
<FilterField label="Result">
|
|
132
|
+
<Combobox
|
|
133
|
+
multiple
|
|
134
|
+
size="small"
|
|
135
|
+
aria-label="Filter by result"
|
|
136
|
+
options={RESULT_OPTIONS}
|
|
137
|
+
value={resultValue}
|
|
138
|
+
onValueChange={handleResultValue}
|
|
139
|
+
placeholder="All results"
|
|
140
|
+
emptyState="No results"
|
|
141
|
+
className="w-full"
|
|
142
|
+
maxVisibleChips={2}
|
|
143
|
+
/>
|
|
144
|
+
</FilterField>
|
|
145
|
+
<FilterField label="Source">
|
|
146
|
+
<Combobox
|
|
147
|
+
multiple
|
|
148
|
+
size="small"
|
|
149
|
+
options={toOptions(sources)}
|
|
150
|
+
value={filters.source ?? []}
|
|
151
|
+
onValueChange={(value) =>
|
|
152
|
+
onFiltersChange({source: value.length > 0 ? value : undefined})
|
|
153
|
+
}
|
|
154
|
+
placeholder="All sources"
|
|
155
|
+
emptyState="No sources yet"
|
|
156
|
+
className="w-full"
|
|
157
|
+
maxVisibleChips={2}
|
|
158
|
+
/>
|
|
159
|
+
</FilterField>
|
|
160
|
+
<FilterField label="Event">
|
|
161
|
+
<Combobox
|
|
162
|
+
multiple
|
|
163
|
+
size="small"
|
|
164
|
+
options={toOptions(events)}
|
|
165
|
+
value={filters.event ?? []}
|
|
166
|
+
onValueChange={(value) =>
|
|
167
|
+
onFiltersChange({event: value.length > 0 ? value : undefined})
|
|
168
|
+
}
|
|
169
|
+
placeholder="All events"
|
|
170
|
+
emptyState="No events yet"
|
|
171
|
+
className="w-full"
|
|
172
|
+
maxVisibleChips={2}
|
|
173
|
+
/>
|
|
174
|
+
</FilterField>
|
|
175
|
+
</div>
|
|
176
|
+
</CollapsibleContent>
|
|
177
|
+
</Collapsible>
|
|
178
|
+
</PanelHeader>
|
|
176
179
|
);
|
|
177
180
|
}
|
|
178
181
|
|
|
@@ -72,6 +72,15 @@ async function selectResult(label: string) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
describe('EventsList', () => {
|
|
75
|
+
test('renders the event list as a panel with a header strip', () => {
|
|
76
|
+
renderList(makeProps());
|
|
77
|
+
|
|
78
|
+
const panel = document.querySelector('[data-slot="panel"]');
|
|
79
|
+
|
|
80
|
+
expect(panel).toBeInTheDocument();
|
|
81
|
+
expect(panel?.querySelector('[data-slot="panel-header"]')).toBeInTheDocument();
|
|
82
|
+
});
|
|
83
|
+
|
|
75
84
|
test('renders a row per event with its match summary', () => {
|
|
76
85
|
renderList(makeProps());
|
|
77
86
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {QueryLoadError} from '@shipfox/client-ui';
|
|
2
2
|
import {Button} from '@shipfox/react-ui/button';
|
|
3
|
+
import {Panel} from '@shipfox/react-ui/panel';
|
|
3
4
|
import {Table, TableBody, TableHead, TableHeader, TableRow} from '@shipfox/react-ui/table';
|
|
4
5
|
import {hasTriggerEventFilters} from '#core/trigger-event.js';
|
|
5
6
|
import {EventsFilterBar} from './events-filter-bar.js';
|
|
@@ -40,7 +41,7 @@ export function EventsList({
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
return (
|
|
43
|
-
<
|
|
44
|
+
<Panel className="min-h-0 overflow-hidden p-0">
|
|
44
45
|
<EventsFilterBar
|
|
45
46
|
filters={filters}
|
|
46
47
|
onFiltersChange={onFiltersChange}
|
|
@@ -92,6 +93,6 @@ export function EventsList({
|
|
|
92
93
|
) : null}
|
|
93
94
|
</>
|
|
94
95
|
) : null}
|
|
95
|
-
</
|
|
96
|
+
</Panel>
|
|
96
97
|
);
|
|
97
98
|
}
|
|
@@ -103,6 +103,9 @@ describe('TriggerEventDetailView', () => {
|
|
|
103
103
|
renderDetailView(makeEvent());
|
|
104
104
|
|
|
105
105
|
expect(await screen.findByText('Triggered 1 workflow')).toBeInTheDocument();
|
|
106
|
+
expect(screen.getByRole('heading', {name: 'Matched workflows'})).toBeInTheDocument();
|
|
107
|
+
expect(screen.getByRole('heading', {name: 'Payload'})).toBeInTheDocument();
|
|
108
|
+
expect(document.querySelectorAll('[data-slot="panel"]')).toHaveLength(2);
|
|
106
109
|
expect(screen.getByText('Deploy production')).toBeInTheDocument();
|
|
107
110
|
expect(screen.getByRole('link', {name: DEPLOY_RUN_LINK_NAME})).toHaveAttribute(
|
|
108
111
|
'href',
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from '@shipfox/react-ui/code-block';
|
|
15
15
|
import {EmptyState} from '@shipfox/react-ui/empty-state';
|
|
16
16
|
import {Icon} from '@shipfox/react-ui/icon';
|
|
17
|
+
import {Panel, PanelBody, PanelHeader, PanelTitle} from '@shipfox/react-ui/panel';
|
|
17
18
|
import {RelativeTime} from '@shipfox/react-ui/relative-time';
|
|
18
19
|
import {Skeleton} from '@shipfox/react-ui/skeleton';
|
|
19
20
|
import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';
|
|
@@ -29,8 +30,6 @@ import {useTriggerEventQuery} from '#hooks/api/trigger-events.js';
|
|
|
29
30
|
import {triggerEventResult} from './trigger-event-result.js';
|
|
30
31
|
import {TriggerSourceIcon} from './trigger-source-icon.js';
|
|
31
32
|
|
|
32
|
-
const PANEL_CLASS =
|
|
33
|
-
'min-h-0 rounded-8 border border-border-neutral-base bg-background-neutral-base';
|
|
34
33
|
const DETAIL_RAIL_CLASS =
|
|
35
34
|
'@min-[820px]:sticky @min-[820px]:top-16 @min-[820px]:max-h-[calc(var(--app-content-h,100dvh_-_96px)_-_32px)] @min-[820px]:min-h-[min(320px,calc(var(--app-content-h,100dvh_-_96px)_-_32px))]';
|
|
36
35
|
|
|
@@ -87,9 +86,9 @@ export function TriggerEventDetailView({
|
|
|
87
86
|
return (
|
|
88
87
|
<aside
|
|
89
88
|
aria-label="Event details"
|
|
90
|
-
className={cn(
|
|
89
|
+
className={cn(DETAIL_RAIL_CLASS, 'flex min-h-0 flex-col gap-group')}
|
|
91
90
|
>
|
|
92
|
-
<div className="flex shrink-0 flex-col gap-cluster
|
|
91
|
+
<div className="flex shrink-0 flex-col gap-cluster p-panel-compact">
|
|
93
92
|
<Button
|
|
94
93
|
type="button"
|
|
95
94
|
variant="transparentMuted"
|
|
@@ -138,7 +137,7 @@ export function TriggerEventDetailView({
|
|
|
138
137
|
|
|
139
138
|
<div
|
|
140
139
|
key={event.id}
|
|
141
|
-
className="flex min-h-0 flex-1 flex-col gap-
|
|
140
|
+
className="flex min-h-0 flex-1 flex-col gap-group overflow-y-auto scrollbar"
|
|
142
141
|
>
|
|
143
142
|
<EventRuns workspaceId={workspaceId} workspaceSlug={workspaceSlug} event={event} />
|
|
144
143
|
<EventPayload payload={formattedPayload} />
|
|
@@ -157,14 +156,10 @@ function triggerEventFullLabel(event: Pick<TriggerEventDetailModel, 'event' | 's
|
|
|
157
156
|
|
|
158
157
|
function TriggerEventDetailPlaceholder() {
|
|
159
158
|
return (
|
|
160
|
-
<aside
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
'hidden min-h-[240px] items-center justify-center p-panel @min-[820px]:flex',
|
|
165
|
-
)}
|
|
166
|
-
>
|
|
167
|
-
<EmptyState icon="pulseLine" variant="compact" title="No event selected" />
|
|
159
|
+
<aside aria-label="Event details" className="hidden min-h-[240px] @min-[820px]:flex">
|
|
160
|
+
<Panel className="flex min-h-0 flex-1 items-center justify-center p-panel">
|
|
161
|
+
<EmptyState icon="pulseLine" variant="compact" title="No event selected" />
|
|
162
|
+
</Panel>
|
|
168
163
|
</aside>
|
|
169
164
|
);
|
|
170
165
|
}
|
|
@@ -173,28 +168,26 @@ function TriggerEventDetailLoading({onBack}: {onBack: () => void}) {
|
|
|
173
168
|
return (
|
|
174
169
|
<aside
|
|
175
170
|
aria-label="Event details"
|
|
176
|
-
className={cn(
|
|
177
|
-
PANEL_CLASS,
|
|
178
|
-
DETAIL_RAIL_CLASS,
|
|
179
|
-
'flex min-h-[320px] flex-col gap-group p-panel-compact',
|
|
180
|
-
)}
|
|
171
|
+
className={cn(DETAIL_RAIL_CLASS, 'flex min-h-[320px] flex-col')}
|
|
181
172
|
>
|
|
182
|
-
<
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
<
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
173
|
+
<Panel className="flex min-h-[320px] flex-col gap-group p-panel-compact">
|
|
174
|
+
<Button
|
|
175
|
+
type="button"
|
|
176
|
+
variant="transparentMuted"
|
|
177
|
+
size="sm"
|
|
178
|
+
iconLeft="arrowLeftLine"
|
|
179
|
+
className="self-start @min-[820px]:hidden"
|
|
180
|
+
onClick={onBack}
|
|
181
|
+
>
|
|
182
|
+
Back to events
|
|
183
|
+
</Button>
|
|
184
|
+
<div className="flex flex-col gap-inline">
|
|
185
|
+
<Skeleton className="h-16 w-160" />
|
|
186
|
+
<Skeleton className="h-12 w-120" />
|
|
187
|
+
</div>
|
|
188
|
+
<Skeleton className="h-96" />
|
|
189
|
+
<Skeleton className="h-160" />
|
|
190
|
+
</Panel>
|
|
198
191
|
</aside>
|
|
199
192
|
);
|
|
200
193
|
}
|
|
@@ -203,30 +196,28 @@ function TriggerEventDetailError({onBack, onRetry}: {onBack: () => void; onRetry
|
|
|
203
196
|
return (
|
|
204
197
|
<aside
|
|
205
198
|
aria-label="Event details"
|
|
206
|
-
className={cn(
|
|
207
|
-
PANEL_CLASS,
|
|
208
|
-
DETAIL_RAIL_CLASS,
|
|
209
|
-
'flex min-h-[320px] flex-col gap-group p-panel-compact',
|
|
210
|
-
)}
|
|
199
|
+
className={cn(DETAIL_RAIL_CLASS, 'flex min-h-[320px] flex-col')}
|
|
211
200
|
>
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
<
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
201
|
+
<Panel className="flex min-h-[320px] flex-col gap-group p-panel-compact">
|
|
202
|
+
<Button
|
|
203
|
+
type="button"
|
|
204
|
+
variant="transparentMuted"
|
|
205
|
+
size="sm"
|
|
206
|
+
iconLeft="arrowLeftLine"
|
|
207
|
+
className="self-start @min-[820px]:hidden"
|
|
208
|
+
onClick={onBack}
|
|
209
|
+
>
|
|
210
|
+
Back to events
|
|
211
|
+
</Button>
|
|
212
|
+
<Callout role="alert" type="error">
|
|
213
|
+
<div className="flex items-center justify-between gap-cluster">
|
|
214
|
+
<Text size="sm">Event detail could not be loaded.</Text>
|
|
215
|
+
<Button type="button" variant="secondary" size="xs" onClick={onRetry}>
|
|
216
|
+
Retry
|
|
217
|
+
</Button>
|
|
218
|
+
</div>
|
|
219
|
+
</Callout>
|
|
220
|
+
</Panel>
|
|
230
221
|
</aside>
|
|
231
222
|
);
|
|
232
223
|
}
|
|
@@ -243,9 +234,13 @@ function EventRuns({
|
|
|
243
234
|
if (event.decisions.length === 0) {
|
|
244
235
|
if (event.outcome === 'discarded') {
|
|
245
236
|
return (
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
237
|
+
<Panel>
|
|
238
|
+
<PanelBody className="p-panel-compact">
|
|
239
|
+
<Text size="sm" className="text-foreground-neutral-muted">
|
|
240
|
+
No workflows are subscribed to this event.
|
|
241
|
+
</Text>
|
|
242
|
+
</PanelBody>
|
|
243
|
+
</Panel>
|
|
249
244
|
);
|
|
250
245
|
}
|
|
251
246
|
return null;
|
|
@@ -301,23 +296,25 @@ function EventRunsList({
|
|
|
301
296
|
event: TriggerEventDetailModel;
|
|
302
297
|
}) {
|
|
303
298
|
return (
|
|
304
|
-
<
|
|
305
|
-
<
|
|
306
|
-
Matched workflows
|
|
307
|
-
</
|
|
308
|
-
<
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
299
|
+
<Panel>
|
|
300
|
+
<PanelHeader>
|
|
301
|
+
<PanelTitle>Matched workflows</PanelTitle>
|
|
302
|
+
</PanelHeader>
|
|
303
|
+
<PanelBody>
|
|
304
|
+
<ul>
|
|
305
|
+
{event.decisions.map((decision) => (
|
|
306
|
+
<DecisionRow
|
|
307
|
+
key={decision.id}
|
|
308
|
+
workspaceSlug={workspaceSlug}
|
|
309
|
+
projectId={decision.projectId ?? undefined}
|
|
310
|
+
projectSlug={decision.projectId ? projectSlugs.get(decision.projectId) : undefined}
|
|
311
|
+
projectDetailLookupEnabled={projectDetailLookupEnabled}
|
|
312
|
+
decision={decision}
|
|
313
|
+
/>
|
|
314
|
+
))}
|
|
315
|
+
</ul>
|
|
316
|
+
</PanelBody>
|
|
317
|
+
</Panel>
|
|
321
318
|
);
|
|
322
319
|
}
|
|
323
320
|
|
|
@@ -343,25 +340,27 @@ function DecisionRow({
|
|
|
343
340
|
|
|
344
341
|
if (decision.decision !== 'triggered' || !decision.runId || !decision.runName) {
|
|
345
342
|
return (
|
|
346
|
-
<li className="
|
|
347
|
-
<
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
<
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
{decision.reason ? (
|
|
357
|
-
<Text size="xs" className="text-foreground-highlight-error">
|
|
358
|
-
{decision.reason}
|
|
359
|
-
</Text>
|
|
360
|
-
) : (
|
|
361
|
-
<Text size="xs" className="text-foreground-neutral-disabled">
|
|
362
|
-
No run created
|
|
343
|
+
<li className="border-b border-border-neutral-base px-row py-row last:border-b-0">
|
|
344
|
+
<div className="flex min-w-0 items-start gap-inline">
|
|
345
|
+
<Icon
|
|
346
|
+
name="cornerDownRightLine"
|
|
347
|
+
className="size-14 shrink-0 text-foreground-neutral-disabled"
|
|
348
|
+
aria-hidden="true"
|
|
349
|
+
/>
|
|
350
|
+
<div className="flex min-w-0 flex-col gap-tight">
|
|
351
|
+
<Text size="sm" className="min-w-0 truncate text-foreground-neutral-base">
|
|
352
|
+
{decision.subscriptionName}
|
|
363
353
|
</Text>
|
|
364
|
-
|
|
354
|
+
{decision.reason ? (
|
|
355
|
+
<Text size="xs" className="text-foreground-highlight-error">
|
|
356
|
+
{decision.reason}
|
|
357
|
+
</Text>
|
|
358
|
+
) : (
|
|
359
|
+
<Text size="xs" className="text-foreground-neutral-disabled">
|
|
360
|
+
No run created
|
|
361
|
+
</Text>
|
|
362
|
+
)}
|
|
363
|
+
</div>
|
|
365
364
|
</div>
|
|
366
365
|
</li>
|
|
367
366
|
);
|
|
@@ -385,10 +384,10 @@ function DecisionRow({
|
|
|
385
384
|
</>
|
|
386
385
|
);
|
|
387
386
|
const rowClassName =
|
|
388
|
-
'flex min-w-0 items-start gap-inline
|
|
387
|
+
'flex min-w-0 items-start gap-inline px-row py-row hover:bg-background-neutral-hover focus-visible:outline-none focus-visible:shadow-button-neutral-focus';
|
|
389
388
|
|
|
390
389
|
return (
|
|
391
|
-
<li>
|
|
390
|
+
<li className="border-b border-border-neutral-base last:border-b-0">
|
|
392
391
|
{workspaceSlug && resolvedProjectSlug ? (
|
|
393
392
|
<Link
|
|
394
393
|
to="/w/$workspaceSlug/p/$projectSlug/runs/$workflowRunId"
|
|
@@ -412,34 +411,38 @@ function EventPayload({payload}: {payload: string}) {
|
|
|
412
411
|
const data = [{language: 'json', filename: 'payload.json', code: payload}];
|
|
413
412
|
|
|
414
413
|
return (
|
|
415
|
-
<
|
|
416
|
-
<
|
|
417
|
-
Payload
|
|
418
|
-
</
|
|
419
|
-
<
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
<
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
414
|
+
<Panel>
|
|
415
|
+
<PanelHeader>
|
|
416
|
+
<PanelTitle>Payload</PanelTitle>
|
|
417
|
+
</PanelHeader>
|
|
418
|
+
<PanelBody className="p-0">
|
|
419
|
+
<CodeBlock
|
|
420
|
+
data={data}
|
|
421
|
+
className="flex h-auto flex-col overflow-visible rounded-none bg-background-contrast-base shadow-none"
|
|
422
|
+
>
|
|
423
|
+
<CodeBlockHeader className="sticky top-0 z-10 shrink-0 border-b border-border-contrast-base bg-background-contrast-base p-tight">
|
|
424
|
+
<CodeBlockFiles>
|
|
425
|
+
{(item) => (
|
|
426
|
+
<CodeBlockFilename value={item.filename}>{item.filename}</CodeBlockFilename>
|
|
427
|
+
)}
|
|
428
|
+
</CodeBlockFiles>
|
|
429
|
+
<CodeBlockCopyButton />
|
|
430
|
+
</CodeBlockHeader>
|
|
431
|
+
<CodeBlockBody className="min-h-0 scrollbar">
|
|
432
|
+
{(item) => (
|
|
433
|
+
<CodeBlockItem
|
|
434
|
+
value={item.filename}
|
|
435
|
+
lineNumbers={false}
|
|
436
|
+
className="px-0 pb-0 [&>div]:rounded-none [&>div]:border-0 [&>div]:bg-background-contrast-base [&_code]:!text-foreground-neutral-on-color"
|
|
437
|
+
>
|
|
438
|
+
<CodeBlockContent language="json" syntaxHighlighting={false}>
|
|
439
|
+
{item.code}
|
|
440
|
+
</CodeBlockContent>
|
|
441
|
+
</CodeBlockItem>
|
|
442
|
+
)}
|
|
443
|
+
</CodeBlockBody>
|
|
444
|
+
</CodeBlock>
|
|
445
|
+
</PanelBody>
|
|
446
|
+
</Panel>
|
|
444
447
|
);
|
|
445
448
|
}
|
|
@@ -13,6 +13,7 @@ import {EventsPage} from './events-page.js';
|
|
|
13
13
|
|
|
14
14
|
const WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';
|
|
15
15
|
const EVENT_ID = '22222222-2222-4222-8222-222222222222';
|
|
16
|
+
const EVENTS_HEADING = /^Events$/u;
|
|
16
17
|
|
|
17
18
|
const useTriggerEventsInfiniteQueryMock = vi.mocked(useTriggerEventsInfiniteQuery);
|
|
18
19
|
const useTriggerEventFacetsQueryMock = vi.mocked(useTriggerEventFacetsQuery);
|
|
@@ -89,6 +90,12 @@ describe('EventsPage', () => {
|
|
|
89
90
|
);
|
|
90
91
|
fireEvent.click(await screen.findByRole('button', {name: 'Open details for github · push'}));
|
|
91
92
|
expect(screen.getByRole('button', {name: 'Back to events'})).toBeInTheDocument();
|
|
93
|
+
expect(screen.queryByRole('heading', {name: EVENTS_HEADING})).not.toBeInTheDocument();
|
|
94
|
+
expect(
|
|
95
|
+
screen.queryByText(
|
|
96
|
+
'A workspace-wide audit log of trigger events received from integrations, schedules, and manual trigger calls.',
|
|
97
|
+
),
|
|
98
|
+
).not.toBeInTheDocument();
|
|
92
99
|
|
|
93
100
|
useTriggerEventsInfiniteQueryMock.mockReturnValue(makeListQuery([]));
|
|
94
101
|
rerender(<EventsPage workspaceId={WORKSPACE_ID} filters={{}} onFiltersChange={vi.fn()} />);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {RelativeTimeProvider} from '@shipfox/react-ui/relative-time';
|
|
2
|
-
import {Header, Text} from '@shipfox/react-ui/typography';
|
|
3
2
|
import {cn} from '@shipfox/react-ui/utils';
|
|
4
3
|
import {useEffect, useMemo, useState} from 'react';
|
|
5
4
|
import {EventsList} from '#components/events-list.js';
|
|
@@ -43,20 +42,7 @@ export function EventsPage({
|
|
|
43
42
|
|
|
44
43
|
return (
|
|
45
44
|
<RelativeTimeProvider>
|
|
46
|
-
<section
|
|
47
|
-
className="@container flex min-w-0 flex-col gap-group"
|
|
48
|
-
aria-labelledby="trigger-events-heading"
|
|
49
|
-
>
|
|
50
|
-
<div className="flex flex-col gap-tight">
|
|
51
|
-
<Header id="trigger-events-heading" variant="h3">
|
|
52
|
-
Events
|
|
53
|
-
</Header>
|
|
54
|
-
<Text size="sm" className="text-foreground-neutral-muted">
|
|
55
|
-
A workspace-wide audit log of trigger events received from integrations, schedules, and
|
|
56
|
-
manual trigger calls.
|
|
57
|
-
</Text>
|
|
58
|
-
</div>
|
|
59
|
-
|
|
45
|
+
<section className="@container flex min-w-0 flex-col gap-group" aria-label="Events">
|
|
60
46
|
<div className="grid min-h-0 items-start gap-group @min-[820px]:grid-cols-[minmax(0,1fr)_minmax(360px,420px)]">
|
|
61
47
|
<div className={cn('min-w-0', selectedEventId && '@max-[820px]:hidden')}>
|
|
62
48
|
<EventsList
|