mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-23 00:16:25 +01:00
refactor: project actions (#6203)
https://linear.app/unleash/issue/2-1938/refactor-project-actions Refactors project actions to not include the project in the payload. Includes other misc scouting.
This commit is contained in:
parent
c224d7dc4c
commit
9511e64027
@ -99,8 +99,6 @@ export const useIncomingWebhooksForm = (incomingWebhook?: IIncomingWebhook) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO call backend to check if token name is unique
|
|
||||||
|
|
||||||
clearError(ErrorField.TOKEN_NAME);
|
clearError(ErrorField.TOKEN_NAME);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -26,7 +26,7 @@ export const ProjectActionsActionsCell = ({
|
|||||||
action,
|
action,
|
||||||
onCreateAction,
|
onCreateAction,
|
||||||
}: IProjectActionsActionsCellProps) => {
|
}: IProjectActionsActionsCellProps) => {
|
||||||
const { id: actionSetId, actions } = action;
|
const { actions } = action;
|
||||||
|
|
||||||
if (actions.length === 0) {
|
if (actions.length === 0) {
|
||||||
if (!onCreateAction) return <TextCell>0 actions</TextCell>;
|
if (!onCreateAction) return <TextCell>0 actions</TextCell>;
|
||||||
@ -38,25 +38,21 @@ export const ProjectActionsActionsCell = ({
|
|||||||
<TooltipLink
|
<TooltipLink
|
||||||
tooltip={
|
tooltip={
|
||||||
<StyledActionItems>
|
<StyledActionItems>
|
||||||
{actions.map(
|
{actions.map(({ id, action, executionParams }) => (
|
||||||
({ action, executionParams, sortOrder }) => (
|
<div key={id}>
|
||||||
<div
|
<strong>{action}</strong>
|
||||||
key={`${actionSetId}/${sortOrder}_${action}`}
|
<StyledParameterList>
|
||||||
>
|
{Object.entries(executionParams).map(
|
||||||
<strong>{action}</strong>
|
([param, value]) => (
|
||||||
<StyledParameterList>
|
<li key={param}>
|
||||||
{Object.entries(executionParams).map(
|
<strong>{param}</strong>:{' '}
|
||||||
([param, value]) => (
|
{value}
|
||||||
<li key={param}>
|
</li>
|
||||||
<strong>{param}</strong>:{' '}
|
),
|
||||||
{value}
|
)}
|
||||||
</li>
|
</StyledParameterList>
|
||||||
),
|
</div>
|
||||||
)}
|
))}
|
||||||
</StyledParameterList>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</StyledActionItems>
|
</StyledActionItems>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
@ -9,7 +9,7 @@ export const StyledInnerBox = styled(Box)(({ theme }) => ({
|
|||||||
borderRadius: `${theme.shape.borderRadiusMedium}px`,
|
borderRadius: `${theme.shape.borderRadiusMedium}px`,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const InnerBoxHeader = styled('div')(({ theme }) => ({
|
export const StyledInnerBoxHeader = styled('div')(({ theme }) => ({
|
||||||
marginLeft: 'auto',
|
marginLeft: 'auto',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
[theme.breakpoints.down('sm')]: {
|
[theme.breakpoints.down('sm')]: {
|
||||||
@ -18,31 +18,32 @@ export const InnerBoxHeader = styled('div')(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// row for inner containers
|
// row for inner containers
|
||||||
export const Row = styled('div')({
|
export const StyledRow = styled('div')({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Col = styled('div')({
|
export const StyledCol = styled('div')({
|
||||||
flex: 1,
|
flex: 1,
|
||||||
margin: '0 4px',
|
margin: '0 4px',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const StyledBoxContent = styled('div')(({ theme }) => ({
|
||||||
|
padding: theme.spacing(0.75, 1),
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
fontSize: theme.fontSizes.smallerBody,
|
||||||
|
backgroundColor: theme.palette.seen.primary,
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
position: 'absolute',
|
||||||
|
zIndex: theme.zIndex.fab,
|
||||||
|
top: '50%',
|
||||||
|
left: theme.spacing(2),
|
||||||
|
transform: 'translateY(-50%)',
|
||||||
|
lineHeight: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
export const BoxSeparator: React.FC = ({ children }) => {
|
export const BoxSeparator: React.FC = ({ children }) => {
|
||||||
const StyledBoxContent = styled('div')(({ theme }) => ({
|
|
||||||
padding: theme.spacing(0.75, 1),
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
fontSize: theme.fontSizes.smallerBody,
|
|
||||||
backgroundColor: theme.palette.seen.primary,
|
|
||||||
borderRadius: theme.shape.borderRadius,
|
|
||||||
position: 'absolute',
|
|
||||||
zIndex: theme.zIndex.fab,
|
|
||||||
top: '50%',
|
|
||||||
left: theme.spacing(2),
|
|
||||||
transform: 'translateY(-50%)',
|
|
||||||
lineHeight: 1,
|
|
||||||
}));
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -1,48 +1,35 @@
|
|||||||
import { IconButton, Tooltip } from '@mui/material';
|
import { IconButton, Tooltip } from '@mui/material';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { IAction } from 'interfaces/action';
|
|
||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
|
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
|
||||||
import { Delete } from '@mui/icons-material';
|
import { Delete } from '@mui/icons-material';
|
||||||
import { useProjectEnvironments } from 'hooks/api/getters/useProjectEnvironments/useProjectEnvironments';
|
import { useProjectEnvironments } from 'hooks/api/getters/useProjectEnvironments/useProjectEnvironments';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import mapValues from 'lodash.mapvalues';
|
|
||||||
import { useFeatureSearch } from 'hooks/api/getters/useFeatureSearch/useFeatureSearch';
|
import { useFeatureSearch } from 'hooks/api/getters/useFeatureSearch/useFeatureSearch';
|
||||||
import {
|
import {
|
||||||
BoxSeparator,
|
BoxSeparator,
|
||||||
Col,
|
StyledCol,
|
||||||
InnerBoxHeader,
|
StyledInnerBoxHeader,
|
||||||
Row,
|
StyledRow,
|
||||||
StyledInnerBox,
|
StyledInnerBox,
|
||||||
} from './InnerContainerBox';
|
} from './InnerContainerBox';
|
||||||
|
import { ActionsActionState } from './useProjectActionsForm';
|
||||||
|
|
||||||
export type UIAction = Omit<IAction, 'id' | 'createdAt' | 'createdByUserId'> & {
|
export const ProjectActionsActionItem = ({
|
||||||
id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ActionItem = ({
|
|
||||||
action,
|
action,
|
||||||
index,
|
index,
|
||||||
stateChanged,
|
stateChanged,
|
||||||
onDelete,
|
onDelete,
|
||||||
}: {
|
}: {
|
||||||
action: UIAction;
|
action: ActionsActionState;
|
||||||
index: number;
|
index: number;
|
||||||
stateChanged: (action: UIAction) => void;
|
stateChanged: (action: ActionsActionState) => void;
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { id, action: actionName } = action;
|
const { action: actionName } = action;
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
const environments = useProjectEnvironments(projectId);
|
const environments = useProjectEnvironments(projectId);
|
||||||
const { features } = useFeatureSearch(
|
const { features } = useFeatureSearch({ project: `IS:${projectId}` });
|
||||||
mapValues(
|
|
||||||
{
|
|
||||||
project: `IS:${projectId}`,
|
|
||||||
},
|
|
||||||
(value) => (value ? `${value}` : undefined),
|
|
||||||
),
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
@ -50,18 +37,18 @@ export const ActionItem = ({
|
|||||||
show={<BoxSeparator>THEN</BoxSeparator>}
|
show={<BoxSeparator>THEN</BoxSeparator>}
|
||||||
/>
|
/>
|
||||||
<StyledInnerBox>
|
<StyledInnerBox>
|
||||||
<Row>
|
<StyledRow>
|
||||||
<span>Action {index + 1}</span>
|
<span>Action {index + 1}</span>
|
||||||
<InnerBoxHeader>
|
<StyledInnerBoxHeader>
|
||||||
<Tooltip title='Delete action' arrow>
|
<Tooltip title='Delete action' arrow>
|
||||||
<IconButton onClick={onDelete}>
|
<IconButton onClick={onDelete}>
|
||||||
<Delete />
|
<Delete />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</InnerBoxHeader>
|
</StyledInnerBoxHeader>
|
||||||
</Row>
|
</StyledRow>
|
||||||
<Row>
|
<StyledRow>
|
||||||
<Col>
|
<StyledCol>
|
||||||
<GeneralSelect
|
<GeneralSelect
|
||||||
label='Action'
|
label='Action'
|
||||||
name='action'
|
name='action'
|
||||||
@ -84,8 +71,8 @@ export const ActionItem = ({
|
|||||||
}
|
}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
</Col>
|
</StyledCol>
|
||||||
<Col>
|
<StyledCol>
|
||||||
<GeneralSelect
|
<GeneralSelect
|
||||||
label='Environment'
|
label='Environment'
|
||||||
name='environment'
|
name='environment'
|
||||||
@ -105,8 +92,8 @@ export const ActionItem = ({
|
|||||||
}
|
}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
</Col>
|
</StyledCol>
|
||||||
<Col>
|
<StyledCol>
|
||||||
<GeneralSelect
|
<GeneralSelect
|
||||||
label='Flag name'
|
label='Flag name'
|
||||||
name='flag'
|
name='flag'
|
||||||
@ -126,8 +113,8 @@ export const ActionItem = ({
|
|||||||
}
|
}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
</Col>
|
</StyledCol>
|
||||||
</Row>
|
</StyledRow>
|
||||||
</StyledInnerBox>
|
</StyledInnerBox>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
@ -1,13 +1,13 @@
|
|||||||
import { Badge, IconButton, Tooltip, styled } from '@mui/material';
|
import { Badge, IconButton, Tooltip, styled } from '@mui/material';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { IActionFilter } from './useProjectActionsForm';
|
import { ActionsFilterState } from './useProjectActionsForm';
|
||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
import { Delete } from '@mui/icons-material';
|
import { Delete } from '@mui/icons-material';
|
||||||
import Input from 'component/common/Input/Input';
|
import Input from 'component/common/Input/Input';
|
||||||
import {
|
import {
|
||||||
BoxSeparator,
|
BoxSeparator,
|
||||||
InnerBoxHeader,
|
StyledInnerBoxHeader,
|
||||||
Row,
|
StyledRow,
|
||||||
StyledInnerBox,
|
StyledInnerBox,
|
||||||
} from './InnerContainerBox';
|
} from './InnerContainerBox';
|
||||||
|
|
||||||
@ -20,18 +20,18 @@ const StyledBadge = styled(Badge)(({ theme }) => ({
|
|||||||
margin: theme.spacing(1),
|
margin: theme.spacing(1),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const FilterItem = ({
|
export const ProjectActionsFilterItem = ({
|
||||||
filter,
|
filter,
|
||||||
index,
|
index,
|
||||||
stateChanged,
|
stateChanged,
|
||||||
onDelete,
|
onDelete,
|
||||||
}: {
|
}: {
|
||||||
filter: IActionFilter;
|
filter: ActionsFilterState;
|
||||||
index: number;
|
index: number;
|
||||||
stateChanged: (updatedFilter: IActionFilter) => void;
|
stateChanged: (updatedFilter: ActionsFilterState) => void;
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { id, parameter, value } = filter;
|
const { parameter, value } = filter;
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
@ -39,25 +39,24 @@ export const FilterItem = ({
|
|||||||
show={<BoxSeparator>AND</BoxSeparator>}
|
show={<BoxSeparator>AND</BoxSeparator>}
|
||||||
/>
|
/>
|
||||||
<StyledInnerBox>
|
<StyledInnerBox>
|
||||||
<Row>
|
<StyledRow>
|
||||||
<span>Filter {index + 1}</span>
|
<span>Filter {index + 1}</span>
|
||||||
<InnerBoxHeader>
|
<StyledInnerBoxHeader>
|
||||||
<Tooltip title='Delete filter' arrow>
|
<Tooltip title='Delete filter' arrow>
|
||||||
<IconButton type='button' onClick={onDelete}>
|
<IconButton type='button' onClick={onDelete}>
|
||||||
<Delete />
|
<Delete />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</InnerBoxHeader>
|
</StyledInnerBoxHeader>
|
||||||
</Row>
|
</StyledRow>
|
||||||
<Row>
|
<StyledRow>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
label='Parameter'
|
label='Parameter'
|
||||||
value={parameter}
|
value={parameter}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
stateChanged({
|
stateChanged({
|
||||||
id,
|
...filter,
|
||||||
parameter: e.target.value,
|
parameter: e.target.value,
|
||||||
value,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -67,13 +66,12 @@ export const FilterItem = ({
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
stateChanged({
|
stateChanged({
|
||||||
id,
|
...filter,
|
||||||
parameter,
|
|
||||||
value: e.target.value,
|
value: e.target.value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Row>
|
</StyledRow>
|
||||||
</StyledInnerBox>
|
</StyledInnerBox>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
@ -4,9 +4,9 @@ import Input from 'component/common/Input/Input';
|
|||||||
import { Badge } from 'component/common/Badge/Badge';
|
import { Badge } from 'component/common/Badge/Badge';
|
||||||
import { FormSwitch } from 'component/common/FormSwitch/FormSwitch';
|
import { FormSwitch } from 'component/common/FormSwitch/FormSwitch';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { IActionSet } from 'interfaces/action';
|
|
||||||
import {
|
import {
|
||||||
IActionFilter,
|
ActionsFilterState,
|
||||||
|
ActionsActionState,
|
||||||
ProjectActionsFormErrors,
|
ProjectActionsFormErrors,
|
||||||
} from './useProjectActionsForm';
|
} from './useProjectActionsForm';
|
||||||
import { useServiceAccounts } from 'hooks/api/getters/useServiceAccounts/useServiceAccounts';
|
import { useServiceAccounts } from 'hooks/api/getters/useServiceAccounts/useServiceAccounts';
|
||||||
@ -16,9 +16,9 @@ import { useMemo } from 'react';
|
|||||||
import GeneralSelect, {} from 'component/common/GeneralSelect/GeneralSelect';
|
import GeneralSelect, {} from 'component/common/GeneralSelect/GeneralSelect';
|
||||||
import { Add } from '@mui/icons-material';
|
import { Add } from '@mui/icons-material';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { Row } from './InnerContainerBox';
|
import { StyledRow } from './InnerContainerBox';
|
||||||
import { ActionItem, UIAction } from './ActionItem';
|
import { ProjectActionsActionItem } from './ProjectActionsActionItem';
|
||||||
import { FilterItem } from './FilterItem';
|
import { ProjectActionsFilterItem } from './ProjectActionsFilterItem';
|
||||||
|
|
||||||
const StyledServiceAccountAlert = styled(Alert)(({ theme }) => ({
|
const StyledServiceAccountAlert = styled(Alert)(({ theme }) => ({
|
||||||
marginBottom: theme.spacing(4),
|
marginBottom: theme.spacing(4),
|
||||||
@ -67,26 +67,24 @@ const Step = ({ name, children }: any) => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
interface IProjectActionsFormProps {
|
interface IProjectActionsFormProps {
|
||||||
action?: IActionSet;
|
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
setEnabled: React.Dispatch<React.SetStateAction<boolean>>;
|
setEnabled: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
name: string;
|
name: string;
|
||||||
setName: React.Dispatch<React.SetStateAction<string>>;
|
setName: React.Dispatch<React.SetStateAction<string>>;
|
||||||
sourceId: number;
|
sourceId: number;
|
||||||
setSourceId: React.Dispatch<React.SetStateAction<number>>;
|
setSourceId: React.Dispatch<React.SetStateAction<number>>;
|
||||||
filters: IActionFilter[];
|
filters: ActionsFilterState[];
|
||||||
setFilters: React.Dispatch<React.SetStateAction<IActionFilter[]>>;
|
setFilters: React.Dispatch<React.SetStateAction<ActionsFilterState[]>>;
|
||||||
actorId: number;
|
actorId: number;
|
||||||
setActorId: React.Dispatch<React.SetStateAction<number>>;
|
setActorId: React.Dispatch<React.SetStateAction<number>>;
|
||||||
actions: UIAction[];
|
actions: ActionsActionState[];
|
||||||
setActions: React.Dispatch<React.SetStateAction<UIAction[]>>;
|
setActions: React.Dispatch<React.SetStateAction<ActionsActionState[]>>;
|
||||||
errors: ProjectActionsFormErrors;
|
errors: ProjectActionsFormErrors;
|
||||||
validateName: (name: string) => boolean;
|
validateName: (name: string) => boolean;
|
||||||
validated: boolean;
|
validated: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProjectActionsForm = ({
|
export const ProjectActionsForm = ({
|
||||||
action,
|
|
||||||
enabled,
|
enabled,
|
||||||
setEnabled,
|
setEnabled,
|
||||||
name,
|
name,
|
||||||
@ -124,7 +122,7 @@ export const ProjectActionsForm = ({
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateInFilters = (updatedFilter: IActionFilter) => {
|
const updateInFilters = (updatedFilter: ActionsFilterState) => {
|
||||||
setFilters((filters) =>
|
setFilters((filters) =>
|
||||||
filters.map((filter) =>
|
filters.map((filter) =>
|
||||||
filter.id === updatedFilter.id ? updatedFilter : filter,
|
filter.id === updatedFilter.id ? updatedFilter : filter,
|
||||||
@ -134,7 +132,7 @@ export const ProjectActionsForm = ({
|
|||||||
|
|
||||||
const addAction = (projectId: string) => {
|
const addAction = (projectId: string) => {
|
||||||
const id = uuidv4();
|
const id = uuidv4();
|
||||||
const action: UIAction = {
|
const action: ActionsActionState = {
|
||||||
id,
|
id,
|
||||||
action: '',
|
action: '',
|
||||||
sortOrder:
|
sortOrder:
|
||||||
@ -148,7 +146,7 @@ export const ProjectActionsForm = ({
|
|||||||
setActions([...actions, action]);
|
setActions([...actions, action]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateInActions = (updatedAction: UIAction) => {
|
const updateInActions = (updatedAction: ActionsActionState) => {
|
||||||
setActions((actions) =>
|
setActions((actions) =>
|
||||||
actions.map((action) =>
|
actions.map((action) =>
|
||||||
action.id === updatedAction.id ? updatedAction : action,
|
action.id === updatedAction.id ? updatedAction : action,
|
||||||
@ -241,7 +239,7 @@ export const ProjectActionsForm = ({
|
|||||||
|
|
||||||
<Step name='When this'>
|
<Step name='When this'>
|
||||||
{filters.map((filter, index) => (
|
{filters.map((filter, index) => (
|
||||||
<FilterItem
|
<ProjectActionsFilterItem
|
||||||
key={filter.id}
|
key={filter.id}
|
||||||
index={index}
|
index={index}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
@ -255,7 +253,7 @@ export const ProjectActionsForm = ({
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
<Row>
|
<StyledRow>
|
||||||
<Button
|
<Button
|
||||||
type='button'
|
type='button'
|
||||||
startIcon={<Add />}
|
startIcon={<Add />}
|
||||||
@ -265,7 +263,7 @@ export const ProjectActionsForm = ({
|
|||||||
>
|
>
|
||||||
Add filter
|
Add filter
|
||||||
</Button>
|
</Button>
|
||||||
</Row>
|
</StyledRow>
|
||||||
</Step>
|
</Step>
|
||||||
|
|
||||||
<Step name='Do these action(s)'>
|
<Step name='Do these action(s)'>
|
||||||
@ -287,7 +285,7 @@ export const ProjectActionsForm = ({
|
|||||||
/>
|
/>
|
||||||
<hr />
|
<hr />
|
||||||
{actions.map((action, index) => (
|
{actions.map((action, index) => (
|
||||||
<ActionItem
|
<ProjectActionsActionItem
|
||||||
index={index}
|
index={index}
|
||||||
key={action.id}
|
key={action.id}
|
||||||
action={action}
|
action={action}
|
||||||
@ -300,7 +298,7 @@ export const ProjectActionsForm = ({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<hr />
|
<hr />
|
||||||
<Row>
|
<StyledRow>
|
||||||
<Button
|
<Button
|
||||||
type='button'
|
type='button'
|
||||||
startIcon={<Add />}
|
startIcon={<Add />}
|
||||||
@ -310,7 +308,7 @@ export const ProjectActionsForm = ({
|
|||||||
>
|
>
|
||||||
Add action
|
Add action
|
||||||
</Button>
|
</Button>
|
||||||
</Row>
|
</StyledRow>
|
||||||
</Step>
|
</Step>
|
||||||
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
|
@ -1,22 +1,28 @@
|
|||||||
import { useActions } from 'hooks/api/getters/useActions/useActions';
|
import { useActions } from 'hooks/api/getters/useActions/useActions';
|
||||||
import { IActionSet } from 'interfaces/action';
|
import { IAction, IActionSet } from 'interfaces/action';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { UIAction } from './ActionItem';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
|
|
||||||
export enum ErrorField {
|
enum ErrorField {
|
||||||
NAME = 'name',
|
NAME = 'name',
|
||||||
TRIGGER = 'trigger',
|
TRIGGER = 'trigger',
|
||||||
ACTOR = 'actor',
|
ACTOR = 'actor',
|
||||||
ACTIONS = 'actions',
|
ACTIONS = 'actions',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IActionFilter {
|
export type ActionsFilterState = {
|
||||||
id: string;
|
id: string;
|
||||||
parameter: string;
|
parameter: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export type ActionsActionState = Omit<
|
||||||
|
IAction,
|
||||||
|
'id' | 'createdAt' | 'createdByUserId'
|
||||||
|
> & {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
const DEFAULT_PROJECT_ACTIONS_FORM_ERRORS = {
|
const DEFAULT_PROJECT_ACTIONS_FORM_ERRORS = {
|
||||||
[ErrorField.NAME]: undefined,
|
[ErrorField.NAME]: undefined,
|
||||||
@ -34,38 +40,33 @@ export const useProjectActionsForm = (action?: IActionSet) => {
|
|||||||
const [enabled, setEnabled] = useState(false);
|
const [enabled, setEnabled] = useState(false);
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [sourceId, setSourceId] = useState<number>(0);
|
const [sourceId, setSourceId] = useState<number>(0);
|
||||||
const [filters, setFilters] = useState<IActionFilter[]>([]);
|
const [filters, setFilters] = useState<ActionsFilterState[]>([]);
|
||||||
const [actorId, setActorId] = useState<number>(0);
|
const [actorId, setActorId] = useState<number>(0);
|
||||||
const [actions, setActions] = useState<UIAction[]>([]);
|
const [actions, setActions] = useState<ActionsActionState[]>([]);
|
||||||
|
|
||||||
const reloadForm = () => {
|
const reloadForm = () => {
|
||||||
setEnabled(action?.enabled ?? true);
|
setEnabled(action?.enabled ?? true);
|
||||||
setName(action?.name || '');
|
setName(action?.name || '');
|
||||||
setValidated(false);
|
setSourceId(action?.match?.sourceId ?? 0);
|
||||||
if (action?.actorId) {
|
setFilters(
|
||||||
setActorId(action?.actorId);
|
Object.entries(action?.match?.payload ?? {}).map(
|
||||||
}
|
([parameter, value]) => ({
|
||||||
if (action?.match) {
|
|
||||||
const { sourceId, payload } = action.match;
|
|
||||||
setSourceId(sourceId);
|
|
||||||
setFilters(
|
|
||||||
Object.entries(payload).map(([parameter, value]) => ({
|
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
parameter,
|
parameter,
|
||||||
value: value as string,
|
value: value as string,
|
||||||
})),
|
}),
|
||||||
);
|
),
|
||||||
}
|
);
|
||||||
if (action?.actions) {
|
setActorId(action?.actorId ?? 0);
|
||||||
setActions(
|
setActions(
|
||||||
action.actions.map((action) => ({
|
action?.actions?.map((action) => ({
|
||||||
id: uuidv4(),
|
id: uuidv4(),
|
||||||
action: action.action,
|
action: action.action,
|
||||||
sortOrder: action.sortOrder,
|
sortOrder: action.sortOrder,
|
||||||
executionParams: action.executionParams,
|
executionParams: action.executionParams,
|
||||||
})),
|
})) ?? [],
|
||||||
);
|
);
|
||||||
}
|
setValidated(false);
|
||||||
setErrors(DEFAULT_PROJECT_ACTIONS_FORM_ERRORS);
|
setErrors(DEFAULT_PROJECT_ACTIONS_FORM_ERRORS);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -128,7 +129,7 @@ export const useProjectActionsForm = (action?: IActionSet) => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateActions = (actions: UIAction[]) => {
|
const validateActions = (actions: ActionsActionState[]) => {
|
||||||
if (actions.length === 0) {
|
if (actions.length === 0) {
|
||||||
setError(ErrorField.ACTIONS, 'At least one action is required.');
|
setError(ErrorField.ACTIONS, 'At least one action is required.');
|
||||||
return false;
|
return false;
|
||||||
|
@ -77,7 +77,6 @@ export const ProjectActionsModal = ({
|
|||||||
const title = `${editing ? 'Edit' : 'New'} action`;
|
const title = `${editing ? 'Edit' : 'New'} action`;
|
||||||
|
|
||||||
const payload: ActionSetPayload = {
|
const payload: ActionSetPayload = {
|
||||||
project: projectId,
|
|
||||||
enabled,
|
enabled,
|
||||||
name,
|
name,
|
||||||
match: {
|
match: {
|
||||||
@ -151,7 +150,6 @@ export const ProjectActionsModal = ({
|
|||||||
>
|
>
|
||||||
<StyledForm onSubmit={onSubmit}>
|
<StyledForm onSubmit={onSubmit}>
|
||||||
<ProjectActionsForm
|
<ProjectActionsForm
|
||||||
action={action}
|
|
||||||
enabled={enabled}
|
enabled={enabled}
|
||||||
setEnabled={setEnabled}
|
setEnabled={setEnabled}
|
||||||
name={name}
|
name={name}
|
||||||
|
@ -8,7 +8,7 @@ export type ActionPayload = Omit<
|
|||||||
|
|
||||||
export type ActionSetPayload = Omit<
|
export type ActionSetPayload = Omit<
|
||||||
IActionSet,
|
IActionSet,
|
||||||
'id' | 'createdAt' | 'createdByUserId'
|
'id' | 'project' | 'actions' | 'createdAt' | 'createdByUserId'
|
||||||
> & {
|
> & {
|
||||||
actions: ActionPayload[];
|
actions: ActionPayload[];
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,10 @@ import useUiConfig from '../useUiConfig/useUiConfig';
|
|||||||
import { IActionSet } from 'interfaces/action';
|
import { IActionSet } from 'interfaces/action';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
|
|
||||||
|
const DEFAULT_DATA = {
|
||||||
|
actions: [],
|
||||||
|
};
|
||||||
|
|
||||||
export const useActions = (project: string) => {
|
export const useActions = (project: string) => {
|
||||||
const { isEnterprise } = useUiConfig();
|
const { isEnterprise } = useUiConfig();
|
||||||
const actionsEnabled = useUiFlag('automatedActions');
|
const actionsEnabled = useUiFlag('automatedActions');
|
||||||
@ -14,14 +18,14 @@ export const useActions = (project: string) => {
|
|||||||
actions: IActionSet[];
|
actions: IActionSet[];
|
||||||
}>(
|
}>(
|
||||||
isEnterprise() && actionsEnabled,
|
isEnterprise() && actionsEnabled,
|
||||||
{ actions: [] },
|
DEFAULT_DATA,
|
||||||
formatApiPath(`api/admin/projects/${project}/actions`),
|
formatApiPath(`api/admin/projects/${project}/actions`),
|
||||||
fetcher,
|
fetcher,
|
||||||
);
|
);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
actions: (data?.actions ?? []) as IActionSet[],
|
actions: data?.actions ?? [],
|
||||||
loading: !error && !data,
|
loading: !error && !data,
|
||||||
refetch: () => mutate(),
|
refetch: () => mutate(),
|
||||||
error,
|
error,
|
||||||
|
@ -16,7 +16,9 @@ export const useIncomingWebhooks = () => {
|
|||||||
const { isEnterprise } = useUiConfig();
|
const { isEnterprise } = useUiConfig();
|
||||||
const incomingWebhooksEnabled = useUiFlag('incomingWebhooks');
|
const incomingWebhooksEnabled = useUiFlag('incomingWebhooks');
|
||||||
|
|
||||||
const { data, error, mutate } = useConditionalSWR(
|
const { data, error, mutate } = useConditionalSWR<{
|
||||||
|
incomingWebhooks: IIncomingWebhook[];
|
||||||
|
}>(
|
||||||
isEnterprise() && incomingWebhooksEnabled,
|
isEnterprise() && incomingWebhooksEnabled,
|
||||||
DEFAULT_DATA,
|
DEFAULT_DATA,
|
||||||
formatApiPath(ENDPOINT),
|
formatApiPath(ENDPOINT),
|
||||||
@ -25,8 +27,7 @@ export const useIncomingWebhooks = () => {
|
|||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
incomingWebhooks: (data?.incomingWebhooks ??
|
incomingWebhooks: data?.incomingWebhooks ?? [],
|
||||||
[]) as IIncomingWebhook[],
|
|
||||||
loading: !error && !data,
|
loading: !error && !data,
|
||||||
refetch: () => mutate(),
|
refetch: () => mutate(),
|
||||||
error,
|
error,
|
||||||
|
@ -19,7 +19,10 @@ export interface IMatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IAction {
|
export interface IAction {
|
||||||
|
id: number;
|
||||||
action: string;
|
action: string;
|
||||||
sortOrder: number;
|
sortOrder: number;
|
||||||
executionParams: Record<string, unknown>;
|
executionParams: Record<string, unknown>;
|
||||||
|
createdAt: string;
|
||||||
|
createdByUserId: number;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user