Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/assets/images/icons/AdvancedSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export const AdvancedSearchIcon = ({ isActive = false }: { isActive?: boolean })
const strokeColor = isActive ? 'none' : '#d9d9d9';
return (
<svg
data-testid="advanced-search-icon"
className="w-[16px] h-[16px] text-gray-800 dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
Expand Down
5 changes: 3 additions & 2 deletions src/components/UI/SearchBar/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface SearchBarProps {
endAdornment?: any;
searchMode: boolean;
iconFront?: boolean;
searchParam?:any
searchParam?: any
}

export const SearchBar = ({
Expand Down Expand Up @@ -70,6 +70,7 @@ export const SearchBar = ({
<InputAdornment position="end">
<Tooltip title="Filter">
<IconButton
data-testid="advanced-search-icon"
disableFocusRipple
aria-label="toggle password visibility"
onClick={(e: any) => {
Expand All @@ -78,7 +79,7 @@ export const SearchBar = ({
}}
className={styles.FilterIcon}
>
<AdvancedSearch isActive={searchParam && Object.keys(searchParam).length !== 0 } />
<AdvancedSearch isActive={searchParam && Object.keys(searchParam).length !== 0} />
</IconButton>
</Tooltip>
</InputAdornment>
Expand Down
48 changes: 14 additions & 34 deletions src/components/floweditor/FlowEditor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const templateFlowMocks = [...mocks, getTemplateFlow, resetFlowCount];
const manyKeywordsMocks = [...mocks, getFlowWithManyKeywords, resetFlowCount];

const wrapperFunction = (mocks: any) => (
<MockedProvider mocks={mocks} addTypename={false}>
<MockedProvider mocks={mocks}>
<Router>
<FlowEditor />
</Router>
Expand Down Expand Up @@ -216,9 +216,7 @@ test('start with a keyword message if the simulator opens in floweditor screen',
mockedAxios.post.mockImplementation(() => Promise.resolve({ data: {} }));
render(defaultWrapper);

await waitFor(() => {
expect(screen.findByText('help workflow'));
});
expect(await screen.findByText('help workflow')).toBeInTheDocument();

fireEvent.click(screen.getByTestId('previewButton'));

Expand All @@ -231,13 +229,9 @@ test.skip('if the flow the inactive', async () => {
mockedAxios.post.mockImplementation(() => Promise.resolve({ data: {} }));
render(wrapperFunction(inActiveFlowMocks));

await waitFor(() => {
expect(screen.findByText('help workflow'));
});
expect(await screen.findByText('help workflow')).toBeInTheDocument();
fireEvent.click(screen.getByTestId('previewButton'));
await waitFor(() => {
expect(screen.findByTestId('simulator-container'));
});
expect(await screen.findByTestId('simulator-container')).toBeInTheDocument();

await waitFor(() => {
expect(screen.getByTestId('simulator')).toHaveTextContent('Sorry, the flow is not active');
Expand All @@ -248,9 +242,7 @@ test('flow with no keywords', async () => {
mockedAxios.post.mockImplementation(() => Promise.resolve({ data: {} }));
render(wrapperFunction(noKeywordMocks));

await waitFor(() => {
expect(screen.findByText('help workflow'));
});
expect(await screen.findByText('help workflow')).toBeInTheDocument();
fireEvent.click(screen.getByTestId('previewButton'));

await waitFor(() => {
Expand All @@ -262,9 +254,7 @@ test('reset flow counts', async () => {
mockedAxios.post.mockImplementation(() => Promise.resolve({ data: {} }));
const { getByTestId, getByText } = render(wrapperFunction(noKeywordMocks));

await waitFor(() => {
expect(screen.findByText('help workflow'));
});
expect(await screen.findByText('help workflow')).toBeInTheDocument();

fireEvent.click(getByTestId('moreButton'));
fireEvent.click(getByText('Reset flow count'));
Expand All @@ -288,9 +278,7 @@ test('it translates the flow', async () => {
mockedAxios.post.mockImplementation(() => Promise.resolve({ data: {} }));
const { getByTestId, getByText } = render(defaultWrapper);

await waitFor(() => {
expect(screen.findByText('help workflow'));
});
expect(await screen.findByText('help workflow')).toBeInTheDocument();

fireEvent.click(screen.getByTestId('translateButton'));
fireEvent.click(getByTestId('cancel-button'));
Expand Down Expand Up @@ -321,9 +309,7 @@ test('template words should have template: prefix', async () => {
mockedAxios.post.mockImplementation(() => Promise.resolve({ data: {} }));
render(wrapperFunction(templateFlowMocks));

await waitFor(() => {
expect(screen.findByText('help workflow'));
});
expect(await screen.findByText('help workflow')).toBeInTheDocument();
fireEvent.click(screen.getByTestId('previewButton'));

await waitFor(() => {
Expand All @@ -335,19 +321,17 @@ test('if keywords are more than 8 it should be shown in a tooltip', async () =>
mockedAxios.post.mockImplementation(() => Promise.resolve({ data: {} }));
render(wrapperFunction(manyKeywordsMocks));

await waitFor(() => {
expect(screen.findByText('help, activity, preference, optout, stop, start, end, yes + 2 more'));
});
expect(
await screen.findByText(/help, activity, preference, optout, stop, start, end, yes/)
).toBeInTheDocument();
});

test('should export the flow', async () => {
const exportSpy = vi.spyOn(Utils, 'exportFlowMethod');
mockedAxios.post.mockImplementation(() => Promise.resolve({ data: {} }));
render(defaultWrapper);

await waitFor(() => {
expect(screen.findByText('help workflow'));
});
expect(await screen.findByText('help workflow')).toBeInTheDocument();

fireEvent.click(screen.getByTestId('moreButton'));
fireEvent.click(screen.getByText('Export flow'));
Expand All @@ -360,9 +344,7 @@ test('should export the flow', async () => {
test('should open the share responder dialog box', async () => {
render(defaultWrapper);

await waitFor(() => {
expect(screen.findByText('help workflow'));
});
expect(await screen.findByText('help workflow')).toBeInTheDocument();

fireEvent.click(screen.getByTestId('moreButton'));
fireEvent.click(screen.getByText('Share Responder Link'));
Expand All @@ -376,9 +358,7 @@ test('should show warning when no keywords are present and share responder link
const notificationSpy = vi.spyOn(Notification, 'setNotification');
render(wrapperFunction(noKeywordMocks));

await waitFor(() => {
expect(screen.findByText('help workflow'));
});
expect(await screen.findByText('help workflow')).toBeInTheDocument();

fireEvent.click(screen.getByTestId('moreButton'));
fireEvent.click(screen.getByText('Share Responder Link'));
Expand Down
72 changes: 29 additions & 43 deletions src/containers/Auth/Auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import setLogs from 'config/logs';
import { checkOrgStatus } from 'services/AuthService';
import { TERMS_OF_USE_LINK } from 'common/constants';

import { Promotion } from './Promotion/Promotion';
// import { Promotion } from './Promotion/Promotion';

export interface AuthProps {
pageTitle: string;
Expand Down Expand Up @@ -58,29 +58,27 @@ export const Auth = ({
loading: externalLoading,
inlineSuccessMessage,
}: AuthProps) => {
// handle visibility for the password field
const [showPassword, setShowPassword] = useState(false);
const [loading, setLoading] = useState(false);
const [internalLoading, setInternalLoading] = useState(false);
const loading = externalLoading !== undefined ? externalLoading : internalLoading;
const setLoading = setInternalLoading;
const { t } = useTranslation();
const [orgName, setOrgName] = useState('Glific');
const [status, setStatus] = useState('');

const isLoading = externalLoading !== undefined ? externalLoading : loading;

useEffect(() => {
if (mode === 'trialregistration') {
return;
}

axios
.post(ORGANIZATION_NAME)
.then(({ data }) => {
setOrgName(data?.data?.name);
setStatus(data?.data?.status);
})
.catch((error) => setLogs(`orgName error ${JSON.stringify(error)}`, error));
}, [mode]);
}, []);

useEffect(() => {
// Stop loading if any error
if (loading && errorMessage) {
setLoading(false);
}
Expand All @@ -94,7 +92,6 @@ export const Auth = ({
const boxTitleClass = [styles.BoxTitle];
let buttonClass = styles.AuthButton;
let buttonContainedVariant = true;

switch (mode) {
case 'login':
boxClass.push(styles.LoginBox);
Expand All @@ -106,13 +103,6 @@ export const Auth = ({
boxTitleClass.push(styles.RegistrationBoxTitle);
buttonClass = styles.AuthRegistrationButton;
break;
case 'trialregistration':
boxClass.push(styles.RegistrationBox);
boxClass.push(styles.TrialRegistrationBox);
boxTitleClass.push(styles.RegistrationBoxTitle);
buttonClass = styles.AuthButton;
buttonContainedVariant = true;
break;
case 'confirmotp':
boxClass.push(styles.OTPBox);
boxTitleClass.push(styles.RegistrationBoxTitle);
Expand All @@ -132,31 +122,33 @@ export const Auth = ({
}

const isRegistration = mode === 'registration';
const isTrialRegistration = mode === 'trialregistration';
const whatsAppIcon = <WhatsAppIcon className={styles.WhatsAppIcon} />;
const otpMessage = 'You will receive an OTP on your WhatsApp number';

let displayErrorMessage: any = null;
if (errorMessage) {
displayErrorMessage = <div className={styles.ErrorMessage}>{errorMessage}</div>;
}

let displayInlineSuccessMessage: any = null;
if (inlineSuccessMessage) {
displayInlineSuccessMessage = <div className={styles.SuccessMessage}>{inlineSuccessMessage}</div>;
}

const handlePasswordVisibility = () => {
setShowPassword(!showPassword);
};

// let's add the additonal password field info to the password field to handle
// visibility of the field
const passwordFieldAdditionalInfo = {
endAdornmentCallback: handlePasswordVisibility,
togglePassword: showPassword,
};

const handlePhone =
() =>
(value: string): void => {
initialFormValues.phone = value;
};


let formElements;
// we should not render form elements when displaying success message
if (!successMessage) {
formElements = (
<>
Expand All @@ -169,13 +161,14 @@ export const Auth = ({

<Formik
initialValues={initialFormValues}
enableReinitialize={true}
validationSchema={validationSchema}
onSubmit={(item) => {
setLoading(true);
saveHandler(item);
}}
>
{({ submitForm, values }) => (
{({ submitForm, values, setFieldValue }) => (
<div className={styles.CenterBox}>
<Form className={styles.Form}>
{formFields.map((field, index) => {
Expand All @@ -184,10 +177,9 @@ export const Auth = ({
fieldInfo = { ...field, ...passwordFieldAdditionalInfo };
}
if (field.type === 'phone') {
fieldInfo = { ...field, handlePhone };
fieldInfo = { ...field, handlePhone: () => (value: string) => { setFieldValue('phoneNumber', value); } };
}
const key = index;

return (
<div className={field.styles} key={key}>
{field.label ? (
Expand All @@ -201,17 +193,11 @@ export const Auth = ({
</div>
);
})}

{isTrialRegistration && inlineSuccessMessage && (
<div className={styles.SuccessMessageInline}>{inlineSuccessMessage}</div>
)}

{linkURL && (
<div className={styles.Link}>
<Link to={`/${linkURL}`}>{linkText}</Link>
</div>
)}

<div className={styles.CenterButton}>
{isRegistration ? (
<Captcha
Expand All @@ -226,7 +212,7 @@ export const Auth = ({
}}
className={buttonClass}
data-testid="SubmitButton"
loading={isLoading}
loading={loading}
action="register"
>
{buttonText}
Expand All @@ -239,17 +225,20 @@ export const Auth = ({
onClick={submitForm}
className={buttonClass}
data-testid="SubmitButton"
loading={isLoading}
type="button"
loading={loading}
>
{buttonText}
{!loading && buttonText}
</Button>
)}
</div>
{isRegistration && <div className={styles.InformationText}>{otpMessage}</div>}
{/* We neeed to add this submit button to enable form sumbitting when user hits enter
key. This is an workaround solution till the bug in formik or react is fixed. For
more info: https://github.com/formium/formik/issues/1418 */}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Typos in comment: "neeed" → "need", "sumbitting" → "submitting".

✏️ Fix
-                {/* We neeed to add this submit button to enable form sumbitting when user hits enter
-                key. This is an workaround solution till the bug in formik or react is fixed. For
+                {/* We need to add this submit button to enable form submitting when user hits enter
+                key. This is a workaround solution till the bug in formik or react is fixed. For
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/containers/Auth/Auth.tsx` around lines 241 - 243, Fix the typos in the
JSX comment inside the Auth component: change "neeed" to "need" and "sumbitting"
to "submitting" in the comment that explains why a hidden submit button is added
(the comment block near the form submit button inside the Auth component).
Ensure the wording now reads that the submit button is required to enable form
submitting when the user hits Enter and keep the reference URL unchanged.

<input className={styles.SubmitAction} type="submit" />
</Form>
{displayErrorMessage}
{displayInlineSuccessMessage}
</div>
)}
</Formik>
Expand All @@ -260,17 +249,14 @@ export const Auth = ({
}

return (
<div
className={`${styles.Container} ${isTrialRegistration ? styles.TrialContainer : ''}`}
data-testid="AuthContainer"
>
<div className={styles.Container} data-testid="AuthContainer">
<div className={styles.Auth}>
<div>
<img src={GlificLogo} className={styles.GlificLogo} alt="Glific" />
</div>
<hr className={styles.Break} />

{!isTrialRegistration && <div className={styles.OrganizationName}>{orgName}</div>}
<div className={styles.OrganizationName}>{orgName}</div>

<div className={boxClass.join(' ')}>
{formElements}
Expand All @@ -297,7 +283,7 @@ export const Auth = ({
) : null}
</div>

{mode === 'login' && <Promotion />}
{/* {mode === 'login' && <Promotion />} */}
</div>
);
};
18 changes: 18 additions & 0 deletions src/containers/Auth/ResetPassword/ResetPasswordConfirmOTP.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,22 @@ describe('<ResetPasswordConfirmOTP />', () => {
expect(sendOptMock).toHaveBeenCalledWith('919967665667');
});
});

test('it should show validation error if phone number is empty in state', async () => {
const WrapperEmpty = (
<MemoryRouter initialEntries={[{ state: { phoneNumber: '' } }]}>
<Routes>
<Route path="/" element={<ResetPasswordConfirmOTP />} />
</Routes>
</MemoryRouter>
);

render(WrapperEmpty);
const saveButton = screen.getByText('Save');
await user.click(saveButton);

await waitFor(() => {
expect(screen.getAllByText('Input required').length).toBeGreaterThan(0);
});
});
});
Loading