Skip to content

Commit

Permalink
Merge pull request #2860 from glific/regx
Browse files Browse the repository at this point in the history
Add settings UI to add regex flow triggers
  • Loading branch information
kurund authored May 15, 2024
2 parents b488524 + ebd4e9e commit 4636568
Show file tree
Hide file tree
Showing 13 changed files with 151 additions and 58 deletions.
5 changes: 1 addition & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import gqlClient from 'config/apolloclient';
import { SessionContext, SideDrawerContext } from 'context/session';
import ErrorHandler from 'containers/ErrorHandler/ErrorHandler';
import { Loading } from 'components/UI/Layout/Loading/Loading';
import {
getAuthSession,
checkAuthStatusService,
} from 'services/AuthService';
import { getAuthSession, checkAuthStatusService } from 'services/AuthService';
import { UnauthenticatedRoute } from 'routes/UnauthenticatedRoute/UnauthenticatedRoute';
import { AuthenticatedRoute } from 'routes/AuthenticatedRoute/AuthenticatedRoute';
import { Logout } from 'containers/Auth/Logout/Logout';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ const addAttachment = (attachmentType = '', attachmentURL = '') => {
};

vi.mock('common/notification', async (importOriginal) => {
const mod = await importOriginal<typeof import('common/notification')>()
const mod = await importOriginal<typeof import('common/notification')>();
return {
...mod,
setNotification: vi.fn((...args) => { return args[1] }),
}
})
setNotification: vi.fn((...args) => args[1]),
};
});

beforeEach(() => {
mockedAxios.get.mockImplementation(() =>
Expand Down Expand Up @@ -134,9 +134,11 @@ test('should get error notification if uploading media fails', async () => {
attachmentType: 'IMAGE',
uploadPermission: true,
};
render(<MockedProvider mocks={[uploadMediaErrorMock]}>
<AddAttachment {...defaultProps} />
</MockedProvider>)
render(
<MockedProvider mocks={[uploadMediaErrorMock]}>
<AddAttachment {...defaultProps} />
</MockedProvider>
);

const file = { name: 'photo.png' };

Expand All @@ -145,7 +147,7 @@ test('should get error notification if uploading media fails', async () => {
await waitFor(() => {
expect(setNotification).toHaveReturnedWith('warning');
});
})
});

test('successful media submission', async () => {
const responseData = { data: { is_valid: true } };
Expand All @@ -156,15 +158,17 @@ test('successful media submission', async () => {

// let url validation complete
await waitFor(() => {
expect(screen.queryByText('Please wait for the attachment URL verification')).not.toBeInTheDocument();
expect(
screen.queryByText('Please wait for the attachment URL verification')
).not.toBeInTheDocument();
});

await waitFor(() => {
fireEvent.click(screen.getByTestId('ok-button'));
})
});
await waitFor(() => {
expect(setAttachmentType).toHaveBeenCalledWith('IMAGE');
expect(setAttachmentURL).toHaveBeenCalledWith('https://glific.com')
expect(setAttachmentURL).toHaveBeenCalledWith('https://glific.com');
expect(setAttachment).toHaveBeenCalledWith(false);
})
})
});
});
8 changes: 4 additions & 4 deletions src/containers/Chat/ChatMessages/ChatMessages.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@
margin-right: 13px !important;
}

