Skip to content

Commit

Permalink
Merge branch 'master' into removing-link
Browse files Browse the repository at this point in the history
  • Loading branch information
kurund authored Nov 25, 2024
2 parents 7c32768 + c4ae135 commit 28b6cb0
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 55 deletions.
28 changes: 12 additions & 16 deletions src/assets/images/icons/Pin/Active.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 2 additions & 3 deletions src/common/HelpData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ export const triggerInfo: HelpDataProps = {
};

export const searchInfo: HelpDataProps = {
heading:
'Glific provides search functionality to NGO staff to find contacts from a large set of contacts list.',
heading: 'Glific provides search functionality to NGO staff to find contacts from a large set of contacts list.',

link: 'https://glific.github.io/docs/docs/Product%20Features/Searches',
};
Expand Down Expand Up @@ -105,5 +104,5 @@ export const blockedContactsInfo: HelpDataProps = {
export const assistantsInfo: HelpDataProps = {
heading:
'Assistants can call OpenAI’s models with specific instructions to tune their personality and capabilities. Assistants can access multiple tools in parallel. Assistants can access files in several formats as part of their creation. When using tools, Assistants can also create files (e.g., images, spreadsheets, etc) and cite files they reference in the Messages they create.',
link: 'https://glific.github.io/docs/docs/Product%20Features/Flows/Flow%20Variables/Flow%20variables%20vs%20Contact%20variables', // Replace with the actual Glific documentation link
link: 'https://glific.github.io/docs/docs/Integrations/RAG%20using%20OpenAI%20file%20search%20assistant', // Replace with the actual Glific documentation link
};
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ interface AssistantOptionsProps {

const temperatureInfo =
'Controls randomness: Lowering results in less random completions. As the temperature approaches zero, the model will become deterministic and repetitive.';

const filesInfo =
'Enables the assistant with knowledge from files that you or your users upload. Once a file is uploaded, the assistant automatically decides when to retrieve content based on user requests.';
export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOptionsProps) => {
const [showUploadDialog, setShowUploadDialog] = useState(false);
const [files, setFiles] = useState<any[]>([]);
Expand Down Expand Up @@ -171,6 +172,11 @@ export const AssistantOptions = ({ currentId, options, setOptions }: AssistantOp
<div className={styles.FilesHeader}>
<Typography variant="subtitle2" className={styles.Label} data-testid="inputLabel">
Files
<HelpIcon
helpData={{
heading: filesInfo,
}}
/>
</Typography>
<Button data-testid="addFiles" onClick={() => setShowUploadDialog(true)} variant="outlined">
<AddIcon />
Expand Down
27 changes: 26 additions & 1 deletion src/containers/Flow/FlowList/FlowList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
exportFlow,
releaseFlow,
filterTemplateFlows,
pinFlowQuery,
} from 'mocks/Flow';
import { getOrganizationQuery } from 'mocks/Organization';
import testJSON from 'mocks/ImportFlow.json';
Expand All @@ -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 };

Expand All @@ -44,6 +46,8 @@ const mocks = [
getRoleNameQuery,
getFlowCountQuery({ isTemplate: true }),
filterTemplateFlows,
pinFlowQuery('2', true),
pinFlowQuery('1'),
...getOrganizationQuery,
];

Expand All @@ -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('<FlowList />', () => {
test('should render Flow', async () => {
Expand All @@ -79,7 +84,7 @@ describe('<FlowList />', () => {
});
});

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
Expand Down Expand Up @@ -193,6 +198,26 @@ describe('<FlowList />', () => {
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', () => {
Expand Down
98 changes: 64 additions & 34 deletions src/containers/Flow/FlowList/FlowList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -34,17 +36,13 @@ const getName = (text: string, keywordsList: any, roles: any) => {
{text}
<br />
<span className={styles.Keyword}>{keywords.join(', ')}</span>
{hasDynamicRole && (
<span className={styles.Roles}>{accessRoles && accessRoles.join(', ')} </span>
)}
{hasDynamicRole && <span className={styles.Roles}>{accessRoles && accessRoles.join(', ')} </span>}
</div>
);
};

const getDate = (date: string, fallback: string = '') => (
<div className={styles.LastPublished}>
{date ? dayjs(date).format(STANDARD_DATE_TIME_FORMAT) : fallback}
</div>
<div className={styles.LastPublished}>{date ? dayjs(date).format(STANDARD_DATE_TIME_FORMAT) : fallback}</div>
);

const getLastPublished = (date: string, fallback: string = '') =>
Expand All @@ -55,21 +53,7 @@ const getLastPublished = (date: string, fallback: string = '') =>
);
const getLabel = (tag: any) => <div className={styles.LabelButton}>{tag.label}</div>;

const displayPinned = (isPinned: boolean) => {
if (isPinned) {
return <PinIcon />;
}
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 = <FlowIcon className={styles.FlowIcon} />;

const queries = {
Expand All @@ -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);

Expand Down Expand Up @@ -122,6 +107,8 @@ export const FlowList = () => {
},
});

const [updatePinned] = useMutation(PIN_FLOW);

const handleCopy = (id: any) => {
navigate(`/flow/${id}/edit`, { state: 'copy' });
};
Expand All @@ -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 (
<Tooltip title={'Unpin'} placement={'top-start'}>
<IconButton data-testid="unpin-button" onClick={() => handlePin(id)}>
<ActivePinIcon />
</IconButton>
</Tooltip>
);
}
return (
<Tooltip title={'Pin'} placement={'top-start'}>
<IconButton data-testid="pin-button" onClick={() => handlePin(id, true)}>
<PinIcon />
</IconButton>
</Tooltip>
);
};

if (importStatus.length > 0) {
dialog = (
<DialogBox
Expand Down Expand Up @@ -206,16 +243,8 @@ export const FlowList = () => {

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) : '',
Expand Down Expand Up @@ -360,6 +389,7 @@ export const FlowList = () => {
filterList={activeFilter}
loadingList={importing}
restrictedAction={restrictedAction}
refreshList={refreshList}
/>
</>
);
Expand Down
15 changes: 15 additions & 0 deletions src/graphql/mutations/Flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
`;
24 changes: 24 additions & 0 deletions src/mocks/Flow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
},
},
},
},
});

0 comments on commit 28b6cb0

Please sign in to comment.