Skip to content

Commit 19dfdd7

Browse files
authored
feat(schedule-actions): add delete confirmation action (#1428)
## Summary Adds the Delete schedule action to the schedule-actions menu as a confirmation-only modal with a warning banner. On success, the UI navigates to the schedules list and invalidates `listSchedules`. Rebased onto `master` after #1427 merged; this PR now contains only the delete UI slice (4 commits).
1 parent bd029d5 commit 19dfdd7

12 files changed

Lines changed: 102 additions & 20 deletions

File tree

src/config/dynamic/resolvers/__tests__/schedule-actions-enabled.node.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ describe(scheduleActionsEnabled.name, () => {
2626
expect(result).toEqual({
2727
pause: 'ENABLED',
2828
resume: 'ENABLED',
29+
delete: 'ENABLED',
2930
});
3031
});
3132

@@ -40,6 +41,7 @@ describe(scheduleActionsEnabled.name, () => {
4041
expect(result).toEqual({
4142
pause: 'DISABLED_UNAUTHORIZED',
4243
resume: 'DISABLED_UNAUTHORIZED',
44+
delete: 'DISABLED_UNAUTHORIZED',
4345
});
4446
});
4547

@@ -54,6 +56,7 @@ describe(scheduleActionsEnabled.name, () => {
5456
expect(result).toEqual({
5557
pause: 'DISABLED_DEFAULT',
5658
resume: 'DISABLED_DEFAULT',
59+
delete: 'DISABLED_DEFAULT',
5760
});
5861
});
5962
});

src/config/dynamic/resolvers/schedule-actions-enabled.constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@ export const AUTHORIZED_SCHEDULE_ACTIONS_CONFIG: ScheduleActionsEnabledConfig =
44
{
55
pause: 'ENABLED',
66
resume: 'ENABLED',
7+
delete: 'ENABLED',
78
};
89

910
export const UNAUTHORIZED_SCHEDULE_ACTIONS_CONFIG: ScheduleActionsEnabledConfig =
1011
{
1112
pause: 'DISABLED_UNAUTHORIZED',
1213
resume: 'DISABLED_UNAUTHORIZED',
14+
delete: 'DISABLED_UNAUTHORIZED',
1315
};
1416

1517
export const DEFAULT_DISABLED_SCHEDULE_ACTIONS_CONFIG: ScheduleActionsEnabledConfig =
1618
{
1719
pause: 'DISABLED_DEFAULT',
1820
resume: 'DISABLED_DEFAULT',
21+
delete: 'DISABLED_DEFAULT',
1922
};

src/config/dynamic/resolvers/schedule-actions-enabled.types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type SCHEDULE_ACTIONS_DISABLED_VALUES_CONFIG from './schedule-actions-disabled-values.config';
22

3-
export type ScheduleActionID = 'pause' | 'resume';
3+
export type ScheduleActionID = 'pause' | 'resume' | 'delete';
44

55
export type ScheduleActionDisabledValue =
66
(typeof SCHEDULE_ACTIONS_DISABLED_VALUES_CONFIG)[number];

