- {actions.map(
- ({ action, executionParams, sortOrder }) => (
-
- {action}
-
- {Object.entries(executionParams).map(
- ([param, value]) => (
-
- {param}:{' '}
- {value}
-
- ),
- )}
-
-
- ),
- )}
+ {actions.map(({ id, action, executionParams }) => (
+
+ {action}
+
+ {Object.entries(executionParams).map(
+ ([param, value]) => (
+
+ {param}:{' '}
+ {value}
+
+ ),
+ )}
+
+
+ ))}
}
>
diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/InnerContainerBox.tsx b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/InnerContainerBox.tsx
index d2fc1d85f6..4401114167 100644
--- a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/InnerContainerBox.tsx
+++ b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/InnerContainerBox.tsx
@@ -9,7 +9,7 @@ export const StyledInnerBox = styled(Box)(({ theme }) => ({
borderRadius: `${theme.shape.borderRadiusMedium}px`,
}));
-export const InnerBoxHeader = styled('div')(({ theme }) => ({
+export const StyledInnerBoxHeader = styled('div')(({ theme }) => ({
marginLeft: 'auto',
whiteSpace: 'nowrap',
[theme.breakpoints.down('sm')]: {
@@ -18,31 +18,32 @@ export const InnerBoxHeader = styled('div')(({ theme }) => ({
}));
// row for inner containers
-export const Row = styled('div')({
+export const StyledRow = styled('div')({
display: 'flex',
flexDirection: 'row',
width: '100%',
});
-export const Col = styled('div')({
+export const StyledCol = styled('div')({
flex: 1,
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 }) => {
- 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 (
& {
- id: string;
-};
-
-export const ActionItem = ({
+export const ProjectActionsActionItem = ({
action,
index,
stateChanged,
onDelete,
}: {
- action: UIAction;
+ action: ActionsActionState;
index: number;
- stateChanged: (action: UIAction) => void;
+ stateChanged: (action: ActionsActionState) => void;
onDelete: () => void;
}) => {
- const { id, action: actionName } = action;
+ const { action: actionName } = action;
const projectId = useRequiredPathParam('projectId');
const environments = useProjectEnvironments(projectId);
- const { features } = useFeatureSearch(
- mapValues(
- {
- project: `IS:${projectId}`,
- },
- (value) => (value ? `${value}` : undefined),
- ),
- {},
- );
+ const { features } = useFeatureSearch({ project: `IS:${projectId}` });
return (
THEN}
/>
-
+
Action {index + 1}
-
+
-
-
-
-
+
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
);
diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/FilterItem.tsx b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/ProjectActionsFilterItem.tsx
similarity index 77%
rename from frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/FilterItem.tsx
rename to frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/ProjectActionsFilterItem.tsx
index 7f5271b6a8..6595271ce0 100644
--- a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/FilterItem.tsx
+++ b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/ProjectActionsFilterItem.tsx
@@ -1,13 +1,13 @@
import { Badge, IconButton, Tooltip, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import { IActionFilter } from './useProjectActionsForm';
+import { ActionsFilterState } from './useProjectActionsForm';
import { Fragment } from 'react';
import { Delete } from '@mui/icons-material';
import Input from 'component/common/Input/Input';
import {
BoxSeparator,
- InnerBoxHeader,
- Row,
+ StyledInnerBoxHeader,
+ StyledRow,
StyledInnerBox,
} from './InnerContainerBox';
@@ -20,18 +20,18 @@ const StyledBadge = styled(Badge)(({ theme }) => ({
margin: theme.spacing(1),
}));
-export const FilterItem = ({
+export const ProjectActionsFilterItem = ({
filter,
index,
stateChanged,
onDelete,
}: {
- filter: IActionFilter;
+ filter: ActionsFilterState;
index: number;
- stateChanged: (updatedFilter: IActionFilter) => void;
+ stateChanged: (updatedFilter: ActionsFilterState) => void;
onDelete: () => void;
}) => {
- const { id, parameter, value } = filter;
+ const { parameter, value } = filter;
return (
AND}
/>
-
+
Filter {index + 1}
-
+
-
-
-
+
+
+
stateChanged({
- id,
+ ...filter,
parameter: e.target.value,
- value,
})
}
/>
@@ -67,13 +66,12 @@ export const FilterItem = ({
value={value}
onChange={(e) =>
stateChanged({
- id,
- parameter,
+ ...filter,
value: e.target.value,
})
}
/>
-
+
);
diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/ProjectActionsForm.tsx b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/ProjectActionsForm.tsx
index bad5c72d95..791eb60b5c 100644
--- a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/ProjectActionsForm.tsx
+++ b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsForm/ProjectActionsForm.tsx
@@ -4,9 +4,9 @@ import Input from 'component/common/Input/Input';
import { Badge } from 'component/common/Badge/Badge';
import { FormSwitch } from 'component/common/FormSwitch/FormSwitch';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import { IActionSet } from 'interfaces/action';
import {
- IActionFilter,
+ ActionsFilterState,
+ ActionsActionState,
ProjectActionsFormErrors,
} from './useProjectActionsForm';
import { useServiceAccounts } from 'hooks/api/getters/useServiceAccounts/useServiceAccounts';
@@ -16,9 +16,9 @@ import { useMemo } from 'react';
import GeneralSelect, {} from 'component/common/GeneralSelect/GeneralSelect';
import { Add } from '@mui/icons-material';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
-import { Row } from './InnerContainerBox';
-import { ActionItem, UIAction } from './ActionItem';
-import { FilterItem } from './FilterItem';
+import { StyledRow } from './InnerContainerBox';
+import { ProjectActionsActionItem } from './ProjectActionsActionItem';
+import { ProjectActionsFilterItem } from './ProjectActionsFilterItem';
const StyledServiceAccountAlert = styled(Alert)(({ theme }) => ({
marginBottom: theme.spacing(4),
@@ -67,26 +67,24 @@ const Step = ({ name, children }: any) => (
);
interface IProjectActionsFormProps {
- action?: IActionSet;
enabled: boolean;
setEnabled: React.Dispatch>;
name: string;
setName: React.Dispatch>;
sourceId: number;
setSourceId: React.Dispatch>;
- filters: IActionFilter[];
- setFilters: React.Dispatch>;
+ filters: ActionsFilterState[];
+ setFilters: React.Dispatch>;
actorId: number;
setActorId: React.Dispatch>;
- actions: UIAction[];
- setActions: React.Dispatch>;
+ actions: ActionsActionState[];
+ setActions: React.Dispatch>;
errors: ProjectActionsFormErrors;
validateName: (name: string) => boolean;
validated: boolean;
}
export const ProjectActionsForm = ({
- action,
enabled,
setEnabled,
name,
@@ -124,7 +122,7 @@ export const ProjectActionsForm = ({
]);
};
- const updateInFilters = (updatedFilter: IActionFilter) => {
+ const updateInFilters = (updatedFilter: ActionsFilterState) => {
setFilters((filters) =>
filters.map((filter) =>
filter.id === updatedFilter.id ? updatedFilter : filter,
@@ -134,7 +132,7 @@ export const ProjectActionsForm = ({
const addAction = (projectId: string) => {
const id = uuidv4();
- const action: UIAction = {
+ const action: ActionsActionState = {
id,
action: '',
sortOrder:
@@ -148,7 +146,7 @@ export const ProjectActionsForm = ({
setActions([...actions, action]);
};
- const updateInActions = (updatedAction: UIAction) => {
+ const updateInActions = (updatedAction: ActionsActionState) => {
setActions((actions) =>
actions.map((action) =>
action.id === updatedAction.id ? updatedAction : action,
@@ -241,7 +239,7 @@ export const ProjectActionsForm = ({
{filters.map((filter, index) => (
-
-
+
}
@@ -265,7 +263,7 @@ export const ProjectActionsForm = ({
>
Add filter
-
+
@@ -287,7 +285,7 @@ export const ProjectActionsForm = ({
/>
{actions.map((action, index) => (
-
))}
-
+
}
@@ -310,7 +308,7 @@ export const ProjectActionsForm = ({
>
Add action
-
+
& {
+ id: string;
+};
const DEFAULT_PROJECT_ACTIONS_FORM_ERRORS = {
[ErrorField.NAME]: undefined,
@@ -34,38 +40,33 @@ export const useProjectActionsForm = (action?: IActionSet) => {
const [enabled, setEnabled] = useState(false);
const [name, setName] = useState('');
const [sourceId, setSourceId] = useState(0);
- const [filters, setFilters] = useState([]);
+ const [filters, setFilters] = useState([]);
const [actorId, setActorId] = useState(0);
- const [actions, setActions] = useState([]);
+ const [actions, setActions] = useState([]);
const reloadForm = () => {
setEnabled(action?.enabled ?? true);
setName(action?.name || '');
- setValidated(false);
- if (action?.actorId) {
- setActorId(action?.actorId);
- }
- if (action?.match) {
- const { sourceId, payload } = action.match;
- setSourceId(sourceId);
- setFilters(
- Object.entries(payload).map(([parameter, value]) => ({
+ setSourceId(action?.match?.sourceId ?? 0);
+ setFilters(
+ Object.entries(action?.match?.payload ?? {}).map(
+ ([parameter, value]) => ({
id: uuidv4(),
parameter,
value: value as string,
- })),
- );
- }
- if (action?.actions) {
- setActions(
- action.actions.map((action) => ({
- id: uuidv4(),
- action: action.action,
- sortOrder: action.sortOrder,
- executionParams: action.executionParams,
- })),
- );
- }
+ }),
+ ),
+ );
+ setActorId(action?.actorId ?? 0);
+ setActions(
+ action?.actions?.map((action) => ({
+ id: uuidv4(),
+ action: action.action,
+ sortOrder: action.sortOrder,
+ executionParams: action.executionParams,
+ })) ?? [],
+ );
+ setValidated(false);
setErrors(DEFAULT_PROJECT_ACTIONS_FORM_ERRORS);
};
@@ -128,7 +129,7 @@ export const useProjectActionsForm = (action?: IActionSet) => {
return true;
};
- const validateActions = (actions: UIAction[]) => {
+ const validateActions = (actions: ActionsActionState[]) => {
if (actions.length === 0) {
setError(ErrorField.ACTIONS, 'At least one action is required.');
return false;
diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsModal.tsx b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsModal.tsx
index 89ed2eaa82..541e3ad2d7 100644
--- a/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsModal.tsx
+++ b/frontend/src/component/project/Project/ProjectSettings/ProjectActions/ProjectActionsTable/ProjectActionsModal/ProjectActionsModal.tsx
@@ -77,7 +77,6 @@ export const ProjectActionsModal = ({
const title = `${editing ? 'Edit' : 'New'} action`;
const payload: ActionSetPayload = {
- project: projectId,
enabled,
name,
match: {
@@ -151,7 +150,6 @@ export const ProjectActionsModal = ({
>
& {
actions: ActionPayload[];
};
diff --git a/frontend/src/hooks/api/getters/useActions/useActions.ts b/frontend/src/hooks/api/getters/useActions/useActions.ts
index 46a291bd5a..ae1ea99264 100644
--- a/frontend/src/hooks/api/getters/useActions/useActions.ts
+++ b/frontend/src/hooks/api/getters/useActions/useActions.ts
@@ -6,6 +6,10 @@ import useUiConfig from '../useUiConfig/useUiConfig';
import { IActionSet } from 'interfaces/action';
import { useUiFlag } from 'hooks/useUiFlag';
+const DEFAULT_DATA = {
+ actions: [],
+};
+
export const useActions = (project: string) => {
const { isEnterprise } = useUiConfig();
const actionsEnabled = useUiFlag('automatedActions');
@@ -14,14 +18,14 @@ export const useActions = (project: string) => {
actions: IActionSet[];
}>(
isEnterprise() && actionsEnabled,
- { actions: [] },
+ DEFAULT_DATA,
formatApiPath(`api/admin/projects/${project}/actions`),
fetcher,
);
return useMemo(
() => ({
- actions: (data?.actions ?? []) as IActionSet[],
+ actions: data?.actions ?? [],
loading: !error && !data,
refetch: () => mutate(),
error,
diff --git a/frontend/src/hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks.ts b/frontend/src/hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks.ts
index 2f33f7f4d3..0336fc9206 100644
--- a/frontend/src/hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks.ts
+++ b/frontend/src/hooks/api/getters/useIncomingWebhooks/useIncomingWebhooks.ts
@@ -16,7 +16,9 @@ export const useIncomingWebhooks = () => {
const { isEnterprise } = useUiConfig();
const incomingWebhooksEnabled = useUiFlag('incomingWebhooks');
- const { data, error, mutate } = useConditionalSWR(
+ const { data, error, mutate } = useConditionalSWR<{
+ incomingWebhooks: IIncomingWebhook[];
+ }>(
isEnterprise() && incomingWebhooksEnabled,
DEFAULT_DATA,
formatApiPath(ENDPOINT),
@@ -25,8 +27,7 @@ export const useIncomingWebhooks = () => {
return useMemo(
() => ({
- incomingWebhooks: (data?.incomingWebhooks ??
- []) as IIncomingWebhook[],
+ incomingWebhooks: data?.incomingWebhooks ?? [],
loading: !error && !data,
refetch: () => mutate(),
error,
diff --git a/frontend/src/interfaces/action.ts b/frontend/src/interfaces/action.ts
index 2bb98bb26e..d521c81f06 100644
--- a/frontend/src/interfaces/action.ts
+++ b/frontend/src/interfaces/action.ts
@@ -19,7 +19,10 @@ export interface IMatch {
}
export interface IAction {
+ id: number;
action: string;
sortOrder: number;
executionParams: Record;
+ createdAt: string;
+ createdByUserId: number;
}