Skip to content

Commit

Permalink
[RHOAIENG-3887] Run Schedules - UX cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
jpuzz0 committed Mar 8, 2024
1 parent 44a2bca commit 790d427
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 45 deletions.
45 changes: 27 additions & 18 deletions frontend/src/concepts/pipelines/content/createRun/RunForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { Form, FormGroup, FormSection, Text } from '@patternfly/react-core';
import { Form, FormSection, Text } from '@patternfly/react-core';
import NameDescriptionField from '~/concepts/k8s/NameDescriptionField';
import {
RunFormData,
Expand All @@ -18,6 +18,7 @@ import ExperimentSection from '~/concepts/pipelines/content/createRun/contentSec
import { SupportedArea, useIsAreaAvailable } from '~/concepts/areas';
import PipelineSection from './contentSections/PipelineSection';
import { CreateRunPageSections, runPageSectionTitles } from './const';
import { RunTypeSection } from './contentSections/RunTypeSection';

type RunFormProps = {
data: RunFormData;
Expand All @@ -29,7 +30,6 @@ const RunForm: React.FC<RunFormProps> = ({ data, runType, onValueChange }) => {
const [latestVersion] = useLatestPipelineVersion(data.pipeline?.pipeline_id);
const selectedVersion = data.version || latestVersion;
const paramsRef = React.useRef(data.params);

const isExperimentsAvailable = useIsAreaAvailable(SupportedArea.PIPELINE_EXPERIMENTS).status;

const updateInputParams = React.useCallback(
Expand All @@ -55,17 +55,16 @@ const RunForm: React.FC<RunFormProps> = ({ data, runType, onValueChange }) => {
}, [latestVersion, onValueChange, updateInputParams]);

return (
<Form
maxWidth="500px"
onSubmit={(e) => {
e.preventDefault();
}}
>
<FormSection id="run-section-project-name" title="Project">
<FormGroup label="Project">
<Text>{getProjectDisplayName(data.project)}</Text>
</FormGroup>
<Form onSubmit={(e) => e.preventDefault()} maxWidth="50%">
<RunTypeSection runType={runType} />

<FormSection
id={CreateRunPageSections.PROJECT}
title={runPageSectionTitles[CreateRunPageSections.PROJECT]}
>
<Text>{getProjectDisplayName(data.project)}</Text>
</FormSection>

<FormSection
id={CreateRunPageSections.NAME_DESC}
aria-label={runPageSectionTitles[CreateRunPageSections.NAME_DESC]}
Expand All @@ -77,19 +76,22 @@ const RunForm: React.FC<RunFormProps> = ({ data, runType, onValueChange }) => {
setData={(nameDesc) => onValueChange('nameDesc', nameDesc)}
/>
</FormSection>

{isExperimentsAvailable && (
<ExperimentSection
value={data.experiment}
onChange={(experiment) => onValueChange('experiment', experiment)}
/>
)}

<PipelineSection
value={data.pipeline}
onChange={async (pipeline) => {
onValueChange('pipeline', pipeline);
onValueChange('version', undefined);
}}
/>

<PipelineVersionSection
selectedPipeline={data.pipeline}
value={selectedVersion}
Expand All @@ -98,14 +100,21 @@ const RunForm: React.FC<RunFormProps> = ({ data, runType, onValueChange }) => {
updateInputParams(version);
}}
/>

{runType === PipelineRunType.Scheduled && (
<RunTypeSectionScheduled
data={(data.runType as ScheduledRunType).data}
onChange={(scheduleData) =>
onValueChange('runType', { type: RunTypeOption.SCHEDULED, data: scheduleData })
}
/>
<FormSection
id={CreateRunPageSections.SCHEDULE_SETTINGS}
title={runPageSectionTitles[CreateRunPageSections.SCHEDULE_SETTINGS]}
>
<RunTypeSectionScheduled
data={(data.runType as ScheduledRunType).data}
onChange={(scheduleData) =>
onValueChange('runType', { type: RunTypeOption.SCHEDULED, data: scheduleData })
}
/>
</FormSection>
)}

<ParamsSection
runParams={data.params}
versionId={selectedVersion?.pipeline_version_id}
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/concepts/pipelines/content/createRun/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,23 @@ export const DEFAULT_TIME = '12:00 AM';
export const RUN_OPTION_LABEL_SIZE = 100;

export enum CreateRunPageSections {
RUN_TYPE = 'run-section-run-type',
PROJECT = 'run-section-project',
NAME_DESC = 'run-section-name-desc',
EXPERIMENT = 'run-section-experiment',
PIPELINE = 'run-section-pipeline',
PIPELINE_VERSION = 'run-section-pipeline-version',
SCHEDULE_SETTINGS = 'run-section-schedule-settings',
PARAMS = 'run-section-params',
}

export const runPageSectionTitles: Record<CreateRunPageSections, string> = {
[CreateRunPageSections.RUN_TYPE]: 'Run type',
[CreateRunPageSections.PROJECT]: 'Project',
[CreateRunPageSections.NAME_DESC]: 'Name and description',
[CreateRunPageSections.EXPERIMENT]: 'Experiment',
[CreateRunPageSections.PIPELINE]: 'Pipeline',
[CreateRunPageSections.PIPELINE_VERSION]: 'Pipeline version',
[CreateRunPageSections.PARAMS]: 'Pipeline input parameters',
[CreateRunPageSections.SCHEDULE_SETTINGS]: 'Schedule settings',
[CreateRunPageSections.PARAMS]: 'Parameters',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';

import { Alert, AlertActionCloseButton, Button, FormSection } from '@patternfly/react-core';
import { useNavigate, useParams } from 'react-router-dom';

import { PipelineRunTabTitle, PipelineRunType } from '~/pages/pipelines/global/runs';
import {
CreateRunPageSections,
runPageSectionTitles,
} from '~/concepts/pipelines/content/createRun/const';
import { PipelineRunSearchParam } from '~/concepts/pipelines/content/types';

interface RunTypeSectionProps {
runType: PipelineRunType;
}

export const RunTypeSection: React.FC<RunTypeSectionProps> = ({ runType }) => {
const navigate = useNavigate();
const { namespace } = useParams();
const [isAlertOpen, setIsAlertOpen] = React.useState(true);

let runTypeValue = 'Run once immediately after creation';
let alertProps = {
title: 'Go to Schedules to create schedules that execute recurring runs',
label: `Go to ${PipelineRunTabTitle.Schedules}`,
navSearch: `?${PipelineRunSearchParam.RunType}=${PipelineRunType.Scheduled}`,
};

if (runType === PipelineRunType.Scheduled) {
runTypeValue = 'Schedule recurring run';
alertProps = {
title: 'Go to Active runs to create a run that executes once immediately after creation.',
label: `Go to ${PipelineRunTabTitle.Active}`,
navSearch: `?${PipelineRunSearchParam.RunType}=${PipelineRunType.Active}`,
};
}

return (
<FormSection
id={CreateRunPageSections.RUN_TYPE}
title={runPageSectionTitles[CreateRunPageSections.RUN_TYPE]}
>
{runTypeValue}

{isAlertOpen && (
<Alert
isInline
variant="info"
title={alertProps.title}
actionLinks={
<Button
isInline
variant="link"
onClick={() =>
navigate({
pathname: `/pipelineRuns/${namespace}`,
search: alertProps.navSearch,
})
}
>
{alertProps.label}
</Button>
}
actionClose={<AlertActionCloseButton onClose={() => setIsAlertOpen(false)} />}
/>
)}
</FormSection>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const PipelineDetailsActions: React.FC<PipelineDetailsActionsProps> = ({
<DropdownItem key="upload-version" onClick={() => setIsVersionImportModalOpen(true)}>
Upload new version
</DropdownItem>,
<DropdownSeparator key="separator-1" />,
<DropdownSeparator key="separator-create" />,
<DropdownItem
key="create-run"
onClick={() =>
Expand All @@ -56,6 +56,23 @@ const PipelineDetailsActions: React.FC<PipelineDetailsActionsProps> = ({
>
Create run
</DropdownItem>,
<DropdownItem
key="schedule-run"
onClick={() =>
navigate(
{
pathname: `/pipelineRuns/${namespace}/pipelineRun/create`,
search: `?${PipelineRunSearchParam.RunType}=${PipelineRunType.Scheduled}`,
},
{
state: { lastPipeline: pipeline, lastVersion: pipelineVersion },
},
)
}
>
Schedule run
</DropdownItem>,
<DropdownSeparator key="separator-view" />,
<DropdownItem
key="view-runs"
onClick={() =>
Expand All @@ -72,7 +89,23 @@ const PipelineDetailsActions: React.FC<PipelineDetailsActionsProps> = ({
>
View runs
</DropdownItem>,
<DropdownSeparator key="separator-2" />,
<DropdownItem
key="view-schedules"
onClick={() =>
navigate(
{
pathname: `/pipelineRuns/${namespace}`,
search: `?${PipelineRunSearchParam.RunType}=${PipelineRunType.Scheduled}`,
},
{
state: { lastVersion: pipelineVersion },
},
)
}
>
View schedules
</DropdownItem>,
<DropdownSeparator key="separator-delete" />,
<DropdownItem key="delete-pipeline-version" onClick={() => onDelete()}>
Delete pipeline version
</DropdownItem>,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import { BrowserRouter } from 'react-router-dom';

import { renderHook } from '~/__tests__/unit/testUtils/hooks';
import { useSetVersionFilter } from '~/concepts/pipelines/content/tables/useSetVersionFilter';
import { PipelineVersionKFv2 } from '~/concepts/pipelines/kfTypes';

let mockUseLocationState: { lastVersion: Partial<PipelineVersionKFv2> } | undefined;
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: jest.fn(() => ({
state: mockUseLocationState,
})),
}));

describe('useSetVersionFilter', () => {
const onFilterUpdate = jest.fn();

it('does not call onFilterUpdate when there is no router state version', async () => {
renderHook(() => useSetVersionFilter(onFilterUpdate), {
wrapper: ({ children }) => <BrowserRouter>{children}</BrowserRouter>,
});

expect(onFilterUpdate).not.toHaveBeenCalled();
});

it('calls onFilterUpdate when router state has a version', () => {
mockUseLocationState = {
// eslint-disable-next-line camelcase
lastVersion: { display_name: 'Some version', pipeline_version_id: 'some-id' },
};
renderHook(() => useSetVersionFilter(onFilterUpdate), {
wrapper: ({ children }) => <BrowserRouter>{children}</BrowserRouter>,
});

expect(onFilterUpdate).toBeCalledTimes(1);
expect(onFilterUpdate).toHaveBeenCalledWith('pipeline_version', {
label: 'Some version',
value: 'some-id',
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import PipelineVersionUploadModal from '~/concepts/pipelines/content/import/Pipe
import PipelinesTableRowTime from '~/concepts/pipelines/content/tables/PipelinesTableRowTime';
import usePipelineTableRowData from '~/concepts/pipelines/content/tables/pipeline/usePipelineTableRowData';
import { PipelineAndVersionContext } from '~/concepts/pipelines/content/PipelineAndVersionContext';
import { PipelineRunSearchParam } from '~/concepts/pipelines/content/types';
import { PipelineRunType } from '~/pages/pipelines/global/runs';

const DISABLE_TOOLTIP =
'All child pipeline versions must be deleted before deleting the parent pipeline';
Expand Down Expand Up @@ -111,6 +113,20 @@ const PipelinesTableRow: React.FC<PipelinesTableRowProps> = ({
});
},
},
{
title: 'Schedule run',
onClick: () => {
navigate(
{
pathname: `/pipelines/${namespace}/pipelineRun/create`,
search: `?${PipelineRunSearchParam.RunType}=${PipelineRunType.Scheduled}`,
},
{
state: { lastPipeline: pipeline },
},
);
},
},
{
isSeparator: true,
},
Expand Down
Loading

0 comments on commit 790d427

Please sign in to comment.