mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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