.Paper>ul>li:hover {
.Paper > ul > li:hover {
background-color: #edf6f2 !important;
}

.Paper>ul>li {
.Paper > ul > li {
color: #073f24;
}

Expand Down Expand Up @@ -131,7 +131,7 @@ html {
align-items: center;
}

.SessionTimer+div {
.SessionTimer + div {
text-align: right;
}

Expand All @@ -140,4 +140,4 @@ html {
font-size: 12px;
font-weight: 400;
margin-right: 5px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@

.StatusTitle {
font-weight: bold;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@
font-size: 15px;
font-weight: 400;
line-height: 20px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ const contactManagement = (

// let's mock setNotification function that's called upon successful upload
vi.mock('common/notification', async (importOriginal) => {
const mod = await importOriginal<typeof import('common/notification')>()
const mod = await importOriginal<typeof import('common/notification')>();
return {
...mod,
setNotification: vi.fn(),
}
})
};
});

test('Admin contact management form renders correctly', async () => {
render(contactManagement);
Expand All @@ -48,18 +48,20 @@ test('the page should have a disabled upload button by default', async () => {
expect(uploadButton).toHaveAttribute('disabled');
});

test('Files other than .csv should raise a warning message upon upload', async() => {
test('Files other than .csv should raise a warning message upon upload', async () => {
render(contactManagement);

const nonCSVFile = new File(['This is not a CSV File'], 'test.pdf', {type: 'application/pdf'});
const nonCSVFile = new File(['This is not a CSV File'], 'test.pdf', { type: 'application/pdf' });
await waitFor(() => {
const fileInput = screen.getByTestId('uploadFile');
userEvent.upload(fileInput, nonCSVFile);
})
});
await waitFor(() => {
expect(screen.getByText(/Please make sure the file format matches the sample/)).toBeInTheDocument();
})
})
expect(
screen.getByText(/Please make sure the file format matches the sample/)
).toBeInTheDocument();
});
});

test('Success Notification should be called upon successful CSV upload', async () => {
render(contactManagement);
Expand All @@ -68,21 +70,21 @@ test('Success Notification should be called upon successful CSV upload', async (
const csvContent = `name,phone,collection
John Doe,919876543210,"Optin collection,Optout Collection"
Virat Kohli,919876543220,Cricket`;
const file = new File([csvContent], 'test.csv', {type: 'text/csv'});
const file = new File([csvContent], 'test.csv', { type: 'text/csv' });

await waitFor(() => {
const fileInput = screen.getByTestId('uploadFile');
userEvent.upload(fileInput, file);
})
});
await waitFor(() => {
// the filename should be visible instead of Select .csv after upload
expect(screen.getByText('test.csv')).toBeInTheDocument();
})
});

const uploadBtn = screen.getByTestId('uploadButton');
userEvent.click(uploadBtn)
userEvent.click(uploadBtn);

await waitFor(() => {
expect(setNotification).toHaveBeenCalled();
})
})
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,18 @@ test('Upload contact dialog renders correctly', async () => {
});
});

test('Files other than .csv should raise a warning message upon upload', async() => {
test('Files other than .csv should raise a warning message upon upload', async () => {
render(dialogBox);

const nonCSVFile = new File(['This is not a CSV File'], 'test.pdf', {type: 'application/pdf'});
const nonCSVFile = new File(['This is not a CSV File'], 'test.pdf', { type: 'application/pdf' });
await waitFor(() => {
const fileInput = screen.getByTestId('uploadFile');
userEvent.upload(fileInput, nonCSVFile);
})
});
await waitFor(() => {
expect(screen.getByTestId('invalidCsvFormat')).toBeInTheDocument();
})
})
});
});

