Skip to content

Commit da911e5

Browse files
committed
Merge branch 'master' into dhruv/amina_mt5_defaulting_jurisdiction
2 parents 4f2197a + 4db2d1c commit da911e5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1430
-276
lines changed

packages/bot-web-ui/package.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -71,25 +71,27 @@
7171
"webpack-cli": "^4.7.2"
7272
},
7373
"dependencies": {
74+
"@deriv-com/quill-ui": "1.18.0",
7475
"@deriv-com/ui": "1.36.4",
7576
"@deriv/api": "^1.0.0",
7677
"@deriv/api-types": "1.0.172",
7778
"@deriv/bot-skeleton": "^1.0.0",
7879
"@deriv/components": "^1.0.0",
79-
"@deriv/hooks": "^1.0.0",
8080
"@deriv/deriv-charts": "^2.5.1",
81+
"@deriv/hooks": "^1.0.0",
82+
"@deriv/quill-icons": "1.23.3",
8183
"@deriv/shared": "^1.0.0",
8284
"@deriv/stores": "^1.0.0",
8385
"@deriv/translations": "^1.0.0",
84-
"@deriv/quill-icons": "1.23.14",
85-
"blockly": "^10.4.3",
8686
"@testing-library/user-event": "^13.5.0",
8787
"@types/react-router-dom": "^5.1.6",
88+
"blockly": "^10.4.3",
8889
"classnames": "^2.2.6",
8990
"crc-32": "^1.2.0",
9091
"dompurify": "^3.1.0",
9192
"formik": "^2.1.4",
9293
"gh-pages": "^6.1.1",
94+
"html-react-parser": "5.1.12",
9395
"immutable": "^3.8.2",
9496
"lodash.debounce": "^4.0.8",
9597
"lz-string": "^1.4.4",
@@ -105,7 +107,6 @@
105107
"react-router-dom": "^5.2.0",
106108
"react-toastify": "^9.1.3",
107109
"react-transition-group": "4.4.2",
108-
"html-react-parser": "5.1.12",
109110
"yup": "^0.32.11"
110111
}
111112
}

packages/bot-web-ui/src/constants/bot-contents.ts

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export const DBOT_TABS: TDashboardTabIndex = Object.freeze({
1616
BOT_BUILDER: 1,
1717
CHART: 2,
1818
TUTORIAL: 3,
19-
SERVER_BOT: 4,
2019
});
2120

2221
export const MAX_STRATEGIES = 10;

packages/bot-web-ui/src/pages/bot-builder/quick-strategy/__tests__/quick-strategy.spec.tsx

+15
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,21 @@ jest.mock('../config', () => ({
178178
[],
179179
],
180180
},
181+
D_ALEMBERT: {
182+
label: 'D’Alembert',
183+
},
184+
OSCARS_GRIND: {
185+
label: 'Oscar’s Grind',
186+
},
187+
REVERSE_MARTINGALE: {
188+
label: 'Reverse Martingale',
189+
},
190+
REVERSE_D_ALEMBERT: {
191+
label: 'Reverse D’Alembert',
192+
},
193+
STRATEGY_1_3_2_6: {
194+
label: '1-3-2-6',
195+
},
181196
},
182197
}));
183198

