diff --git a/src/assets/images/icons/Pin/Active.svg b/src/assets/images/icons/Pin/Active.svg
index 6444d4135..95ed20dd1 100644
--- a/src/assets/images/icons/Pin/Active.svg
+++ b/src/assets/images/icons/Pin/Active.svg
@@ -1,16 +1,12 @@
-
-
\ No newline at end of file
+
diff --git a/src/containers/Flow/FlowList/FlowList.test.tsx b/src/containers/Flow/FlowList/FlowList.test.tsx
index 686ed895e..73d4dba1f 100644
--- a/src/containers/Flow/FlowList/FlowList.test.tsx
+++ b/src/containers/Flow/FlowList/FlowList.test.tsx
@@ -13,6 +13,7 @@ import {
exportFlow,
releaseFlow,
filterTemplateFlows,
+ pinFlowQuery,
} from 'mocks/Flow';
import { getOrganizationQuery } from 'mocks/Organization';
import testJSON from 'mocks/ImportFlow.json';
@@ -21,6 +22,7 @@ import { FlowList } from './FlowList';
import { Flow } from '../Flow';
import { getFilterTagQuery } from 'mocks/Tag';
import { getRoleNameQuery } from 'mocks/Role';
+import * as Notification from 'common/notification';
const isActiveFilter = { isActive: true, isTemplate: false };
@@ -44,6 +46,8 @@ const mocks = [
getRoleNameQuery,
getFlowCountQuery({ isTemplate: true }),
filterTemplateFlows,
+ pinFlowQuery('2', true),
+ pinFlowQuery('1'),
...getOrganizationQuery,
];
@@ -69,6 +73,7 @@ vi.mock('react-router-dom', async () => {
setUserSession(JSON.stringify({ roles: [{ id: '1', label: 'Admin' }] }));
setOrganizationServices('{"__typename":"OrganizationServicesResult","rolesAndPermission":true}');
+const notificationSpy = vi.spyOn(Notification, 'setNotification');
describe('', () => {
test('should render Flow', async () => {
@@ -79,7 +84,7 @@ describe('', () => {
});
});
- test('should search flow and check if flow keywprds are present below the name', async () => {
+ test('should search flow and check if flow keywords are present below the name', async () => {
const { getByText, getByTestId, queryByPlaceholderText } = render(flowList);
await waitFor(() => {
// type "Help Workflow" in search box and enter
@@ -193,6 +198,26 @@ describe('', () => {
expect(mockedUsedNavigate).toHaveBeenCalled();
});
});
+
+ test('it should pin/unpin the flows', async () => {
+ render(flowList);
+
+ await waitFor(() => {
+ expect(screen.getByText('Flows')).toBeInTheDocument();
+ });
+
+ fireEvent.click(screen.getAllByTestId('pin-button')[0]);
+
+ await waitFor(() => {
+ expect(notificationSpy).toHaveBeenCalled();
+ });
+
+ fireEvent.click(screen.getAllByTestId('unpin-button')[0]);
+
+ await waitFor(() => {
+ expect(notificationSpy).toHaveBeenCalled();
+ });
+ });
});
describe('Template flows', () => {
diff --git a/src/containers/Flow/FlowList/FlowList.tsx b/src/containers/Flow/FlowList/FlowList.tsx
index e1aa59562..4c650102b 100644
--- a/src/containers/Flow/FlowList/FlowList.tsx
+++ b/src/containers/Flow/FlowList/FlowList.tsx
@@ -4,22 +4,24 @@ import dayjs from 'dayjs';
import { useTranslation } from 'react-i18next';
import { useLazyQuery, useMutation, useQuery } from '@apollo/client';
-import { FormControl, MenuItem, Select } from '@mui/material';
+import { FormControl, IconButton, MenuItem, Select } from '@mui/material';
import FlowIcon from 'assets/images/icons/Flow/Dark.svg?react';
import DuplicateIcon from 'assets/images/icons/Duplicate.svg?react';
import ExportIcon from 'assets/images/icons/Flow/Export.svg?react';
import ConfigureIcon from 'assets/images/icons/Configure/UnselectedDark.svg?react';
-import PinIcon from 'assets/images/icons/Pin/Active.svg?react';
+import PinIcon from 'assets/images/icons/Pin/Pin.svg?react';
+import ActivePinIcon from 'assets/images/icons/Pin/Active.svg?react';
import ViewIcon from 'assets/images/icons/ViewLight.svg?react';
import { FILTER_FLOW, GET_FLOW_COUNT, EXPORT_FLOW, RELEASE_FLOW } from 'graphql/queries/Flow';
-import { DELETE_FLOW, IMPORT_FLOW } from 'graphql/mutations/Flow';
+import { DELETE_FLOW, IMPORT_FLOW, PIN_FLOW } from 'graphql/mutations/Flow';
import { List } from 'containers/List/List';
import { ImportButton } from 'components/UI/ImportButton/ImportButton';
import { STANDARD_DATE_TIME_FORMAT } from 'common/constants';
import { exportFlowMethod, organizationHasDynamicRole } from 'common/utils';
import styles from './FlowList.module.css';
import { GET_TAGS } from 'graphql/queries/Tags';
+import Tooltip from 'components/UI/Tooltip/Tooltip';
import { AutoComplete } from 'components/UI/Form/AutoComplete/AutoComplete';
import { flowInfo } from 'common/HelpData';
import { DialogBox } from 'components/UI/DialogBox/DialogBox';
@@ -34,17 +36,13 @@ const getName = (text: string, keywordsList: any, roles: any) => {
{text}
{keywords.join(', ')}
- {hasDynamicRole && (
- {accessRoles && accessRoles.join(', ')}
- )}
+ {hasDynamicRole && {accessRoles && accessRoles.join(', ')} }
);
};
const getDate = (date: string, fallback: string = '') => (
-
- {date ? dayjs(date).format(STANDARD_DATE_TIME_FORMAT) : fallback}
-
+ {date ? dayjs(date).format(STANDARD_DATE_TIME_FORMAT) : fallback}
);
const getLastPublished = (date: string, fallback: string = '') =>
@@ -55,21 +53,7 @@ const getLastPublished = (date: string, fallback: string = '') =>
);
const getLabel = (tag: any) => {tag.label}
;
-const displayPinned = (isPinned: boolean) => {
- if (isPinned) {
- return ;
- }
- return '';
-};
-
-const columnStyles = [
- styles.Pinned,
- styles.Name,
- styles.DateColumn,
- styles.Label,
- styles.DateColumn,
- styles.Actions,
-];
+const columnStyles = [styles.Pinned, styles.Name, styles.DateColumn, styles.Label, styles.DateColumn, styles.Actions];
const flowIcon = ;
const queries = {
@@ -91,6 +75,7 @@ export const FlowList = () => {
const [importing, setImporting] = useState(false);
const [importStatus, setImportStatus] = useState([]);
const [showDialog, setShowDialog] = useState(false);
+ const [refreshList, setRefreshList] = useState(false);
const [releaseFlow] = useLazyQuery(RELEASE_FLOW);
@@ -122,6 +107,8 @@ export const FlowList = () => {
},
});
+ const [updatePinned] = useMutation(PIN_FLOW);
+
const handleCopy = (id: any) => {
navigate(`/flow/${id}/edit`, { state: 'copy' });
};
@@ -130,8 +117,58 @@ export const FlowList = () => {
setFlowName(item.name);
exportFlowMutation({ variables: { id } });
};
+
+ const handlePin = (updateFlowId: any, pin: boolean = false) => {
+ if (pin) {
+ updatePinned({
+ variables: {
+ updateFlowId,
+ input: {
+ isPinned: true,
+ },
+ },
+ onCompleted: () => {
+ setRefreshList(!refreshList);
+ setNotification('Flow pinned successfully');
+ },
+ });
+ } else {
+ updatePinned({
+ variables: {
+ updateFlowId,
+ input: {
+ isPinned: false,
+ },
+ },
+ onCompleted: () => {
+ setRefreshList(!refreshList);
+ setNotification('Flow unpinned successfully');
+ },
+ });
+ }
+ };
+
let dialog;
+ const displayPinned = (isPinned: boolean, id: any) => {
+ if (isPinned) {
+ return (
+
+ handlePin(id)}>
+
+
+
+ );
+ }
+ return (
+
+ handlePin(id, true)}>
+
+
+
+ );
+ };
+
if (importStatus.length > 0) {
dialog = (
{
const additionalAction = () => (filter === 'isTemplate' ? templateFlowActions : actions);
- const getColumns = ({
- name,
- keywords,
- lastChangedAt,
- lastPublishedAt,
- tag,
- roles,
- isPinned,
- }: any) => ({
- pin: displayPinned(isPinned),
+ const getColumns = ({ name, keywords, lastChangedAt, lastPublishedAt, tag, roles, isPinned, id }: any) => ({
+ pin: displayPinned(isPinned, id),
name: getName(name, keywords, roles),
lastPublishedAt: getLastPublished(lastPublishedAt, t('Not published yet')),
label: tag ? getLabel(tag) : '',
@@ -360,6 +389,7 @@ export const FlowList = () => {
filterList={activeFilter}
loadingList={importing}
restrictedAction={restrictedAction}
+ refreshList={refreshList}
/>
>
);
diff --git a/src/graphql/mutations/Flow.ts b/src/graphql/mutations/Flow.ts
index 7465c8fc8..aad737027 100644
--- a/src/graphql/mutations/Flow.ts
+++ b/src/graphql/mutations/Flow.ts
@@ -167,3 +167,18 @@ export const TERMINATE_FLOW = gql`
}
}
`;
+
+export const PIN_FLOW = gql`
+ mutation UpdateFlow($updateFlowId: ID!, $input: FlowInput) {
+ updateFlow(id: $updateFlowId, input: $input) {
+ errors {
+ key
+ message
+ }
+ flow {
+ id
+ isPinned
+ }
+ }
+ }
+`;
diff --git a/src/mocks/Flow.tsx b/src/mocks/Flow.tsx
index 64d370f16..36ba8d61e 100644
--- a/src/mocks/Flow.tsx
+++ b/src/mocks/Flow.tsx
@@ -21,6 +21,7 @@ import {
IMPORT_FLOW_LOCALIZATIONS,
ADD_FLOW_TO_WA_GROUP,
CREATE_FLOW,
+ PIN_FLOW,
} from 'graphql/mutations/Flow';
import { GET_ORGANIZATION_SERVICES } from 'graphql/queries/Organization';
import json from './ImportFlow.json';
@@ -753,3 +754,26 @@ export const createTagQuery = {
},
},
};
+
+export const pinFlowQuery = (updateFlowId: string, pin: boolean = false) => ({
+ request: {
+ query: PIN_FLOW,
+ variables: {
+ updateFlowId,
+ input: {
+ isPinned: pin,
+ },
+ },
+ },
+ result: {
+ data: {
+ updateFlow: {
+ errors: null,
+ flow: {
+ id: '2',
+ isPinned: pin,
+ },
+ },
+ },
+ },
+});