test('Should be able to upload valid CSV', async () => {
render(dialogBox);
Expand All @@ -57,14 +57,14 @@ test('Should be able to upload valid CSV', async () => {
const csvContent = `name,phone,collection
John Doe,919876543210,"Optin collection,Optout Collection"
Virat Kohli,919876543220,Cricket`;
const file = new File([csvContent], 'test.csv', {type: 'text/csv'});
const file = new File([csvContent], 'test.csv', { type: 'text/csv' });

await waitFor(() => {
const fileInput = screen.getByTestId('uploadFile');
userEvent.upload(fileInput, file);
})
});
await waitFor(() => {
// the filename should be visible instead of Select .csv after upload
expect(screen.getByText('test.csv')).toBeInTheDocument();
})
})
});
});
82 changes: 80 additions & 2 deletions src/containers/SettingList/OrganizationFlows/OrganizationFlows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Checkbox } from 'components/UI/Form/Checkbox/Checkbox';
import { TimePicker } from 'components/UI/Form/TimePicker/TimePicker';
import { Loading } from 'components/UI/Layout/Loading/Loading';
import { AutoComplete } from 'components/UI/Form/AutoComplete/AutoComplete';
import { Input } from 'components/UI/Form/Input/Input';
import { FormLayout } from 'containers/Form/FormLayout';
import { GET_FLOWS } from 'graphql/queries/Flow';
import { GET_ORGANIZATION, USER_LANGUAGES } from 'graphql/queries/Organization';
Expand Down Expand Up @@ -49,6 +50,10 @@ export const OrganizationFlows = () => {
const [organizationId, setOrganizationId] = useState(null);
const [newcontactFlowId, setNewcontactFlowId] = useState(null);
const [newcontactFlowEnabled, setNewcontactFlowEnabled] = useState(false);
const [regxFlowId, setRegxFlowId] = useState(null);
const [regxFlowExpr, setRegxFlowExpr] = useState<string>('');
const [regxFlowOpt, setRegxFlowOpt] = useState<string>('');
const [regxFlowEnabled, setRegxFlowEnabled] = useState(false);
const [optinFlowId, setOptinFlowId] = useState(null);
const [optinFlowEnabled, setOptinFlowEnabled] = useState(false);
const [allDayCheck, setAllDayCheck] = useState(false);
Expand All @@ -62,10 +67,14 @@ export const OrganizationFlows = () => {
enabledDays,
defaultFlowId,
flowId,
optinFlowId,
newcontactFlowId,
newcontactFlowEnabled,
regxFlowId,
regxFlowExpr,
regxFlowOpt,
regxFlowEnabled,
optinFlowId,
optinFlowEnabled,
newcontactFlowId,
allDayCheck,
};

Expand Down Expand Up @@ -97,6 +106,7 @@ export const OrganizationFlows = () => {
const setStates = ({
outOfOffice: outOfOfficeValue,
newcontactFlowId: newcontactFlowIdValue,
regxFlow: regxFlowValue,
optinFlowId: optinFlowIdValue,
}: any) => {
setHours(outOfOfficeValue.enabled);
Expand All @@ -115,6 +125,12 @@ export const OrganizationFlows = () => {
setNewcontactFlowEnabled(true);
setNewcontactFlowId(getFlow(newcontactFlowIdValue));
}
if (regxFlowValue) {
setRegxFlowEnabled(true);
setRegxFlowId(getFlow(regxFlowValue.flowId));
setRegxFlowExpr(regxFlowValue.regx);
setRegxFlowOpt(regxFlowValue.regxOpt);
}
if (optinFlowIdValue) {
setOptinFlowEnabled(true);
setOptinFlowId(getFlow(optinFlowIdValue));
Expand Down Expand Up @@ -203,6 +219,19 @@ export const OrganizationFlows = () => {
is: (val: string) => val,
then: (schema) => schema.nullable().required(t('Optin flow is required.')),
}),
regxFlowId: Yup.object()
.nullable()
.when('regxFlowEnabled', {
is: (val: string) => val,
then: (schema) => schema.nullable().required(t('Regular expression flow is required.')),
}),
regxFlowExpr: Yup.string()
.nullable()
.when('regxFlowEnabled', {
is: (val: string) => val,
then: (schema) => schema.nullable().required(t('Regular expression is required.')),
}),
regxFlowOpt: Yup.string().nullable(),
};

const FormSchema = Yup.object().shape(validation);
Expand Down Expand Up @@ -316,6 +345,45 @@ export const OrganizationFlows = () => {
disabled: !optinFlowEnabled,
label: t('Select flow'),
},

{
component: Checkbox,
name: 'regxFlowEnabled',
className: styles.Checkbox,
title: (
<Typography className={styles.CheckboxLabel}>{t('Regular expression flow')}</Typography>
),
handleChange: setRegxFlowEnabled,
},
{
component: AutoComplete,
name: 'regxFlowId',
options: flow.flows,
optionLabel: 'name',
multiple: false,
disabled: !regxFlowEnabled,
label: t('Select flow'),
helperText: t(
'The selected flow will trigger when end-users aren’t in any flow, their message doesn’t match any keyword, and their message matches the regular expression defined below.'
),
},
{
component: Input,
name: 'regxFlowExpr',
type: 'text',
label: t('Regular expression'),
disabled: !regxFlowEnabled,
helperText: t(
'A pattern that the regular expression engine attempts to match in input text.'
),
},
{
component: Input,
name: 'regxFlowOpt',
type: 'text',
label: t('Regular expression modifiers'),
disabled: !regxFlowEnabled,
},
];

const assignDays = (enabledDay: any) => {
Expand Down Expand Up @@ -344,6 +412,7 @@ export const OrganizationFlows = () => {
// set active Language Ids
let newContactFlowId = null;
let optinFlowId = null;
let regxFlow = null;

if (newcontactFlowEnabled) {
newContactFlowId = payload.newcontactFlowId.id;
Expand All @@ -352,6 +421,14 @@ export const OrganizationFlows = () => {
if (optinFlowEnabled) {
optinFlowId = payload.optinFlowId.id;
}

if (regxFlowEnabled) {
regxFlow = {
flowId: payload.regxFlowId.id,
regx: payload.regxFlowExpr,
regxOpt: payload.regxFlowOpt,
};
}
object = {
outOfOffice: {
defaultFlowId: payload.defaultFlowId ? payload.defaultFlowId.id : null,
Expand All @@ -361,6 +438,7 @@ export const OrganizationFlows = () => {
flowId: payload.flowId ? payload.flowId.id : null,
startTime: dayjs(payload.startTime).format(EXTENDED_TIME_FORMAT),
},
regxFlow: regxFlow,
newcontactFlowId: newContactFlowId,
optinFlowId,
};
Expand Down
2 changes: 1 addition & 1 deletion src/graphql/mutations/Tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const CREATE_LABEL = gql`

export const UPDATE_TAG = gql`
mutation UpdateTag($id: ID!, $input: TagInput!) {
updateTag(id: $id,input: $input) {
updateTag(id: $id, input: $input) {
tag {
label
}
Expand Down
Loading

0 comments on commit 4636568

Please sign in to comment.