{
const no_residence = cashier_validation?.includes('no_residence');
const unwelcome_status = cashier_validation?.includes('unwelcome_status');
@@ -52,6 +56,21 @@ const getMessage = ({
const pa_commision_withdrawal_limit = cashier_validation?.includes('PACommisionWithdrawalLimit');
const pathname = history.location.pathname;
+ if (is_account_to_be_closed_by_residence && pathname === routes.cashier_deposit) {
+ return {
+ icon: 'IcCashierNoBalance',
+ title: localize('Deposits disabled'),
+ description: (
+
+ ),
+ };
+ }
+
if (is_system_maintenance) {
if (is_crypto && is_withdrawal_locked)
return {
diff --git a/packages/cashier/src/components/cashier-locked/cashier-locked.tsx b/packages/cashier/src/components/cashier-locked/cashier-locked.tsx
index 9de83a7115d0..40cfc8e49d31 100644
--- a/packages/cashier/src/components/cashier-locked/cashier-locked.tsx
+++ b/packages/cashier/src/components/cashier-locked/cashier-locked.tsx
@@ -20,6 +20,8 @@ const CashierLocked = observer(() => {
is_withdrawal_lock: is_withdrawal_locked,
loginid,
is_identity_verification_needed,
+ is_account_to_be_closed_by_residence,
+ account_time_of_closure,
} = client;
const mf_account_status = useMFAccountStatus();
const is_cashier_locked = useCashierLocked();
@@ -40,6 +42,8 @@ const CashierLocked = observer(() => {
is_withdrawal_locked,
is_identity_verification_needed,
is_pending_verification: mf_account_status === MT5_ACCOUNT_STATUS.PENDING,
+ is_account_to_be_closed_by_residence,
+ account_time_of_closure,
});
return (
diff --git a/packages/core/src/App/Containers/app-notification-messages.jsx b/packages/core/src/App/Containers/app-notification-messages.jsx
index 4f9603da1693..ee8862778273 100644
--- a/packages/core/src/App/Containers/app-notification-messages.jsx
+++ b/packages/core/src/App/Containers/app-notification-messages.jsx
@@ -129,6 +129,7 @@ const AppNotificationMessages = observer(
'trustpilot',
'unwelcome',
'additional_kyc_info',
+ 'notify_account_is_to_be_closed_by_residence',
].includes(message.key) || message.type === 'p2p_completed_order'
: true;
diff --git a/packages/core/src/Stores/client-store.js b/packages/core/src/Stores/client-store.js
index 8385afcfc677..cedde01b6053 100644
--- a/packages/core/src/Stores/client-store.js
+++ b/packages/core/src/Stores/client-store.js
@@ -421,6 +421,8 @@ export default class ClientStore extends BaseStore {
is_mf_account: computed,
is_tradershub_tracking: observable,
setTradersHubTracking: action.bound,
+ account_time_of_closure: computed,
+ is_account_to_be_closed_by_residence: computed,
});
reaction(
@@ -2881,4 +2883,14 @@ export default class ClientStore extends BaseStore {
get is_mf_account() {
return this.loginid?.startsWith('MF');
}
+
+ get account_time_of_closure() {
+ return this.account_status?.account_closure?.find(
+ item => item?.status_codes?.includes('residence_closure') && item?.type === 'residence'
+ )?.time_of_closure;
+ }
+
+ get is_account_to_be_closed_by_residence() {
+ return this.account_time_of_closure && this.residence && this.residence === 'sn';
+ }
}
diff --git a/packages/core/src/Stores/notification-store.js b/packages/core/src/Stores/notification-store.js
index 07840441f8eb..7652dd65d7a9 100644
--- a/packages/core/src/Stores/notification-store.js
+++ b/packages/core/src/Stores/notification-store.js
@@ -416,6 +416,14 @@ export default class NotificationStore extends BaseStore {
this.removeNotificationByKey({ key: this.client_notifications.enable_passkey });
}
+ if (this.root_store.client.is_account_to_be_closed_by_residence) {
+ this.addNotificationMessage(this.client_notifications.notify_account_is_to_be_closed_by_residence);
+ } else {
+ this.removeNotificationByKey({
+ key: this.client_notifications.notify_account_is_to_be_closed_by_residence,
+ });
+ }
+
const client = accounts[loginid];
if (client && !client.is_virtual) {
if (isEmptyObject(account_status)) return;
@@ -1541,6 +1549,24 @@ export default class NotificationStore extends BaseStore {
},
type: 'warning',
},
+ notify_account_is_to_be_closed_by_residence: {
+ action: {
+ route: routes.cashier_withdrawal,
+ text: localize('Withdraw funds'),
+ },
+ header: localize('Deposits and trading disabled'),
+ key: 'notify_account_is_to_be_closed_by_residence',
+ message: (
+
+ ),
+ should_show_again: true,
+ type: 'warning',
+ },
};
this.client_notifications = notifications;
diff --git a/packages/core/src/Stores/ui-store.js b/packages/core/src/Stores/ui-store.js
index 2dd38a2a5441..122f41d2223f 100644
--- a/packages/core/src/Stores/ui-store.js
+++ b/packages/core/src/Stores/ui-store.js
@@ -46,6 +46,7 @@ export default class UIStore extends BaseStore {
is_update_email_modal_visible = false;
is_reset_trading_password_modal_visible = false;
is_mf_verification_pending_modal_visible = false;
+ is_trading_disabled_by_residence_modal_visible = false;
// @observable is_purchase_lock_on = false;
// SmartCharts Controls
// TODO: enable asset information
@@ -267,6 +268,7 @@ export default class UIStore extends BaseStore {
is_landscape: observable,
is_language_settings_modal_on: observable,
is_mf_verification_pending_modal_visible: observable,
+ is_trading_disabled_by_residence_modal_visible: observable,
is_mobile_language_menu_open: observable,
is_nativepicker_visible: observable,
@@ -397,6 +399,7 @@ export default class UIStore extends BaseStore {
setSubSectionIndex: action.bound,
setTopUpInProgress: action.bound,
setIsMFVericationPendingModal: action.bound,
+ setIsTradingDisabledByResidenceModal: action.bound,
setMT5MigrationModalEnabled: action.bound,
setMobileLanguageMenuOpen: action.bound,
toggleAccountsDialog: action.bound,
@@ -956,6 +959,10 @@ export default class UIStore extends BaseStore {
this.is_mf_verification_pending_modal_visible = value;
}
+ setIsTradingDisabledByResidenceModal(value) {
+ this.is_trading_disabled_by_residence_modal_visible = value;
+ }
+
toggleAdditionalKycInfoModal() {
this.is_additional_kyc_info_modal_open = !this.is_additional_kyc_info_modal_open;
}
diff --git a/packages/hooks/src/useDepositLocked.ts b/packages/hooks/src/useDepositLocked.ts
index 3e6c39ae9610..1cd1148edd9b 100644
--- a/packages/hooks/src/useDepositLocked.ts
+++ b/packages/hooks/src/useDepositLocked.ts
@@ -5,7 +5,12 @@ import useNeedTNC from './useNeedTNC';
const useDepositLocked = () => {
const { client } = useStore();
- const { is_deposit_lock, is_trading_experience_incomplete, landing_company_shortcode } = client;
+ const {
+ is_deposit_lock,
+ is_trading_experience_incomplete,
+ landing_company_shortcode,
+ is_account_to_be_closed_by_residence,
+ } = client;
const is_need_authentication = useNeedAuthentication();
const is_need_tnc = useNeedTNC();
const is_need_financial_assessment = useNeedFinancialAssessment();
@@ -18,7 +23,8 @@ const useDepositLocked = () => {
is_deposit_lock ||
is_need_authentication ||
is_need_tnc ||
- is_trading_experience_incomplete_or_need_financial_assessment;
+ is_trading_experience_incomplete_or_need_financial_assessment ||
+ is_account_to_be_closed_by_residence;
return is_deposit_locked;
};
diff --git a/packages/stores/src/mockStore.ts b/packages/stores/src/mockStore.ts
index e309fe27c29e..fdc66ab43bdf 100644
--- a/packages/stores/src/mockStore.ts
+++ b/packages/stores/src/mockStore.ts
@@ -308,6 +308,8 @@ const mock = (): TStores & { is_mock: boolean } => {
is_mf_account: false,
is_tradershub_tracking: false,
setTradersHubTracking: jest.fn(),
+ account_time_of_closure: undefined,
+ is_account_to_be_closed_by_residence: false,
},
common: {
error: common_store_error,
@@ -481,6 +483,8 @@ const mock = (): TStores & { is_mock: boolean } => {
setShouldShowDepositNowOrLaterModal: jest.fn(),
should_show_crypto_transaction_processing_modal: false,
setShouldShowCryptoTransactionProcessingModal: jest.fn(),
+ is_trading_disabled_by_residence_modal_visible: false,
+ setIsTradingDisabledByResidenceModal: jest.fn(),
},
traders_hub: {
getAccount: jest.fn(),
diff --git a/packages/stores/types.ts b/packages/stores/types.ts
index b6d2239f58f5..631c2a1dcd1c 100644
--- a/packages/stores/types.ts
+++ b/packages/stores/types.ts
@@ -618,6 +618,8 @@ type TClientStore = {
is_cr_account: boolean;
is_mf_account: boolean;
setTradersHubTracking: (value: boolean) => void;
+ account_time_of_closure?: number;
+ is_account_to_be_closed_by_residence: boolean;
};
type TCommonStoreError = {
@@ -826,6 +828,8 @@ type TUiStore = {
setShouldShowDepositNowOrLaterModal: (value: boolean) => void;
should_show_crypto_transaction_processing_modal: boolean;
setShouldShowCryptoTransactionProcessingModal: (value: boolean) => void;
+ is_trading_disabled_by_residence_modal_visible: boolean;
+ setIsTradingDisabledByResidenceModal: (value: boolean) => void;
};
type TPortfolioStore = {
diff --git a/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/__tests__/trading-disabled-by-residence-modal-content.spec.tsx b/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/__tests__/trading-disabled-by-residence-modal-content.spec.tsx
new file mode 100644
index 000000000000..a0203d76f097
--- /dev/null
+++ b/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/__tests__/trading-disabled-by-residence-modal-content.spec.tsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import { StoreProvider, mockStore } from '@deriv/stores';
+import { TradingDisabledByResidenceModalContent } from '../trading-disabled-by-residence-modal-content';
+
+describe('
', () => {
+ const mockDefault = mockStore({});
+
+ const wrapper = (mock: ReturnType
= mockDefault) => {
+ const Component = ({ children }: { children: JSX.Element }) => (
+ {children}
+ );
+ return Component;
+ };
+
+ it('should render modal content with correct title', () => {
+ render(, {
+ wrapper: wrapper(),
+ });
+
+ expect(screen.getByText(/Trading disabled/)).toBeInTheDocument();
+ });
+});
diff --git a/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/__tests__/trading-disabled-by-residence-modal.spec.tsx b/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/__tests__/trading-disabled-by-residence-modal.spec.tsx
new file mode 100644
index 000000000000..bf1cfd96200c
--- /dev/null
+++ b/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/__tests__/trading-disabled-by-residence-modal.spec.tsx
@@ -0,0 +1,85 @@
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { useDevice } from '@deriv-com/ui';
+import { StoreProvider, mockStore } from '@deriv/stores';
+import TradingDisabledByResidenceModal from '../trading-disabled-by-residence-modal';
+
+jest.mock('@deriv-com/ui', () => ({
+ ...jest.requireActual('@deriv-com/ui'),
+ useDevice: jest.fn(() => ({ isMobile: false })),
+}));
+
+jest.mock('../trading-disabled-by-residence-modal-content', () => ({
+ __esModule: true,
+ default: () => undefined,
+ TradingDisabledByResidenceModalContent: () => Content
,
+}));
+
+describe('', () => {
+ let modal_root_el: HTMLDivElement;
+
+ const setIsTradingDisabledByResidenceModal = jest.fn();
+
+ const mockDefault = mockStore({
+ ui: {
+ is_trading_disabled_by_residence_modal_visible: true,
+ setIsTradingDisabledByResidenceModal,
+ },
+ });
+
+ const wrapper = (mock: ReturnType = mockDefault) => {
+ const Component = ({ children }: { children: JSX.Element }) => (
+ {children}
+ );
+ return Component;
+ };
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ beforeAll(() => {
+ modal_root_el = document.createElement('div');
+ modal_root_el.setAttribute('id', 'modal_root');
+ modal_root_el.setAttribute('data-testid', 'dt_test_modal');
+ document.body.appendChild(modal_root_el);
+ });
+
+ afterAll(() => {
+ document.body.removeChild(modal_root_el);
+ });
+
+ it('should render modal for desktop', () => {
+ render(, {
+ wrapper: wrapper(),
+ });
+
+ expect(screen.getByTestId('dt_test_modal')).toBeInTheDocument();
+ expect(screen.getByText('Content')).toBeInTheDocument();
+ });
+
+ it('should render modal for responsive', () => {
+ (useDevice as jest.Mock).mockReturnValueOnce({ isMobile: true });
+
+ render(, {
+ wrapper: wrapper(),
+ });
+
+ expect(screen.getByTestId('dt_test_modal')).toBeInTheDocument();
+ expect(screen.getByText('Content')).toBeInTheDocument();
+ });
+
+ it('should call setIsTradingDisabledByResidenceModal with false when try to close modal', () => {
+ render(, {
+ wrapper: wrapper(),
+ });
+
+ const close_button = screen.getByRole('button', {
+ name: '',
+ });
+ userEvent.click(close_button);
+
+ expect(setIsTradingDisabledByResidenceModal).toHaveBeenCalledWith(false);
+ });
+});
diff --git a/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/index.ts b/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/index.ts
new file mode 100644
index 000000000000..eedacba07c7a
--- /dev/null
+++ b/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/index.ts
@@ -0,0 +1,3 @@
+import TradingDisabledByResidenceModal from './trading-disabled-by-residence-modal';
+
+export default TradingDisabledByResidenceModal;
diff --git a/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/trading-disabled-by-residence-modal-content.tsx b/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/trading-disabled-by-residence-modal-content.tsx
new file mode 100644
index 000000000000..aab565006eac
--- /dev/null
+++ b/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/trading-disabled-by-residence-modal-content.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import { Icon, Text } from '@deriv/components';
+import { Localize } from '@deriv/translations';
+import { formatDate } from '@deriv/shared';
+import { observer, useStore } from '@deriv/stores';
+
+export const TradingDisabledByResidenceModalContent = observer(() => {
+ const { client } = useStore();
+ const { account_time_of_closure } = client;
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+});
diff --git a/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/trading-disabled-by-residence-modal.scss b/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/trading-disabled-by-residence-modal.scss
new file mode 100644
index 000000000000..14d99da3ee42
--- /dev/null
+++ b/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/trading-disabled-by-residence-modal.scss
@@ -0,0 +1,14 @@
+.trading-disabled-by-residence-modal {
+ &__content {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ gap: 2.4rem;
+ padding: 3.2rem;
+
+ @include mobile-screen {
+ padding: 1.6rem;
+ }
+ }
+}
diff --git a/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/trading-disabled-by-residence-modal.tsx b/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/trading-disabled-by-residence-modal.tsx
new file mode 100644
index 000000000000..ba0ce0440bb3
--- /dev/null
+++ b/packages/trader/src/App/Components/Elements/Modals/TradingDisabledByResidenceModal/trading-disabled-by-residence-modal.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import { useDevice } from '@deriv-com/ui';
+import { MobileDialog, Modal } from '@deriv/components';
+import { observer, useStore } from '@deriv/stores';
+import { TradingDisabledByResidenceModalContent } from './trading-disabled-by-residence-modal-content';
+import './trading-disabled-by-residence-modal.scss';
+
+const TradingDisabledByResidenceModal = observer(() => {
+ const { isMobile } = useDevice();
+ const { ui } = useStore();
+ const { is_trading_disabled_by_residence_modal_visible, setIsTradingDisabledByResidenceModal } = ui;
+
+ const onCloseModal = () => {
+ setIsTradingDisabledByResidenceModal(false);
+ };
+
+ return (
+
+ {isMobile ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+ );
+});
+
+export default TradingDisabledByResidenceModal;
diff --git a/packages/trader/src/App/Containers/Modals/trade-modals.tsx b/packages/trader/src/App/Containers/Modals/trade-modals.tsx
index 19739aec97d9..e7cc66cba220 100644
--- a/packages/trader/src/App/Containers/Modals/trade-modals.tsx
+++ b/packages/trader/src/App/Containers/Modals/trade-modals.tsx
@@ -3,6 +3,7 @@ import { getUrlSmartTrader } from '@deriv/shared';
import MarketUnavailableModal from 'App/Components/Elements/Modals/MarketUnavailableModal';
import ServicesErrorModal from 'App/Components/Elements/Modals/ServicesErrorModal';
import AccountVerificationPendingModal from 'App/Components/Elements/Modals/AccountVerificationPendingModal';
+import TradingDisabledByResidenceModal from 'App/Components/Elements/Modals/TradingDisabledByResidenceModal';
import { observer, useStore } from '@deriv/stores';
import { useTraderStore } from 'Stores/useTraderStores';
@@ -57,6 +58,8 @@ const TradeModals = observer(() => {
is_visible={is_mf_verification_pending_modal_visible}
onConfirm={() => setIsMFVericationPendingModal(false)}
/>
+
+
);
});
diff --git a/packages/trader/src/Modules/Trading/Containers/purchase.tsx b/packages/trader/src/Modules/Trading/Containers/purchase.tsx
index c14fb41b4ca3..6e4d6696ecb8 100644
--- a/packages/trader/src/Modules/Trading/Containers/purchase.tsx
+++ b/packages/trader/src/Modules/Trading/Containers/purchase.tsx
@@ -30,7 +30,13 @@ const getSortedIndex = (type: string, index: number) => {
const Purchase = observer(({ is_market_closed }: { is_market_closed?: boolean }) => {
const {
portfolio: { all_positions, onClickSell },
- ui: { purchase_states: purchased_states_arr, is_mobile, setPurchaseState },
+ ui: {
+ purchase_states: purchased_states_arr,
+ is_mobile,
+ setPurchaseState,
+ setIsTradingDisabledByResidenceModal,
+ },
+ client: { is_account_to_be_closed_by_residence },
} = useStore();
const {
basis,
@@ -47,7 +53,7 @@ const Purchase = observer(({ is_market_closed }: { is_market_closed?: boolean })
is_vanilla_fx,
is_vanilla,
onHoverPurchase,
- onPurchase: onClickPurchase,
+ onPurchase,
proposal_info,
purchase_info,
symbol,
@@ -82,6 +88,10 @@ const Purchase = observer(({ is_market_closed }: { is_market_closed?: boolean })
const components: JSX.Element[] = [];
+ const onClickPurchase = is_account_to_be_closed_by_residence
+ ? () => setIsTradingDisabledByResidenceModal(true)
+ : onPurchase;
+
Object.keys(trade_types).forEach((type, index) => {
const info = proposal_info?.[type] || {};
const is_disabled = !is_trade_enabled || !info.id || !is_purchase_enabled;