diff --git a/packages/trader/src/Constants/index.js b/packages/trader/src/Constants/index.js deleted file mode 100644 index 5ecdd1f3443d..000000000000 --- a/packages/trader/src/Constants/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from './ui'; diff --git a/packages/trader/src/Constants/ui.js b/packages/trader/src/Constants/ui.js deleted file mode 100644 index 0865c8c5efa8..000000000000 --- a/packages/trader/src/Constants/ui.js +++ /dev/null @@ -1,2 +0,0 @@ -export const MAX_MOBILE_WIDTH = 767; -export const MAX_TABLET_WIDTH = 1024; diff --git a/packages/trader/src/Stores/Modules/SmartChart/Constants/barriers.js b/packages/trader/src/Stores/Modules/SmartChart/Constants/barriers.ts similarity index 94% rename from packages/trader/src/Stores/Modules/SmartChart/Constants/barriers.js rename to packages/trader/src/Stores/Modules/SmartChart/Constants/barriers.ts index 26bef1e88d04..0fe590eeefe1 100644 --- a/packages/trader/src/Stores/Modules/SmartChart/Constants/barriers.js +++ b/packages/trader/src/Stores/Modules/SmartChart/Constants/barriers.ts @@ -14,13 +14,13 @@ export const CONTRACT_SHADES = { MULTUP: 'ABOVE', MULTDOWN: 'BELOW', ACCU: 'NONE_DOUBLE', -}; +} as const; // Default non-shade according to number of barriers export const DEFAULT_SHADES = { 1: 'NONE_SINGLE', 2: 'NONE_DOUBLE', -}; +} as const; export const BARRIER_COLORS = { GREEN: '#4bb4b3', @@ -28,10 +28,10 @@ export const BARRIER_COLORS = { ORANGE: '#ff6444', GRAY: '#999999', DARK_GRAY: '#6E6E6E', -}; +} as const; export const BARRIER_LINE_STYLES = { DASHED: 'dashed', DOTTED: 'dotted', SOLID: 'solid', -}; +} as const; diff --git a/packages/trader/src/Stores/Modules/SmartChart/Constants/markers.js b/packages/trader/src/Stores/Modules/SmartChart/Constants/markers.js deleted file mode 100644 index 987f95e37c48..000000000000 --- a/packages/trader/src/Stores/Modules/SmartChart/Constants/markers.js +++ /dev/null @@ -1,59 +0,0 @@ -import { localize } from '@deriv/translations'; -import MarkerLine from 'Modules/SmartChart/Components/Markers/marker-line.jsx'; -import MarkerSpotLabel from 'Modules/SmartChart/Components/Markers/marker-spot-label.jsx'; -import MarkerSpot from 'Modules/SmartChart/Components/Markers/marker-spot.jsx'; - -export const MARKER_TYPES_CONFIG = { - LINE_END: { - type: 'LINE_END', - marker_config: { - ContentComponent: MarkerLine, - className: 'chart-marker-line', - }, - content_config: { line_style: 'dash', label: localize('End Time') }, - }, - LINE_PURCHASE: { - type: 'LINE_PURCHASE', - marker_config: { - ContentComponent: MarkerLine, - className: 'chart-marker-line', - }, - content_config: { line_style: 'solid', label: localize('Purchase Time') }, - }, - LINE_START: { - type: 'LINE_START', - marker_config: { - ContentComponent: MarkerLine, - className: 'chart-marker-line', - }, - content_config: { line_style: 'solid', label: localize('Start Time') }, - }, - SPOT_ENTRY: { - type: 'SPOT_ENTRY', - marker_config: { - ContentComponent: MarkerSpot, - }, - content_config: { className: 'chart-spot__entry' }, - }, - SPOT_SELL: { - type: 'SPOT_SELL', - marker_config: { - ContentComponent: MarkerSpot, - }, - content_config: { className: 'chart-spot__sell' }, - }, - SPOT_EXIT: { - type: 'SPOT_EXIT', - marker_config: { - ContentComponent: MarkerSpotLabel, - }, - content_config: { spot_className: 'chart-spot__spot' }, - }, - SPOT_MIDDLE: { - type: 'SPOT_MIDDLE', - marker_config: { - ContentComponent: MarkerSpotLabel, - }, - content_config: { spot_className: 'chart-spot__spot' }, - }, -}; diff --git a/packages/trader/src/Stores/Modules/SmartChart/Helpers/__tests__/barriers.js b/packages/trader/src/Stores/Modules/SmartChart/Helpers/__tests__/barriers.js deleted file mode 100644 index 10fa8830af0d..000000000000 --- a/packages/trader/src/Stores/Modules/SmartChart/Helpers/__tests__/barriers.js +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; -import * as Barriers from '../barriers'; - -describe('Barriers', () => { - describe('isBarrierSupported', () => { - it('should return false when barrier is not in CONTRACT_SHADES', () => { - expect(Barriers.isBarrierSupported('SomeThinMadeUp')).toEqual(false); - }); - it('should return true when barrier is in CONTRACT_SHADES', () => { - expect(Barriers.isBarrierSupported('CALL')).toEqual(true); - }); - }); - - describe('barriersToString', () => { - it('should convert non-zero barriers which do not have +/- to string consisting of them without +/- while is_relative is false', () => { - expect(Barriers.barriersToString(false, 10, 15)).toEqual(['10', '15']); - }); - it('should convert values without +/- and zero to string consisting of them without +/- while is_relative is false', () => { - expect(Barriers.barriersToString(false, 0, 15)).toEqual(['0', '15']); - }); - it('should convert barriers which have +/- to string consisting of them without +/- while is_relative is false', () => { - expect(Barriers.barriersToString(false, +11, 15)).toEqual(['11', '15']); - }); - it('should convert barriers which have +/- to string consisting of them with +/- while is_relative is true', () => { - expect(Barriers.barriersToString(true, +11, +15)).toEqual(['+11', '+15']); - }); - }); - - describe('barriersObjectToArray', () => { - const main = { - color: 'green', - draggable: false, - }; - it('should return an array from values in barriers object', () => { - const barriers = { - main, - }; - expect(Barriers.barriersObjectToArray(barriers, [])).toEqual([ - { - color: 'green', - draggable: false, - }, - ]); - }); - it('should return an array from values in barriers object (empty values should be filtered out)', () => { - const barriers = { - main, - somethingEmpty: {}, - }; - expect(Barriers.barriersObjectToArray(barriers, [])).toEqual([ - { - color: 'green', - draggable: false, - }, - ]); - }); - }); -}); diff --git a/packages/trader/src/Stores/Modules/SmartChart/Helpers/__tests__/barriers.ts b/packages/trader/src/Stores/Modules/SmartChart/Helpers/__tests__/barriers.ts new file mode 100644 index 000000000000..ec9371f9b2ad --- /dev/null +++ b/packages/trader/src/Stores/Modules/SmartChart/Helpers/__tests__/barriers.ts @@ -0,0 +1,50 @@ +import * as Barriers from '../barriers'; + +describe('Barriers', () => { + describe('barriersToString', () => { + it('should convert non-zero barriers which do not have +/- to string consisting of them without +/- while is_relative is false', () => { + expect(Barriers.barriersToString(false, 10, 15)).toEqual(['10', '15']); + }); + it('should convert values without +/- and zero to string consisting of them without +/- while is_relative is false', () => { + expect(Barriers.barriersToString(false, 0, 15)).toEqual(['0', '15']); + }); + it('should convert barriers which have +/- to string consisting of them without +/- while is_relative is false', () => { + expect(Barriers.barriersToString(false, +11, 15)).toEqual(['11', '15']); + }); + it('should convert barriers which have +/- to string consisting of them with +/- while is_relative is true', () => { + expect(Barriers.barriersToString(true, +11, +15)).toEqual(['+11', '+15']); + }); + }); + describe('removeBarrier', () => { + let barriers: Barriers.TBarrier[]; + const BARRIERS_KEYS = { + PURCHASE_SPOT_BARRIER: 'PURCHASE_SPOT_BARRIER', + TAKE_PROFIT: 'take_profit', + STOP_LOSS: 'stop_loss', + STOP_OUT: 'stop_out', + }; + beforeEach(() => { + barriers = [ + { key: BARRIERS_KEYS.PURCHASE_SPOT_BARRIER, high: '1111.11' }, + { key: BARRIERS_KEYS.TAKE_PROFIT, high: '2222.22' }, + { key: BARRIERS_KEYS.STOP_OUT, high: '3333.33' }, + ] as Barriers.TBarrier[]; + }); + it('should remove the barrier with a specified key from initial barriers array', () => { + const key_to_remove = BARRIERS_KEYS.TAKE_PROFIT; + Barriers.removeBarrier(barriers, key_to_remove); + expect(barriers.find(barrier => barrier.key === key_to_remove)).toBeUndefined(); + expect(barriers.length).toEqual(2); + }); + it('should not remove any barriers if the key is not found', () => { + Barriers.removeBarrier(barriers, BARRIERS_KEYS.STOP_LOSS); + expect(barriers.length).toEqual(3); + }); + it('should not modify the barriers array if it is empty', () => { + const key_to_remove = BARRIERS_KEYS.STOP_OUT; + const empty_barriers = [] as Barriers.TBarrier[]; + Barriers.removeBarrier(empty_barriers, key_to_remove); + expect(empty_barriers.length).toEqual(0); + }); + }); +}); diff --git a/packages/trader/src/Stores/Modules/SmartChart/Helpers/barriers.js b/packages/trader/src/Stores/Modules/SmartChart/Helpers/barriers.js deleted file mode 100644 index f26a39e3146f..000000000000 --- a/packages/trader/src/Stores/Modules/SmartChart/Helpers/barriers.js +++ /dev/null @@ -1,28 +0,0 @@ -import { toJS } from 'mobx'; -import { isEmptyObject } from '@deriv/shared'; -import { CONTRACT_SHADES } from '../Constants/barriers'; - -export const isBarrierSupported = contract_type => contract_type in CONTRACT_SHADES; - -export const barriersToString = (is_relative, ...barriers_list) => - barriers_list - .filter(barrier => barrier !== undefined && barrier !== null) - .map(barrier => `${is_relative && !/^[+-]/.test(barrier) ? '+' : ''}${barrier}`); - -export const barriersObjectToArray = (barriers, reference_array) => { - Object.keys(barriers).forEach(barrier => { - const js_object = toJS(barriers[barrier]); - if (!isEmptyObject(js_object)) { - reference_array.push(js_object); - } - }); - - return reference_array; -}; - -export const removeBarrier = (barriers, key) => { - const index = barriers.findIndex(b => b.key === key); - if (index > -1) { - barriers.splice(index, 1); - } -}; diff --git a/packages/trader/src/Stores/Modules/SmartChart/Helpers/barriers.ts b/packages/trader/src/Stores/Modules/SmartChart/Helpers/barriers.ts new file mode 100644 index 000000000000..0c8862333e82 --- /dev/null +++ b/packages/trader/src/Stores/Modules/SmartChart/Helpers/barriers.ts @@ -0,0 +1,18 @@ +import type { ChartBarrierStore } from '../chart-barrier-store'; + +export type TBarrier = ChartBarrierStore & { key?: string }; + +export const barriersToString = ( + is_relative: boolean, + ...barriers_list: Array +): Array => + barriers_list + .filter(barrier => barrier !== undefined && barrier !== null) + .map(barrier => `${is_relative && !/^[+-]/.test(`${barrier}`) ? '+' : ''}${barrier}`); + +export const removeBarrier = (barriers: TBarrier[], key: string) => { + const index = barriers.findIndex(b => b.key === key); + if (index > -1) { + barriers.splice(index, 1); + } +}; diff --git a/packages/trader/src/Stores/Modules/SmartChart/chart-barrier-store.js b/packages/trader/src/Stores/Modules/SmartChart/chart-barrier-store.ts similarity index 52% rename from packages/trader/src/Stores/Modules/SmartChart/chart-barrier-store.js rename to packages/trader/src/Stores/Modules/SmartChart/chart-barrier-store.ts index 12c8b6f950fd..e75abb2256a0 100644 --- a/packages/trader/src/Stores/Modules/SmartChart/chart-barrier-store.js +++ b/packages/trader/src/Stores/Modules/SmartChart/chart-barrier-store.ts @@ -2,26 +2,38 @@ import { action, computed, observable, makeObservable } from 'mobx'; import { BARRIER_COLORS, BARRIER_LINE_STYLES, CONTRACT_SHADES, DEFAULT_SHADES } from './Constants/barriers'; import { barriersToString } from './Helpers/barriers'; -export class ChartBarrierStore { - color; - lineStyle; - shade; - shadeColor; - - high; - low; - - relative; - draggable; +type TOnChartBarrierChange = null | ((barrier_1: string, barrier_2?: string) => void); +type TOnChangeParams = { high: string | number; low?: string | number }; +type TChartBarrierStoreOptions = + | { + color: string; + line_style?: string; + not_draggable?: boolean; + } + | Record; - hidePriceLines; - hideBarrierLine; - hideOffscreenLine; - title; - - onChartBarrierChange; - - constructor(high_barrier, low_barrier, onChartBarrierChange = null, { color, line_style, not_draggable } = {}) { +export class ChartBarrierStore { + color: string; + lineStyle: string; + shade?: string; + shadeColor?: string; + high?: string | number; + low?: string | number; + onChange: (barriers: TOnChangeParams) => void; + relative: boolean; + draggable: boolean; + hidePriceLines: boolean; + hideBarrierLine?: boolean; + hideOffscreenLine?: boolean; + title?: string; + onChartBarrierChange: TOnChartBarrierChange | null; + + constructor( + high_barrier: string | number, + low_barrier?: string | number, + onChartBarrierChange: TOnChartBarrierChange = null, + { color, line_style, not_draggable }: TChartBarrierStoreOptions = {} + ) { makeObservable(this, { color: observable, lineStyle: observable, @@ -49,7 +61,7 @@ export class ChartBarrierStore { // trade_store's action to process new barriers on dragged this.onChartBarrierChange = - typeof onChartBarrierChange === 'function' ? onChartBarrierChange.bind(this) : () => {}; + typeof onChartBarrierChange === 'function' ? onChartBarrierChange.bind(this) : () => undefined; this.high = high_barrier || 0; // 0 to follow the price if (low_barrier) { @@ -59,37 +71,38 @@ export class ChartBarrierStore { this.shade = this.default_shade; const has_barrier = !!high_barrier; - this.relative = !has_barrier || /^[+-]/.test(high_barrier); + this.relative = !has_barrier || /^[+-]/.test(high_barrier.toString()); this.draggable = !not_draggable && has_barrier; this.hidePriceLines = !has_barrier; } - updateBarriers(high, low, isFromChart = false) { + updateBarriers(high: string | number, low?: string | number, isFromChart = false) { if (!isFromChart) { - this.relative = /^[+-]/.test(high); + this.relative = /^[+-]/.test(high.toString()); } this.high = high || undefined; this.low = low || undefined; } - updateBarrierShade(should_display, contract_type) { - this.shade = (should_display && CONTRACT_SHADES[contract_type]) || this.default_shade; + updateBarrierShade(should_display: boolean, contract_type: string) { + this.shade = + (should_display && CONTRACT_SHADES[contract_type as keyof typeof CONTRACT_SHADES]) || this.default_shade; } - onBarrierChange({ high, low }) { + onBarrierChange({ high, low }: TOnChangeParams) { this.updateBarriers(high, low, true); - this.onChartBarrierChange(...barriersToString(this.relative, high, low)); + this.onChartBarrierChange?.(...(barriersToString(this.relative, high, low) as [string, string | undefined])); } - updateBarrierColor(is_dark_mode) { + updateBarrierColor(is_dark_mode: boolean) { this.color = is_dark_mode ? BARRIER_COLORS.DARK_GRAY : BARRIER_COLORS.GRAY; } - get barrier_count() { - return (typeof this.high !== 'undefined') + (typeof this.low !== 'undefined'); + get barrier_count(): number { + return +(typeof this.high !== 'undefined') + +(typeof this.low !== 'undefined'); } get default_shade() { - return DEFAULT_SHADES[this.barrier_count]; + return DEFAULT_SHADES[this.barrier_count as keyof typeof DEFAULT_SHADES]; } } diff --git a/packages/trader/src/Stores/Modules/SmartChart/chart-marker-store.js b/packages/trader/src/Stores/Modules/SmartChart/chart-marker-store.js deleted file mode 100644 index c501b8e827fe..000000000000 --- a/packages/trader/src/Stores/Modules/SmartChart/chart-marker-store.js +++ /dev/null @@ -1,16 +0,0 @@ -import { observable, makeObservable } from 'mobx'; - -export class ChartMarkerStore { - marker_config = observable.object({}); - content_config = observable.object({}); - - constructor(marker_config, content_config) { - makeObservable(this, { - marker_config: observable, - content_config: observable, - }); - - this.marker_config = marker_config; - this.content_config = content_config; - } -} diff --git a/packages/trader/src/Stores/Modules/Trading/Constants/ui.js b/packages/trader/src/Stores/Modules/Trading/Constants/ui.js deleted file mode 100644 index 325fcda6e315..000000000000 --- a/packages/trader/src/Stores/Modules/Trading/Constants/ui.js +++ /dev/null @@ -1,11 +0,0 @@ -import Amount from 'Modules/Trading/Components/Form/TradeParams/amount.jsx'; -import Barrier from 'Modules/Trading/Components/Form/TradeParams/barrier.jsx'; -import Duration from 'Modules/Trading/Components/Form/TradeParams/Duration'; -import LastDigit from 'Modules/Trading/Components/Form/TradeParams/last-digit.jsx'; - -export const form_components = [ - { name: 'duration', Component: Duration }, - { name: 'barrier', Component: Barrier }, - { name: 'last_digit', Component: LastDigit }, - { name: 'amount', Component: Amount }, -]; diff --git a/packages/trader/src/Stores/Modules/Trading/Constants/validation-rules.js b/packages/trader/src/Stores/Modules/Trading/Constants/validation-rules.js deleted file mode 100644 index 3b50b9ad2d90..000000000000 --- a/packages/trader/src/Stores/Modules/Trading/Constants/validation-rules.js +++ /dev/null @@ -1,187 +0,0 @@ -import { localize } from '@deriv/translations'; -import { isHourValid, isMinuteValid, isTimeValid, toMoment } from '@deriv/shared'; -import { isSessionAvailable } from '../Helpers/start-date'; - -const tradeSpecificBarrierCheck = (is_vanilla, input) => is_vanilla || input !== 0; - -export const getValidationRules = () => ({ - amount: { - rules: [ - ['req', { message: localize('Amount is a required field.') }], - ['number', { min: 0, type: 'float' }], - ], - }, - barrier_1: { - rules: [ - [ - 'req', - { - condition: store => store.barrier_count && store.form_components.indexOf('barrier') > -1, - message: localize('Barrier is a required field.'), - }, - ], - ['barrier', { condition: store => store.barrier_count }], - [ - 'custom', - { - func: (value, options, store, inputs) => - store.barrier_count > 1 ? +value > +inputs.barrier_2 : true, - message: localize('Higher barrier must be higher than lower barrier.'), - }, - ], - [ - 'custom', - { - func: (value, options, store, inputs) => - /^[+-]/.test(inputs.barrier_1) - ? tradeSpecificBarrierCheck(store.is_vanilla, +inputs.barrier_1) - : true, - message: localize('Barrier cannot be zero.'), - }, - ], - ], - trigger: 'barrier_2', - }, - barrier_2: { - rules: [ - [ - 'req', - { - condition: store => store.barrier_count > 1 && store.form_components.indexOf('barrier') > -1, - message: localize('Barrier is a required field.'), - }, - ], - ['barrier', { condition: store => store.barrier_count }], - [ - 'custom', - { - func: (value, options, store, inputs) => - (/^[+-]/g.test(inputs.barrier_1) && /^[+-]/g.test(value)) || - (/^(?![+-])/g.test(inputs.barrier_1) && /^(?![+-])/g.test(value)), - message: localize('Both barriers should be relative or absolute'), - }, - ], - [ - 'custom', - { - func: (value, options, store, inputs) => +inputs.barrier_1 > +value, - message: localize('Lower barrier must be lower than higher barrier.'), - }, - ], - ], - trigger: 'barrier_1', - }, - duration: { - rules: [['req', { message: localize('Duration is a required field.') }]], - }, - start_date: { - trigger: 'start_time', - }, - expiry_date: { - trigger: 'expiry_time', - }, - start_time: { - rules: [ - [ - 'custom', - { - func: (value, options, store) => store.contract_start_type === 'spot' || isTimeValid(value), - message: localize('Please enter the start time in the format "HH:MM".'), - }, - ], - [ - 'custom', - { - func: (value, options, store) => store.contract_start_type === 'spot' || isHourValid(value), - message: localize('Hour must be between 0 and 23.'), - }, - ], - [ - 'custom', - { - func: (value, options, store) => store.contract_start_type === 'spot' || isMinuteValid(value), - message: localize('Minute must be between 0 and 59.'), - }, - ], - [ - 'custom', - { - func: (value, options, store) => { - if (store.contract_start_type === 'spot') return true; - if (!isTimeValid(value)) return false; - const start_moment = toMoment(store.start_date); - const start_moment_clone = start_moment.clone(); - const [h, m] = value.split(':'); - return isSessionAvailable(store.sessions, start_moment_clone.hour(h).minute(m), start_moment); - }, - message: localize('Start time cannot be in the past.'), - }, - ], - ], - }, - expiry_time: { - rules: [ - [ - 'custom', - { - func: (value, options, store) => store.contract_start_type === 'spot' || isTimeValid(value), - message: localize('Please enter the start time in the format "HH:MM".'), - }, - ], - [ - 'custom', - { - func: (value, options, store) => store.contract_start_type === 'spot' || isHourValid(value), - message: localize('Hour must be between 0 and 23.'), - }, - ], - [ - 'custom', - { - func: (value, options, store) => store.contract_start_type === 'spot' || isMinuteValid(value), - message: localize('Minute must be between 0 and 59.'), - }, - ], - [ - 'custom', - { - func: (value, options, store) => { - if (store.contract_start_type === 'spot') return true; - if (!isTimeValid(value)) return false; - const start_moment = toMoment(store.start_date); - const start_moment_clone = start_moment.clone(); - const [h, m] = value.split(':'); - return isSessionAvailable(store.sessions, start_moment_clone.hour(h).minute(m), start_moment); - }, - message: localize('Expiry time cannot be in the past.'), - }, - ], - ], - }, - ...getMultiplierValidationRules(), -}); - -export const getMultiplierValidationRules = () => ({ - stop_loss: { - rules: [ - [ - 'req', - { - condition: store => store.has_stop_loss && !store.stop_loss, - message: localize('Please enter a stop loss amount.'), - }, - ], - ], - }, - take_profit: { - rules: [ - [ - 'req', - { - condition: store => store.has_take_profit && !store.take_profit, - message: localize('Please enter a take profit amount.'), - }, - ], - ], - }, -}); diff --git a/packages/trader/src/Stores/Modules/Trading/Constants/validation-rules.ts b/packages/trader/src/Stores/Modules/Trading/Constants/validation-rules.ts new file mode 100644 index 000000000000..6911efae718e --- /dev/null +++ b/packages/trader/src/Stores/Modules/Trading/Constants/validation-rules.ts @@ -0,0 +1,234 @@ +import { localize } from '@deriv/translations'; +import { isHourValid, isMinuteValid, isTimeValid, toMoment } from '@deriv/shared'; +import { isSessionAvailable } from '../Helpers/start-date'; +import { TTradeStore } from 'Types'; +import type { TRuleOptions } from 'Utils/Validator/validator'; + +const tradeSpecificBarrierCheck = (is_vanilla: boolean, input: number) => is_vanilla || input !== 0; + +export const getValidationRules = () => + ({ + amount: { + rules: [ + ['req', { message: localize('Amount is a required field.') }], + ['number', { min: 0, type: 'float' }], + ], + }, + barrier_1: { + rules: [ + [ + 'req', + { + condition: (store: TTradeStore) => + store.barrier_count && store.form_components.indexOf('barrier') > -1, + message: localize('Barrier is a required field.'), + }, + ], + ['barrier', { condition: (store: TTradeStore) => store.barrier_count }], + [ + 'custom', + { + func: ( + value: TTradeStore['barrier_1'], + options: Partial, + store: TTradeStore, + inputs: Pick + ) => (store.barrier_count > 1 ? +value > +inputs.barrier_2 : true), + message: localize('Higher barrier must be higher than lower barrier.'), + }, + ], + [ + 'custom', + { + func: ( + value: TTradeStore['barrier_1'], + options: Partial, + store: TTradeStore, + inputs: Pick + ) => + /^[+-]/.test(inputs.barrier_1) + ? tradeSpecificBarrierCheck(store.is_vanilla, +inputs.barrier_1) + : true, + message: localize('Barrier cannot be zero.'), + }, + ], + ], + trigger: 'barrier_2', + }, + barrier_2: { + rules: [ + [ + 'req', + { + condition: (store: TTradeStore) => + store.barrier_count > 1 && store.form_components.indexOf('barrier') > -1, + message: localize('Barrier is a required field.'), + }, + ], + ['barrier', { condition: (store: TTradeStore) => store.barrier_count }], + [ + 'custom', + { + func: ( + value: TTradeStore['barrier_2'], + options: Partial, + store: TTradeStore, + inputs: Pick + ) => + (/^[+-]/g.test(inputs.barrier_1) && /^[+-]/g.test(value)) || + (/^(?![+-])/g.test(inputs.barrier_1) && /^(?![+-])/g.test(value)), + message: localize('Both barriers should be relative or absolute'), + }, + ], + [ + 'custom', + { + func: ( + value: TTradeStore['barrier_2'], + options: Partial, + store: TTradeStore, + inputs: Pick + ) => +inputs.barrier_1 > +value, + message: localize('Lower barrier must be lower than higher barrier.'), + }, + ], + ], + trigger: 'barrier_1', + }, + duration: { + rules: [['req', { message: localize('Duration is a required field.') }]], + }, + start_date: { + trigger: 'start_time', + }, + expiry_date: { + trigger: 'expiry_time', + }, + start_time: { + rules: [ + [ + 'custom', + { + func: (value: TTradeStore['start_time'], options: Partial, store: TTradeStore) => + store.contract_start_type === 'spot' || isTimeValid(value ?? ''), + message: localize('Please enter the start time in the format "HH:MM".'), + }, + ], + [ + 'custom', + { + func: (value: TTradeStore['start_time'], options: Partial, store: TTradeStore) => + store.contract_start_type === 'spot' || isHourValid(value ?? ''), + message: localize('Hour must be between 0 and 23.'), + }, + ], + [ + 'custom', + { + func: (value: TTradeStore['start_time'], options: Partial, store: TTradeStore) => + store.contract_start_type === 'spot' || isMinuteValid(value ?? ''), + message: localize('Minute must be between 0 and 59.'), + }, + ], + [ + 'custom', + { + func: ( + value: TTradeStore['start_time'], + options: Partial, + store: TTradeStore + ) => { + if (store.contract_start_type === 'spot') return true; + if (!isTimeValid(value ?? '')) return false; + const start_moment = toMoment(store.start_date); + const start_moment_clone = start_moment.clone(); + const [h, m] = value?.split(':') ?? []; + return isSessionAvailable( + store.sessions, + start_moment_clone.hour(+h).minute(+m), + start_moment + ); + }, + message: localize('Start time cannot be in the past.'), + }, + ], + ], + }, + expiry_time: { + rules: [ + [ + 'custom', + { + func: (value: TTradeStore['expiry_time'], options: Partial, store: TTradeStore) => + store.contract_start_type === 'spot' || isTimeValid(value ?? ''), + message: localize('Please enter the start time in the format "HH:MM".'), + }, + ], + [ + 'custom', + { + func: (value: TTradeStore['expiry_time'], options: Partial, store: TTradeStore) => + store.contract_start_type === 'spot' || isHourValid(value ?? ''), + message: localize('Hour must be between 0 and 23.'), + }, + ], + [ + 'custom', + { + func: (value: TTradeStore['expiry_time'], options: Partial, store: TTradeStore) => + store.contract_start_type === 'spot' || isMinuteValid(value ?? ''), + message: localize('Minute must be between 0 and 59.'), + }, + ], + [ + 'custom', + { + func: ( + value: TTradeStore['expiry_time'], + options: Partial, + store: TTradeStore + ) => { + if (store.contract_start_type === 'spot') return true; + if (!isTimeValid(value ?? '')) return false; + const start_moment = toMoment(store.start_date); + const start_moment_clone = start_moment.clone(); + const [h, m] = value?.split(':') ?? []; + return isSessionAvailable( + store.sessions, + start_moment_clone.hour(+h).minute(+m), + start_moment + ); + }, + message: localize('Expiry time cannot be in the past.'), + }, + ], + ], + }, + ...getMultiplierValidationRules(), + } as const); + +export const getMultiplierValidationRules = () => + ({ + stop_loss: { + rules: [ + [ + 'req', + { + condition: (store: TTradeStore) => store.has_stop_loss && !store.stop_loss, + message: localize('Please enter a stop loss amount.'), + }, + ], + ], + }, + take_profit: { + rules: [ + [ + 'req', + { + condition: (store: TTradeStore) => store.has_take_profit && !store.take_profit, + message: localize('Please enter a take profit amount.'), + }, + ], + ], + }, + } as const); diff --git a/packages/trader/src/Utils/Validator/validator.ts b/packages/trader/src/Utils/Validator/validator.ts index 01c50cec285e..60f258a7fcad 100644 --- a/packages/trader/src/Utils/Validator/validator.ts +++ b/packages/trader/src/Utils/Validator/validator.ts @@ -9,7 +9,7 @@ type TOptions = { decimals?: string | number; is_required?: boolean; max?: number | string; - min?: number; + min?: number | string; name1?: string; name2?: string; regex?: RegExp; @@ -18,7 +18,7 @@ type TOptions = { type TInitPreBuildDVRs = ReturnType; -type TRuleOptions = { +export type TRuleOptions = { func: (value: string | number, options?: TOptions, store?: TTradeStore, inputs?: unknown) => boolean; condition: (store: TTradeStore) => boolean; message: string;