packages/bot-web-ui/src/pages/bot-builder/quick-strategy/config.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ const SYMBOL: TConfigItem = {
4141

4242
const LABEL_TRADETYPE: TConfigItem = {
4343
type: 'label',
44-
label: localize('Trade type'),
45-
description: localize('Your bot will use this trade type for every run'),
44+
label: localize('Contract type'),
45+
description: localize('Your bot will use this contract type for every run'),
4646
};
4747

4848
const TRADETYPE: TConfigItem = {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
import React from 'react';
2+
import { useFormikContext } from 'formik';
3+
import { Button, Text, ThemedScrollbars } from '@deriv/components';
4+
import Icon from '@deriv/components/src/components/icon/icon';
5+
import { observer } from '@deriv/stores';
6+
import { localize } from '@deriv/translations';
7+
import { useDBotStore } from 'Stores/useDBotStore';
8+
import { rudderStackSendQsEditStrategyEvent } from '../../../../../analytics/rudderstack-quick-strategy';
9+
import { STRATEGIES } from '../../config';
10+
import { TFormData, TFormValues } from '../../types';
11+
import StrategyTabContent from '../strategy-tab-content';
12+
import useQsSubmitHandler from '../useQsSubmitHandler';
13+
import QSStepper from './qs-stepper';
14+
import StrategyTemplatePicker from './strategy-template-picker';
15+
import { QsSteps } from './trade-constants';
16+
import '../../quick-strategy.scss';
17+
18+
type TDesktopFormWrapper = {
19+
children: React.ReactNode;
20+
current_step: QsSteps;
21+
setCurrentStep: (current_step: QsSteps) => void;
22+
onClickClose: () => void;
23+
selected_trade_type: string;
24+
setSelectedTradeType: (selected_trade_type: string) => void;
25+
};
26+
27+
const QuickSelectionPanel = ({
28+
selected_trade_type,
29+
selected_startegy_label,
30+
children,
31+
}: Pick<TDesktopFormWrapper, 'selected_trade_type' | 'children'> & { selected_startegy_label: string }) => (
32+
<>
33+
<div className='qs__selected-options'>
34+
<div className='qs__selected-options__item'>
35+
<Text size='xs' line_height='s'>
36+
{localize('Trade type')}
37+
</Text>
38+
<Text size='xs' weight='bold' line_height='s'>
39+
{selected_trade_type}
40+
</Text>
41+
</div>
42+
<div className='qs__selected-options__item'>
43+
<Text size='xs' line_height='s'>
44+
{localize('Strategy')}
45+
</Text>
46+
<Text size='xs' weight='bold' line_height='s'>
47+
{selected_startegy_label}
48+
</Text>
49+
</div>
50+
</div>
51+
<StrategyTabContent formfields={children} active_tab={'TRADE_PARAMETERS'} />
52+
</>
53+
);
54+
55+
const FormWrapper = observer(
56+
({
57+
children,
58+
current_step,
59+
setCurrentStep,
60+
onClickClose,
61+
selected_trade_type,
62+
setSelectedTradeType,
63+
}: TDesktopFormWrapper) => {
64+
const scroll_ref = React.useRef<HTMLDivElement & SVGSVGElement>(null);
65+
const { submitForm, isValid, setFieldValue, validateForm, values } = useFormikContext<TFormValues>();
66+
const { quick_strategy } = useDBotStore();
67+
const { selected_strategy, onSubmit, is_stop_bot_dialog_open } = quick_strategy;
68+
const { handleSubmit } = useQsSubmitHandler();
69+
70+
const selected_startegy_label = STRATEGIES[selected_strategy as keyof typeof STRATEGIES].label;
71+
const is_selected_strategy_step = current_step === QsSteps.StrategySelect;
72+
73+
React.useEffect(() => {
74+
if (isValid && current_step === QsSteps.StrategyVerified) {
75+
setCurrentStep(QsSteps.StrategyCompleted);
76+
}
77+
if (!isValid && current_step === QsSteps.StrategyCompleted) {
78+
setCurrentStep(QsSteps.StrategyVerified);
79+
}
80+
}, [isValid, current_step]);
81+
82+
React.useEffect(() => {
83+
validateForm();
84+
}, [selected_strategy, validateForm]);
85+
86+
const onEdit = async () => {
87+
await setFieldValue('action', 'EDIT');
88+
validateForm();
89+
submitForm().then((form_data: TFormData | void) => {
90+
if (isValid && form_data) {
91+
rudderStackSendQsEditStrategyEvent({
92+
form_values: values,
93+
selected_strategy,
94+
});
95+
onSubmit(form_data); // true to load and run the bot
96+
}
97+
});
98+
};
99+
100+
const onRun = () => {
101+
handleSubmit();
102+
};
103+
104+
const onBack = () => {
105+
setCurrentStep(QsSteps.StrategySelect);
106+
};
107+
108+
const renderContent = React.useCallback(() => {
109+
switch (current_step) {
110+
case QsSteps.StrategySelect:
111+
return (
112+
<StrategyTemplatePicker
113+
setCurrentStep={setCurrentStep}
114+
setSelectedTradeType={setSelectedTradeType}
115+
/>
116+
);
117+
case QsSteps.StrategyVerified:
118+
return (
119+
<QuickSelectionPanel
120+
selected_trade_type={selected_trade_type}
121+
selected_startegy_label={selected_startegy_label}
122+
>
123+
{children}
124+
</QuickSelectionPanel>
125+
);
126+
case QsSteps.StrategyCompleted:
127+
return (
128+
<QuickSelectionPanel
129+
selected_trade_type={selected_trade_type}
130+
selected_startegy_label={selected_startegy_label}
131+
>
132+
{children}
133+
</QuickSelectionPanel>
134+
);
135+
default:
136+
return null;
137+
}
138+
}, [
139+
current_step,
140+
selected_trade_type,
141+
selected_startegy_label,
142+
children,
143+
setCurrentStep,
144+
setSelectedTradeType,
145+
]);
146+
147+
return (
148+
!is_stop_bot_dialog_open && (
149+
<div className='qs'>
150+
<div className='qs__head'>
151+
<div className='qs__head__title'>
152+
<Text weight='bold'>{localize('Quick Strategy')}</Text>
153+
</div>
154+
<div className='qs__head__action'>
155+
<span
156+
data-testid='qs-desktop-close-button'
157+
onClick={onClickClose}
158+
tabIndex={0}
159+
onKeyDown={(e: React.KeyboardEvent) => {
160+
if (e.key === 'Enter') {
161+
onClickClose();
162+
}
163+
}}
164+
>
165+
<Icon icon='IcCross' />
166+
</span>
167+
</div>
168+
</div>
169+
<div className='qs__body'>
170+
<div className='qs__body__sidebar'>
171+
<div className='qs__body__sidebar__subtitle'>
172+
<Text size='xs'>
173+
{localize('Choose a template below and set your trade parameters.')}
174+
</Text>
175+
</div>
176+
<QSStepper current_step={current_step} />
177+
</div>
178+
<div className='qs__body__content'>
179+
<ThemedScrollbars
180+
className='qs__form__container qs__form__container--no-footer'
181+
autohide={false}
182+
refSetter={scroll_ref}
183+
>
184+
{renderContent()}
185+
</ThemedScrollbars>
186+
{!is_selected_strategy_step && (
187+
<div className='qs__body__content__footer'>
188+
<Button
189+
transparent
190+
classNameSpan='qs__body__content__footer--back'
191+
disabled={is_selected_strategy_step}
192+
onClick={onBack}
193+
>
194+
{localize('Back')}
195+
</Button>
196+
<Button secondary disabled={!isValid} onClick={onEdit}>
197+
{localize('Load')}
198+
</Button>
199+
<Button
200+
data-testid='qs-run-button'
201+
primary
202+
onClick={e => {
203+
e.preventDefault();
204+
onRun();
205+
}}
206+
disabled={!isValid}
207+
>
208+
{localize('Run')}
209+
</Button>
210+
</div>
211+
)}
212+
</div>
213+
</div>
214+
</div>
215+
)
216+
);
217+
}
218+
);
219+
220+
export default React.memo(FormWrapper);

0 commit comments

Comments
 (0)