Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accounts team/accounts package ts migration/sprint 6 #101

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a9b314f
translations: 📚 sync translations with crowdin (#11678)
github-actions[bot] Nov 22, 2023
08df687
translations: 📚 sync translations with crowdin (#11681)
github-actions[bot] Nov 22, 2023
76452bd
Merge pull request #100 from amina-deriv/shaheer/sprint6_ts_migration…
shaheer-deriv Nov 22, 2023
befa74c
[CFDS] [Refactoring] Hamza / Remove the hard-coded variables and repl…
hamza-deriv Nov 23, 2023
9ffa897
[WALL]/ Sergei / wall 2698 / create sketch of change investor passwor…
sergei-deriv Nov 23, 2023
9d2cc67
[WALL] Fix POA screen switching condition and POI status hook (#11687)
adrienne-deriv Nov 23, 2023
a2a738d
[COJ] | wall-2430, fix for trading assessment form (showing the error…
yauheni-deriv Nov 23, 2023
855b87c
Likhith/wall 1714/enhance error message handling for poi (#10711)
likhith-deriv Nov 23, 2023
ba632d7
Maryia/BOT-905/Quick Strategy minor bug fixes and improvements (#11116)
maryia-matskevich-deriv Nov 23, 2023
1b4ee92
fix: fiat onramp currency select (#11685)
lubega-deriv Nov 23, 2023
424a164
[WALL] Farhan/WALL-2152/Manual Upload Flow (#11667)
farhan-nurzi-deriv Nov 23, 2023
b68ddfc
[WALL] Aizad/WALL-2637/password-validation-issue (#11657)
aizad-deriv Nov 23, 2023
115b75d
Revert "Likhith/wall 1714/enhance error message handling for poi (#10…
likhith-deriv Nov 23, 2023
bbda6f6
translations: 📚 sync translations with crowdin (#11695)
github-actions[bot] Nov 23, 2023
3ce853c
translations: 📚 sync translations with crowdin (#11698)
github-actions[bot] Nov 23, 2023
532d47b
translations: 📚 sync translations with crowdin (#11700)
github-actions[bot] Nov 23, 2023
6be525a
[WALL] Add creation MT5 account (#11699)
adrienne-deriv Nov 23, 2023
d6e8292
translations: 📚 sync translations with crowdin (#11701)
github-actions[bot] Nov 23, 2023
06ed8d6
translations: 📚 sync translations with crowdin (#11707)
github-actions[bot] Nov 23, 2023
c200a94
[FEQ] Add websocketurl to timeout (#11249)
markw-deriv Nov 23, 2023
1557c8e
aizad/chore: Make ChangePassword component change its title based on …
aizad-deriv Nov 23, 2023
2d268d1
[Refactoring] shontzu/CFDS-1435/use-landing-company-hooks-for-cTrader…
shontzu-deriv Nov 23, 2023
9c0a15f
Merge branch 'binary-com:master' into accounts_team/accounts_package_…
shaheer-deriv Nov 23, 2023
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
24 changes: 23 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -505,11 +505,6 @@ const PersonalDetailsForm = props => {
legal_entity_name: getLegalEntityName('maltainvest'),
}
)}
renderlabel={title => (
<Text size='xs' line_height='s'>
{title}
</Text>
)}
withTabIndex={0}
data-testid='tax_identification_confirm'
has_error={
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import classNames from 'classnames';
import React from 'react';
import { Formik, Form } from 'formik';
import { Form, Formik } from 'formik';
import { Button, Modal, Text } from '@deriv/components';
import { isEmptyObject, isMobile } from '@deriv/shared';
import { isMobile } from '@deriv/shared';
import { localize, Localize } from '@deriv/translations';
import { MAX_QUESTION_TEXT_LENGTH } from '../../Constants/trading-assessment';
import ScrollToFieldWithError from '../forms/scroll-to-field-with-error';
Expand Down Expand Up @@ -104,10 +104,14 @@ const TradingAssessmentForm = ({

const isAssessmentCompleted = answers => Object.values(answers).every(answer => Boolean(answer));

const nextButtonHandler = values => {
const nextButtonHandler = (values, { setTouched }) => {
if (is_section_filled) {
if (isAssessmentCompleted(values) && stored_items === last_question_index) onSubmit(values);
else displayNextPage();
if (isAssessmentCompleted(values) && stored_items === last_question_index) {
onSubmit(values);
} else {
setTouched({});
displayNextPage();
}
}
};

Expand Down Expand Up @@ -159,13 +163,13 @@ const TradingAssessmentForm = ({
<Localize i18n_default_text='In providing our services to you, we are required to obtain information from you in order to assess whether a given product or service is appropriate for you.' />
</Text>
<Formik initialValues={{ ...form_value }} validate={handleValidate} onSubmit={nextButtonHandler}>
{({ errors, setFieldValue, values }) => {
{({ errors, setFieldValue, values, setErrors, touched }) => {
const { question_text, form_control, answer_options, questions } =
current_question_details.current_question;
const has_long_question = questions?.some(
question => question.question_text.length > MAX_QUESTION_TEXT_LENGTH
);

const is_section_required = Object.keys(values).some(field => !!errors[field] && !!touched[field]);
return (
<React.Fragment>
<Text weight='bold' size='xs' className='trading-assessment__question-counter'>
Expand All @@ -177,7 +181,7 @@ const TradingAssessmentForm = ({
}}
/>
<Text color='loss-danger' size='xxs'>
* {!isEmptyObject(errors) && <Localize i18n_default_text={'This is required'} />}
{is_section_required && <Localize i18n_default_text={'* This is required'} />}
</Text>
</Text>
<section className={'trading-assessment__form'}>
Expand Down Expand Up @@ -220,7 +224,10 @@ const TradingAssessmentForm = ({
{should_display_previous_button && (
<Button
has_effect
onClick={displayPreviousPage}
onClick={() => {
setErrors({});
displayPreviousPage();
}}
text={localize('Previous')}
type='button'
secondary
Expand Down
1 change: 1 addition & 0 deletions packages/api/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export { default as useAllAvailableAccounts } from './useAllAvailableAccounts';
export { default as useAllWalletAccounts } from './useAllWalletAccounts';
export { default as useAuthentication } from './useAuthentication';
export { default as useAuthorize } from './useAuthorize';
export { default as useAccesiblePlatforms } from './useAccesiblePlatforms';
export { default as useAvailableMT5Accounts } from './useAvailableMT5Accounts';
export { default as useAvailableWallets } from './useAvailableWallets';
export { default as useBalance } from './useBalance';
Expand Down
32 changes: 32 additions & 0 deletions packages/api/src/hooks/useAccesiblePlatforms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useMemo } from 'react';
import useLandingCompany from './useLandingCompany';

/**
* A custom hook that provides flags to determine the accessibility status of cTrader and Dxtrade based on the current country of residence.
*/
const useAccesiblePlatforms = () => {
const { data: landing_company, ...rest } = useLandingCompany();

const modified_accesible_platform = useMemo(() => {
if (!landing_company) return;

/** check if ctrader jurisdiction is offered in the landing_company response */
const is_ctrader_available = landing_company?.ctrader?.all?.standard === 'svg';
/** check if dxtrade is in the landing_company response */
const is_dxtrade_available = landing_company?.dxtrade_all_company;

return {
/** is ctrader accessible for this country of residence */
is_ctrader_available: !!is_ctrader_available,
/** is dxtrade accessible for this country of residence */
is_dxtrade_available: !!is_dxtrade_available,
};
}, [landing_company]);

return {
data: modified_accesible_platform,
...rest,
};
};

export default useAccesiblePlatforms;
4 changes: 2 additions & 2 deletions packages/api/src/hooks/useAuthentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ const useAuthentication = () => {
is_poi_name_mismatch: account_status.has('poi_name_mismatch'),
/** client's name in POA documents does not match */
is_poa_address_mismatch: account_status.has('poa_address_mismatch'),
/** client has attempted POI before */
has_poa_been_attempted: get_account_status_data?.authentication?.document?.status !== 'none',
/** client has attempted POA before */
has_poa_been_attempted: get_account_status_data?.authentication?.document?.status !== 'none',
/** client has attempted POI before */
has_poi_been_attempted: get_account_status_data?.authentication?.identity?.status !== 'none',
/** client's poi verification status */
poi_status: get_account_status_data?.authentication?.identity?.status,
Expand Down
55 changes: 22 additions & 33 deletions packages/api/src/hooks/useDocumentUpload.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useCallback, useMemo, useState } from 'react';
import useMutation from '../useMutation';
import { compressImageFile, generateChunks, numToUint8Array, readFile } from '../utils';
import md5 from 'md5';
import { getActiveWebsocket } from '../APIProvider';

type TDocumentUploadPayload = Parameters<ReturnType<typeof useMutation<'document_upload'>>['mutate']>[0]['payload'];
type TUploadPayload = Omit<TDocumentUploadPayload, 'expected_checksum' | 'file_size'> & {
type TUploadPayload = Omit<TDocumentUploadPayload, 'document_format' | 'expected_checksum' | 'file_size'> & {
file?: File;
};

Expand All @@ -15,36 +15,19 @@ const useDocumentUpload = () => {
data,
isLoading: _isLoading,
isSuccess: _isSuccess,
mutate,
mutateAsync,
status,
...rest
} = useMutation('document_upload');
const [tempChunks, setTempChunks] = useState<Uint8Array[]>([]);
const [isDocumentUploaded, setIsDocumentUploaded] = useState(false);
const activeWebSocket = getActiveWebsocket();

const isLoading = useMemo(() => _isLoading || !isDocumentUploaded, [_isLoading, isDocumentUploaded]);
const isSuccess = useMemo(() => _isSuccess && isDocumentUploaded, [_isSuccess, isDocumentUploaded]);

const sendChunks = useCallback(
(chunks: Uint8Array[]) => {
const id = numToUint8Array(data?.document_upload?.upload_id || 0);
const type = numToUint8Array(data?.document_upload?.call_type || 0);

chunks.forEach(chunk => {
const size = numToUint8Array(chunk.length);
const payload = new Uint8Array([...type, ...id, ...size, ...chunk]);
activeWebSocket?.send(payload);
});
setIsDocumentUploaded(true);
},
[data, activeWebSocket]
);
const isLoading = _isLoading || (!isDocumentUploaded && status === 'success');
const isSuccess = _isSuccess && isDocumentUploaded;

const upload = useCallback(
async (payload: TUploadPayload) => {
if (!payload?.file) return Promise.reject(new Error('No file selected'));
setIsDocumentUploaded(false);
const file = payload.file;
delete payload.file;
const fileBlob = await compressImageFile(file);
Expand All @@ -55,26 +38,32 @@ const useDocumentUpload = () => {

const updatedPayload = {
...payload,
document_format: file.type
.split('/')[1]
.toLocaleUpperCase() as TDocumentUploadPayload['document_format'],
expected_checksum: checksum,
file_size: fileBuffer.length,
passthrough: {
document_upload: true,
},
};
const chunks = generateChunks(fileBuffer, { chunkSize: 16384 });
setTempChunks(chunks);
await mutate({ payload: updatedPayload });
setIsDocumentUploaded(false);
await mutateAsync({ payload: updatedPayload }).then(async res => {
const chunks = generateChunks(fileBuffer, {});
const id = numToUint8Array(res?.document_upload?.upload_id || 0);
const type = numToUint8Array(res?.document_upload?.call_type || 0);

chunks.forEach(chunk => {
const size = numToUint8Array(chunk.length);
const payload = new Uint8Array([...type, ...id, ...size, ...chunk]);
activeWebSocket?.send(payload);
});
setIsDocumentUploaded(true);
});
},
[mutate]
[activeWebSocket, mutateAsync]
);

useEffect(() => {
if (status === 'success' && tempChunks.length && data) {
sendChunks(tempChunks);
setTempChunks([]);
}
}, [data, status, sendChunks, tempChunks]);

const modified_response = useMemo(() => ({ ...data?.document_upload }), [data?.document_upload]);

return {
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/hooks/usePOI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const usePOI = () => {
const user_country_code = get_settings_data?.citizen || get_settings_data?.country_code;
const matching_residence_data = residence_list_data?.find(r => r.value === user_country_code);
const is_idv_supported = matching_residence_data?.identity?.services?.idv?.is_country_supported;
const is_onfido_supported = matching_residence_data?.identity?.services?.onfido?.documents_supported;
const is_onfido_supported = matching_residence_data?.identity?.services?.onfido?.is_country_supported;
const services = authentication_data?.identity?.services;
const idv_submission_left = services?.idv?.submissions_left ?? 0;
const onfido_submission_left = services?.onfido?.submissions_left ?? 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@ jest.mock('@deriv/bot-skeleton/src/scratch/dbot', () => jest.fn());
describe('<QSInput />', () => {
let wrapper: ({ children }: { children: JSX.Element }) => JSX.Element, mock_DBot_store: RootStore | undefined;
const onChange = jest.fn();

beforeEach(() => {
const mock_store = mockStore({
ui: {
is_mobile: true,
},
});

mock_DBot_store = mockDBotStore(mock_store, mock_ws);
const mock_onSubmit = jest.fn();

const initial_value = {
duration: 1,
size: 0,
};

wrapper = ({ children }: { children: JSX.Element }) => (
Expand All @@ -49,36 +53,45 @@ describe('<QSInput />', () => {
const { container } = render(<QSInput onChange={onChange} name='duration' />, {
wrapper,
});

expect(container).toBeInTheDocument();
});

it('should increase the value on click of + button', () => {
it('should increase the value on click of "+" button', () => {
render(<QSInput name='duration' type='number' onChange={onChange} />, {
wrapper,
});

const increase_button = screen.getByTestId('qs-input-increase');
userEvent.click(increase_button);
expect(onChange).toBeCalledWith('duration', '2');

const input_increase = screen.getByTestId('qs-input') as HTMLInputElement;
const input_value = input_increase.value;
expect(input_value).toEqual('2');
});

it('should decrease the value on click of - button', () => {
it('should decrease the value on click of "-" button', () => {
render(<QSInput name='duration' type='number' onChange={onChange} />, {
wrapper,
});

const decrease_button = screen.getByTestId('qs-input-decrease');
userEvent.click(decrease_button);
expect(onChange).toBeCalledWith('duration', '0');

const input_decrease = screen.getByTestId('qs-input') as HTMLInputElement;
const input_value = input_decrease.value;

expect(input_value).toEqual('0');
});

it('should update the value', () => {
it('the onMouseLeave handler sets the focus to false', () => {
render(<QSInput name='duration' type='number' onChange={onChange} />, {
wrapper,
});

const input = screen.getByTestId('qs-input');
userEvent.type(input, '5');
expect(onChange).toBeCalledWith('duration', 15);
const container_element = screen.getByTestId('qs-input-container');
userEvent.unhover(container_element);

expect(container_element).not.toHaveClass('no-focus');
});
});
Loading
Loading