src/config/dynamic/resolvers/schemas/resolver-schemas.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ const resolverSchemas: ResolverSchemas = {
7474
returnType: z.object({
7575
pause: scheduleActionsEnabledValueSchema,
7676
resume: scheduleActionsEnabledValueSchema,
77+
delete: scheduleActionsEnabledValueSchema,
7778
}),
7879
},
7980
CRON_LIST_ENABLED: {

src/utils/config/__fixtures__/resolved-config-values.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const mockResolvedConfigValues: LoadedConfigResolvedValues = {
5353
SCHEDULE_ACTIONS_ENABLED: {
5454
pause: 'ENABLED',
5555
resume: 'ENABLED',
56+
delete: 'ENABLED',
5657
},
5758
SCHEDULES_ENABLED: false,
5859
WORKFLOWS_LIST_ENABLED: false,

src/views/schedule-actions/__fixtures__/schedule-actions-config.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import {
2+
MdDeleteOutline,
23
MdOutlineWarningAmber,
34
MdPauseCircleOutline,
45
MdPlayCircleOutline,
56
} from 'react-icons/md';
67
import { z } from 'zod';
78

89
import { ScheduleCatchUpPolicy } from '@/__generated__/proto-ts/uber/cadence/api/v1/ScheduleCatchUpPolicy';
10+
import { type DeleteScheduleResponse } from '@/route-handlers/delete-schedule/delete-schedule.types';
911
import { type PauseScheduleResponse } from '@/route-handlers/pause-schedule/pause-schedule.types';
1012
import { type UnpauseScheduleResponse } from '@/route-handlers/unpause-schedule/unpause-schedule.types';
1113

@@ -95,7 +97,32 @@ export const mockResumeActionConfig: ScheduleAction<
9597
renderSuccessMessage: () => 'Mock resume notification',
9698
};
9799

100+
export const mockDeleteActionConfig: ScheduleAction<DeleteScheduleResponse> = {
101+
id: 'delete',
102+
label: 'Delete',
103+
subtitle: 'Mock delete a schedule',
104+
modal: {
105+
banner: {
106+
kind: 'warning',
107+
icon: MdOutlineWarningAmber,
108+
render: () =>
109+
'Deletes the schedule permanently. In-progress workflow runs are not affected.',
110+
},
111+
withForm: false,
112+
},
113+
icon: MdDeleteOutline,
114+
getRunnableStatus: () => 'RUNNABLE',
115+
apiRoute: (params) =>
116+
`/api/domains/${params.domain}/${params.cluster}/schedules/${params.scheduleId}`,
117+
httpMethod: 'DELETE',
118+
renderSuccessMessage: () => 'Schedule deleted.',
119+
onSuccess: ({ router, params }) => {
120+
router.push(`/domains/${params.domain}/${params.cluster}/schedules`);
121+
},
122+
};
123+
98124
export const mockScheduleActionsConfig = [
99125
mockPauseActionConfig,
100126
mockResumeActionConfig,
127+
mockDeleteActionConfig,
101128
] as const;

src/views/schedule-actions/__tests__/schedule-actions.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ function setup({
152152
{
153153
pause: 'ENABLED',
154154
resume: 'ENABLED',
155+
delete: 'ENABLED',
155156
},
156157
{ status: 200 }
157158
);

src/views/schedule-actions/config/schedule-actions.config.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import {
2+
MdDeleteOutline,
23
MdOutlineWarningAmber,
34
MdPauseCircleOutline,
45
MdPlayCircleOutline,
56
} from 'react-icons/md';
67

8+
import { type DeleteScheduleResponse } from '@/route-handlers/delete-schedule/delete-schedule.types';
79
import { type PauseScheduleResponse } from '@/route-handlers/pause-schedule/pause-schedule.types';
810
import { type UnpauseScheduleResponse } from '@/route-handlers/unpause-schedule/unpause-schedule.types';
911

@@ -74,9 +76,42 @@ const resumeScheduleActionConfig: ScheduleAction<
7476
renderSuccessMessage: () => 'Schedule has been resumed.',
7577
};
7678

79+
const deleteScheduleActionConfig: ScheduleAction<DeleteScheduleResponse> = {
80+
id: 'delete',
81+
label: 'Delete',
82+
subtitle: 'Delete this schedule permanently',
83+
modal: {
84+
banner: {
85+
kind: 'warning',
86+
icon: MdOutlineWarningAmber,
87+
render: () =>
88+
'Deletes the schedule permanently. In-progress workflow runs are not affected.',
89+
},
90+
withForm: false,
91+
},
92+
icon: MdDeleteOutline,
93+
getRunnableStatus: () => 'RUNNABLE',
94+
apiRoute: (params) =>
95+
`/api/domains/${encodeURIComponent(params.domain)}/${encodeURIComponent(params.cluster)}/schedules/${encodeURIComponent(params.scheduleId)}`,
96+
httpMethod: 'DELETE',
97+
renderSuccessMessage: () => 'Schedule deleted.',
98+
onSuccess: ({ queryClient, params, router }) => {
99+
router.push(
100+
`/domains/${encodeURIComponent(params.domain)}/${encodeURIComponent(params.cluster)}/schedules`
101+
);
102+
queryClient.invalidateQueries({
103+
queryKey: [
104+
'listSchedules',
105+
{ domain: params.domain, cluster: params.cluster },
106+
],
107+
});
108+
},
109+
};
110+
77111
const scheduleActionsConfig = [
78112
pauseScheduleActionConfig,
79113
resumeScheduleActionConfig,
114+
deleteScheduleActionConfig,
80115
] as const;
81116

82117
/** Discriminated union of configured actions; use at menu/selection boundaries. */

src/views/schedule-actions/schedule-actions-menu/__tests__/schedule-actions-menu.test.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ describe(ScheduleActionsMenu.name, () => {
4141
});
4242

4343
const menuButtons = screen.getAllByRole('button');
44-
expect(menuButtons).toHaveLength(2);
44+
expect(menuButtons).toHaveLength(3);
4545

4646
expect(within(menuButtons[0]).getByText('Mock pause')).toBeInTheDocument();
4747
expect(
@@ -54,13 +54,20 @@ describe(ScheduleActionsMenu.name, () => {
5454
within(menuButtons[1]).getByText('Mock resume a schedule')
5555
).toBeInTheDocument();
5656
expect(menuButtons[1]).not.toBeDisabled();
57+
58+
expect(within(menuButtons[2]).getByText('Delete')).toBeInTheDocument();
59+
expect(
60+
within(menuButtons[2]).getByText('Mock delete a schedule')
61+
).toBeInTheDocument();
62+
expect(menuButtons[2]).not.toBeDisabled();
5763
});
5864

5965
it('disables menu items and shows tooltip if they are disabled from config', async () => {
6066
const { user } = setup({
6167
actionsEnabledConfig: {
6268
pause: 'DISABLED_DEFAULT',
6369
resume: 'DISABLED_DEFAULT',
70+
delete: 'DISABLED_DEFAULT',
6471
},
6572
});
6673

src/views/schedule-actions/schedule-actions-modal-content/__tests__/schedule-actions-modal-content.test.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { type PauseScheduleResponse } from '@/route-handlers/pause-schedule/paus
77
import { type UnpauseScheduleResponse } from '@/route-handlers/unpause-schedule/unpause-schedule.types';
88

99
import { mockScheduleActionsConfig } from '../../__fixtures__/schedule-actions-config';
10-
import { type ScheduleAction } from '../../schedule-actions.types';
1110
import ScheduleActionsModalContent from '../schedule-actions-modal-content';
1211

1312
const mockScheduleParams = {
@@ -26,10 +25,9 @@ jest.mock('baseui/snackbar', () => ({
2625
}),
2726
}));
2827

29-
const mockPush = jest.fn();
3028
jest.mock('next/navigation', () => ({
3129
...jest.requireActual('next/navigation'),
32-
useRouter: () => ({ push: mockPush }),
30+
useRouter: () => ({ push: jest.fn() }),
3331
}));
3432

3533
describe(ScheduleActionsModalContent.name, () => {
@@ -146,11 +144,9 @@ describe(ScheduleActionsModalContent.name, () => {
146144

147145
function setup({
148146
error,
149-
actionConfig,
150147
schedule = mockDescribeScheduleResponse,
151148
}: {
152149
error?: boolean;
153-
actionConfig?: ScheduleAction<any, any, any>;
154150
schedule?: typeof mockDescribeScheduleResponse;
155151
}) {
156152
const user = userEvent.setup();
@@ -163,7 +159,7 @@ function setup({
163159

164160
render(
165161
<ScheduleActionsModalContent
166-
action={actionConfig ?? mockScheduleActionsConfig[0]}
162+
action={mockScheduleActionsConfig[0]}
167163
params={{ ...mockScheduleParams }}
168164
schedule={schedule}
169165
onCloseModal={mockOnClose}

0 commit comments

Comments
 (0)