}
list={[
{
text: localize('Block'),
diff --git a/src/components/AdvertiserName/BlockUserCount.scss b/src/components/AdvertiserName/BlockUserCount.scss
index 23d241ad..310e692f 100644
--- a/src/components/AdvertiserName/BlockUserCount.scss
+++ b/src/components/AdvertiserName/BlockUserCount.scss
@@ -1,9 +1,4 @@
.block-user-count {
- & .deriv-tooltip-container {
- display: flex;
- align-items: center;
- gap: 0.1rem;
- }
&__tooltip {
padding: 0 0.8rem;
display: flex;
@@ -28,4 +23,12 @@
&__button {
padding: 0;
}
+
+ & .deriv-tooltip {
+ padding: 0.8rem;
+
+ @include mobile-or-tablet-screen {
+ display: none;
+ }
+ }
}
diff --git a/src/components/AdvertiserName/BlockUserCount.tsx b/src/components/AdvertiserName/BlockUserCount.tsx
index 904e4013..8efe1fd5 100644
--- a/src/components/AdvertiserName/BlockUserCount.tsx
+++ b/src/components/AdvertiserName/BlockUserCount.tsx
@@ -2,9 +2,8 @@ import { TLocalize } from 'types';
import { useModalManager } from '@/hooks';
import { LabelPairedCircleUserSlashSmRegularIcon } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
-import { Text, useDevice } from '@deriv-com/ui';
+import { Text, Tooltip, useDevice } from '@deriv-com/ui';
import { BlockUserCountModal } from '../Modals';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
import './BlockUserCount.scss';
type TBlockUserCount = {
@@ -32,21 +31,21 @@ const BlockUserCount = ({ count }: TBlockUserCount) => {
const { isDesktop, isMobile } = useDevice();
return (
-
{
isDesktop ? undefined : showModal('BlockUserCountModal');
}}
- tooltipContent={getMessage(localize, count)}
+ tooltipContent={{getMessage(localize, count)}}
>
{count ?? 0}
-
+
{!!isModalOpenFor('BlockUserCountModal') && (
)}
diff --git a/src/components/AdvertsTableRow/AdvertsTableRow.tsx b/src/components/AdvertsTableRow/AdvertsTableRow.tsx
index 9c15eb7c..74c90474 100644
--- a/src/components/AdvertsTableRow/AdvertsTableRow.tsx
+++ b/src/components/AdvertsTableRow/AdvertsTableRow.tsx
@@ -218,7 +218,7 @@ const AdvertsTableRow = memo((props: TAdvertsTableRowRenderer) => {
className='lg:min-w-[7.5rem]'
disabled={isAdvertiserBarred}
onClick={() => {
- if (!isPoaVerified || !isPoiVerified) {
+ if (!isAdvertiser && (!isPoaVerified || !isPoiVerified)) {
const searchParams = new URLSearchParams(location.search);
searchParams.set('poi_poa_verified', 'false');
history.replace({
@@ -226,7 +226,7 @@ const AdvertsTableRow = memo((props: TAdvertsTableRowRenderer) => {
search: searchParams.toString(),
});
} else {
- if (!isAdvertiser) setSelectedAdvertId(advertId);
+ setSelectedAdvertId(advertId);
showModal(isAdvertiser ? 'BuySellForm' : 'NicknameModal');
}
}}
diff --git a/src/components/AppFooter/AccountLimits.tsx b/src/components/AppFooter/AccountLimits.tsx
index c2282e54..482debe0 100644
--- a/src/components/AppFooter/AccountLimits.tsx
+++ b/src/components/AppFooter/AccountLimits.tsx
@@ -1,20 +1,15 @@
import { ACCOUNT_LIMITS } from '@/constants';
import { LegacyAccountLimitsIcon } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
+import { Tooltip } from '@deriv-com/ui';
const AccountLimits = () => {
const { localize } = useTranslations();
return (
-
+
-
+
);
};
diff --git a/src/components/AppFooter/ChangeTheme.tsx b/src/components/AppFooter/ChangeTheme.tsx
index 530cfdf3..0eb5c633 100644
--- a/src/components/AppFooter/ChangeTheme.tsx
+++ b/src/components/AppFooter/ChangeTheme.tsx
@@ -1,6 +1,6 @@
import { LegacyThemeLightIcon } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
+import { Tooltip } from '@deriv-com/ui';
const ChangeTheme = () => {
const { localize } = useTranslations();
@@ -8,9 +8,9 @@ const ChangeTheme = () => {
return (
// TODO need to add theme logic
// TODO update the component's tests after adding the logic
-
+
-
+
);
};
diff --git a/src/components/AppFooter/Deriv.tsx b/src/components/AppFooter/Deriv.tsx
index 643556d0..b072bd65 100644
--- a/src/components/AppFooter/Deriv.tsx
+++ b/src/components/AppFooter/Deriv.tsx
@@ -1,13 +1,13 @@
import { DERIV_COM } from '@/constants';
import { LegacyDerivIcon } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
+import { Tooltip } from '@deriv-com/ui';
const Deriv = () => {
const { localize } = useTranslations();
return (
-
{
tooltipContent={localize('Go to deriv.com')}
>
-
+
);
};
diff --git a/src/components/AppFooter/FullScreen.tsx b/src/components/AppFooter/FullScreen.tsx
index c1e0da75..edbbdc3e 100644
--- a/src/components/AppFooter/FullScreen.tsx
+++ b/src/components/AppFooter/FullScreen.tsx
@@ -1,21 +1,21 @@
import { useFullScreen } from '@/hooks';
import { LegacyFullscreen1pxIcon } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
+import { Tooltip } from '@deriv-com/ui';
const FullScreen = () => {
const { toggleFullScreenMode } = useFullScreen();
const { localize } = useTranslations();
return (
-
-
+
);
};
diff --git a/src/components/AppFooter/HelpCentre.tsx b/src/components/AppFooter/HelpCentre.tsx
index 350ff9be..85c7ba57 100644
--- a/src/components/AppFooter/HelpCentre.tsx
+++ b/src/components/AppFooter/HelpCentre.tsx
@@ -1,13 +1,13 @@
import { HELP_CENTRE } from '@/constants';
import { LegacyHelpCentreIcon } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
+import { Tooltip } from '@deriv-com/ui';
const HelpCentre = () => {
const { localize } = useTranslations();
return (
-
{
tooltipContent={localize('Help centre')}
>
-
+
);
};
diff --git a/src/components/AppFooter/LanguageSettings.tsx b/src/components/AppFooter/LanguageSettings.tsx
index d8c98356..d1e4cee8 100644
--- a/src/components/AppFooter/LanguageSettings.tsx
+++ b/src/components/AppFooter/LanguageSettings.tsx
@@ -1,9 +1,8 @@
import { useMemo } from 'react';
import { LANGUAGES } from '@/constants';
import { useTranslations } from '@deriv-com/translations';
-import { Text } from '@deriv-com/ui';
+import { Text, Tooltip } from '@deriv-com/ui';
import { LocalStorageUtils } from '@deriv-com/utils';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
type TLanguageSettings = {
openLanguageSettingModal: () => void;
@@ -19,7 +18,7 @@ const LanguageSettings = ({ openLanguageSettingModal }: TLanguageSettings) => {
);
return (
-
{
{currentLang}
-
+
);
};
diff --git a/src/components/AppFooter/Livechat.tsx b/src/components/AppFooter/Livechat.tsx
index 08848cd8..2a6ae165 100644
--- a/src/components/AppFooter/Livechat.tsx
+++ b/src/components/AppFooter/Livechat.tsx
@@ -1,14 +1,14 @@
import { useLiveChat } from '@/hooks/custom-hooks';
import { LegacyLiveChatOutlineIcon } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
+import { Tooltip } from '@deriv-com/ui';
const Livechat = () => {
const { LiveChatWidget } = useLiveChat();
const { localize } = useTranslations();
return (
-
{
@@ -17,7 +17,7 @@ const Livechat = () => {
tooltipContent={localize('Live chat')}
>
-
+
);
};
diff --git a/src/components/AppFooter/NetworkStatus.tsx b/src/components/AppFooter/NetworkStatus.tsx
index 5468bc1d..6e2680ec 100644
--- a/src/components/AppFooter/NetworkStatus.tsx
+++ b/src/components/AppFooter/NetworkStatus.tsx
@@ -2,7 +2,7 @@ import { useMemo } from 'react';
import clsx from 'clsx';
import { useNetworkStatus } from '@/hooks';
import { useTranslations } from '@deriv-com/translations';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
+import { Tooltip } from '@deriv-com/ui';
const statusConfigs = {
blinking: {
@@ -20,15 +20,14 @@ const NetworkStatus = () => {
const { className, tooltip } = useMemo(() => statusConfigs[status], [status]);
return (
-
-
+
);
};
diff --git a/src/components/AppFooter/ResponsibleTrading.tsx b/src/components/AppFooter/ResponsibleTrading.tsx
index a9449baf..9c87029b 100644
--- a/src/components/AppFooter/ResponsibleTrading.tsx
+++ b/src/components/AppFooter/ResponsibleTrading.tsx
@@ -1,13 +1,13 @@
import { RESPONSIBLE } from '@/constants';
import { LegacyResponsibleTradingIcon } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
+import { Tooltip } from '@deriv-com/ui';
const ResponsibleTrading = () => {
const { localize } = useTranslations();
return (
-
{
tooltipContent={localize('Responsible trading')}
>
-
+
);
};
diff --git a/src/components/AppFooter/ServerTime.tsx b/src/components/AppFooter/ServerTime.tsx
index 9972a6f9..1311fdba 100644
--- a/src/components/AppFooter/ServerTime.tsx
+++ b/src/components/AppFooter/ServerTime.tsx
@@ -1,8 +1,7 @@
import { DATE_TIME_FORMAT_WITH_GMT, DATE_TIME_FORMAT_WITH_OFFSET } from '@/constants';
import { useSyncedTime } from '@/hooks';
import { epochToLocal, epochToUTC } from '@/utils';
-import { Text, useDevice } from '@deriv-com/ui';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
+import { Text, Tooltip, useDevice } from '@deriv-com/ui';
const ServerTime = () => {
const time = useSyncedTime();
@@ -11,15 +10,9 @@ const ServerTime = () => {
const { isDesktop } = useDevice();
return (
-
+
{UTCFormat}
-
+
);
};
diff --git a/src/components/AppFooter/WhatsApp.tsx b/src/components/AppFooter/WhatsApp.tsx
index 1c2fe02d..9540bfa6 100644
--- a/src/components/AppFooter/WhatsApp.tsx
+++ b/src/components/AppFooter/WhatsApp.tsx
@@ -1,13 +1,13 @@
import { LegacyWhatsappIcon } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
+import { Tooltip } from '@deriv-com/ui';
import { URLConstants } from '@deriv-com/utils';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
const WhatsApp = () => {
const { localize } = useTranslations();
return (
-
{
tooltipContent={localize('WhatsApp')}
>
-
+
);
};
diff --git a/src/components/AppHeader/AppHeader.tsx b/src/components/AppHeader/AppHeader.tsx
index 5fdca055..34ffe17f 100644
--- a/src/components/AppHeader/AppHeader.tsx
+++ b/src/components/AppHeader/AppHeader.tsx
@@ -1,13 +1,12 @@
import { useEffect } from 'react';
import { getOauthUrl } from '@/constants';
-import { api, useOAuth } from '@/hooks';
+import { api, useGrowthbookGetFeatureValue, useOAuth } from '@/hooks';
import { getCurrentRoute } from '@/utils';
import { StandaloneCircleUserRegularIcon } from '@deriv/quill-icons';
import { useAuthData } from '@deriv-com/api-hooks';
import { useTranslations } from '@deriv-com/translations';
-import { Button, Header, Text, useDevice, Wrapper } from '@deriv-com/ui';
+import { Button, Header, Text, Tooltip, useDevice, Wrapper } from '@deriv-com/ui';
import { LocalStorageUtils } from '@deriv-com/utils';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
import { AccountsInfoLoader } from './AccountsInfoLoader';
import { AccountSwitcher } from './AccountSwitcher';
import { AppLogo } from './AppLogo';
@@ -31,6 +30,9 @@ const AppHeader = () => {
document.documentElement.dir = instance.dir((currentLang || 'en').toLowerCase());
}, [currentLang, instance]);
const { oAuthLogout } = useOAuth();
+ const [isNotificationServiceEnabled] = useGrowthbookGetFeatureValue({
+ featureFlag: 'new_notifications_service_enabled',
+ });
const renderAccountSection = () => {
if (!isEndpointPage && !activeAccount) {
@@ -40,18 +42,17 @@ const AppHeader = () => {
if (activeLoginid) {
return (
<>
-
+ {isNotificationServiceEnabled &&
}
{isDesktop && (
-
-
+
)}
-
mutate({ upgrade_limits: 1 })} size='lg' textSize={textSize}>
+ {
+ mutate({ upgrade_limits: 1 });
+
+ //TODO: Remove this once implemented in BE
+ updateNotification({ ids: [], notifications_update_status: 'remove' });
+ }}
+ size='lg'
+ textSize={textSize}
+ >
>
diff --git a/src/components/Modals/DailyLimitModal/__tests__/DailyLimitModal.spec.tsx b/src/components/Modals/DailyLimitModal/__tests__/DailyLimitModal.spec.tsx
index 9d46c8bd..ca802f9a 100644
--- a/src/components/Modals/DailyLimitModal/__tests__/DailyLimitModal.spec.tsx
+++ b/src/components/Modals/DailyLimitModal/__tests__/DailyLimitModal.spec.tsx
@@ -21,6 +21,9 @@ jest.mock('@/hooks', () => ({
advertiser: {
useUpdate: jest.fn(() => mockUseAdvertiserUpdate),
},
+ notification: {
+ useUpdate: jest.fn(() => ({ mutate: jest.fn() })),
+ },
},
}));
diff --git a/src/components/Modals/OrderTimeTooltipModal/OrderTimeTooltipModal.tsx b/src/components/Modals/OrderTimeTooltipModal/OrderTimeTooltipModal.tsx
index 6a75eada..e59dc288 100644
--- a/src/components/Modals/OrderTimeTooltipModal/OrderTimeTooltipModal.tsx
+++ b/src/components/Modals/OrderTimeTooltipModal/OrderTimeTooltipModal.tsx
@@ -19,7 +19,7 @@ const OrderTimeTooltipModal = ({ isModalOpen, onRequestClose }: TOrderTimeToolti
-
+
diff --git a/src/components/Modals/OrderTimeTooltipModal/__tests__/OrderTimeTooltipModal.spec.tsx b/src/components/Modals/OrderTimeTooltipModal/__tests__/OrderTimeTooltipModal.spec.tsx
index 00b39c7b..bc7b3bc6 100644
--- a/src/components/Modals/OrderTimeTooltipModal/__tests__/OrderTimeTooltipModal.spec.tsx
+++ b/src/components/Modals/OrderTimeTooltipModal/__tests__/OrderTimeTooltipModal.spec.tsx
@@ -14,7 +14,7 @@ describe('', () => {
});
it('should handle the onclick for ok button', async () => {
render();
- const okButton = screen.getByRole('button', { name: 'Ok' });
+ const okButton = screen.getByRole('button', { name: 'OK' });
await userEvent.click(okButton);
expect(mockProps.onRequestClose).toBeCalledTimes(1);
});
diff --git a/src/components/PaymentMethodCard/PaymentMethodCardHeader/PaymentMethodCardHeader.scss b/src/components/PaymentMethodCard/PaymentMethodCardHeader/PaymentMethodCardHeader.scss
index 9f1c5b37..0a4498e4 100644
--- a/src/components/PaymentMethodCard/PaymentMethodCardHeader/PaymentMethodCardHeader.scss
+++ b/src/components/PaymentMethodCard/PaymentMethodCardHeader/PaymentMethodCardHeader.scss
@@ -21,6 +21,7 @@
width: 12.8rem;
border: 0;
padding: 0;
+ margin-top: unset;
}
.deriv-input {
diff --git a/src/components/PaymentMethodCard/PaymentMethodCardHeader/PaymentMethodCardHeader.tsx b/src/components/PaymentMethodCard/PaymentMethodCardHeader/PaymentMethodCardHeader.tsx
index c4cc1fa9..6d939b8d 100644
--- a/src/components/PaymentMethodCard/PaymentMethodCardHeader/PaymentMethodCardHeader.tsx
+++ b/src/components/PaymentMethodCard/PaymentMethodCardHeader/PaymentMethodCardHeader.tsx
@@ -61,8 +61,8 @@ const PaymentMethodCardHeader = ({
/>
{isEditable && (
}
className='payment-method-card__header-dropdown'
- dropdownIcon={}
list={getActions(localize)}
name='payment-method-actions'
onSelect={value => {
diff --git a/src/components/PaymentMethodForm/PaymentMethodFormAutocomplete/PaymentMethodFormAutocomplete.tsx b/src/components/PaymentMethodForm/PaymentMethodFormAutocomplete/PaymentMethodFormAutocomplete.tsx
index e1aacfad..76edfbcd 100644
--- a/src/components/PaymentMethodForm/PaymentMethodFormAutocomplete/PaymentMethodFormAutocomplete.tsx
+++ b/src/components/PaymentMethodForm/PaymentMethodFormAutocomplete/PaymentMethodFormAutocomplete.tsx
@@ -1,3 +1,4 @@
+import { MouseEvent } from 'react';
import { TFormState, THooks, TPaymentMethod, TSelectedPaymentMethod } from 'types';
import { LabelPairedSearchMdRegularIcon, LegacyCloseCircle1pxBlackIcon } from '@deriv/quill-icons';
import { Localize, useTranslations } from '@deriv-com/translations';
@@ -52,27 +53,34 @@ const PaymentMethodFormAutocomplete = ({
}
return '';
};
+
+ const handleDropdownClick = (event: MouseEvent) => {
+ event.preventDefault();
+ };
return (
<>
- }
- isFullWidth
- label={localize('Payment method')}
- list={availablePaymentMethodsList}
- name='Payment method'
- onSelect={value => {
- const selectedPaymentMethod = availablePaymentMethods?.find(p => p.id === value);
- if (selectedPaymentMethod) {
- onAdd?.({
- displayName: selectedPaymentMethod?.display_name,
- fields: selectedPaymentMethod?.fields,
- method: selectedPaymentMethod?.id,
- });
- }
- }}
- value={getValue()}
- variant='prompt'
- />
+ {/* To prevent default submission */}
+
+ }
+ isFullWidth
+ label={localize('Payment method')}
+ list={availablePaymentMethodsList}
+ name='Payment method'
+ onSelect={value => {
+ const selectedPaymentMethod = availablePaymentMethods?.find(p => p.id === value);
+ if (selectedPaymentMethod) {
+ onAdd?.({
+ displayName: selectedPaymentMethod?.display_name,
+ fields: selectedPaymentMethod?.fields,
+ method: selectedPaymentMethod?.id,
+ });
+ }
+ }}
+ value={getValue()}
+ variant='prompt'
+ />
+
diff --git a/src/components/PopoverDropdown/PopoverDropdown.scss b/src/components/PopoverDropdown/PopoverDropdown.scss
index cb20731c..b567a535 100644
--- a/src/components/PopoverDropdown/PopoverDropdown.scss
+++ b/src/components/PopoverDropdown/PopoverDropdown.scss
@@ -2,18 +2,26 @@
display: flex;
flex-direction: column;
position: relative;
+ justify-content: center;
+ padding: 0.1rem 0.8rem;
- & .tooltip-menu-icon {
- width: 3.2rem;
- display: flex;
- justify-content: center;
- border-radius: 4px;
+ @include desktop {
+ width: 4.2rem;
+ }
+
+ @include mobile-or-tablet-screen {
+ margin-top: -1rem;
+ }
- @include mobile-or-tablet-screen {
- margin-top: -0.5rem;
+ @include desktop {
+ & .deriv-tooltip__trigger {
+ display: flex;
+ width: 100%;
+ justify-content: center;
&:hover {
- background-color: transparent;
+ border-radius: 4px;
+ background-color: #e6e9e9;
}
}
}
diff --git a/src/components/PopoverDropdown/PopoverDropdown.tsx b/src/components/PopoverDropdown/PopoverDropdown.tsx
index 7bb44723..f85ad505 100644
--- a/src/components/PopoverDropdown/PopoverDropdown.tsx
+++ b/src/components/PopoverDropdown/PopoverDropdown.tsx
@@ -1,8 +1,7 @@
import { useRef, useState } from 'react';
import { useOnClickOutside } from 'usehooks-ts';
import { LabelPairedEllipsisVerticalLgBoldIcon } from '@deriv/quill-icons';
-import { Button, Text, useDevice } from '@deriv-com/ui';
-import { TooltipMenuIcon } from '../TooltipMenuIcon';
+import { Button, Text, Tooltip, useDevice } from '@deriv-com/ui';
import './PopoverDropdown.scss';
type TItem = {
@@ -28,13 +27,15 @@ const PopoverDropdown = ({ dropdownList, isBarred, onClick, tooltipMessage }: TP
{isBarred ? (
) : (
- setVisible(prevState => !prevState)}
tooltipContent={tooltipMessage}
+ tooltipPosition='top'
>
-
+
)}
{visible && (
diff --git a/src/components/ProfileContent/ProfileDailyLimit/ProfileDailyLimit.tsx b/src/components/ProfileContent/ProfileDailyLimit/ProfileDailyLimit.tsx
index 25a7e6a0..5639575c 100644
--- a/src/components/ProfileContent/ProfileDailyLimit/ProfileDailyLimit.tsx
+++ b/src/components/ProfileContent/ProfileDailyLimit/ProfileDailyLimit.tsx
@@ -3,6 +3,7 @@ import { api } from '@/hooks';
import { useAdvertiserStats, useModalManager } from '@/hooks/custom-hooks';
import { Localize } from '@deriv-com/translations';
import { Button, Text, useDevice } from '@deriv-com/ui';
+import { FormatUtils } from '@deriv-com/utils';
import './ProfileDailyLimit.scss';
const ProfileDailyLimit = () => {
@@ -21,8 +22,12 @@ const ProfileDailyLimit = () => {
i18n_default_text='Want to increase your daily limits to <0>{{maxDailyBuy}} {{currency}}0> (buy) and <1>{{maxDailySell}} {{currency}}1> (sell)?'
values={{
currency: activeAccount?.currency,
- maxDailyBuy: advertiserStats?.daily_buy_limit,
- maxDailySell: advertiserStats?.daily_sell_limit,
+ maxDailyBuy: FormatUtils.formatMoney(
+ Number(advertiserStats?.upgradable_daily_limits?.max_daily_buy)
+ ),
+ maxDailySell: FormatUtils.formatMoney(
+ Number(advertiserStats?.upgradable_daily_limits?.max_daily_sell)
+ ),
}}
/>
diff --git a/src/components/TextField/HelperMessage.tsx b/src/components/TextField/HelperMessage.tsx
deleted file mode 100644
index 51a6d7b1..00000000
--- a/src/components/TextField/HelperMessage.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { ComponentProps, FC, InputHTMLAttributes, memo } from 'react';
-import { Text } from '@deriv-com/ui';
-
-export type HelperMessageProps = {
- inputValue?: InputHTMLAttributes
['value'];
- isError?: boolean;
- maxLength?: InputHTMLAttributes['maxLength'];
- message?: string;
- messageVariant?: 'error' | 'general' | 'warning';
-};
-
-const HelperMessage: FC = memo(
- ({ inputValue, isError, maxLength, message, messageVariant = 'general' }) => {
- const HelperMessageColors: Record['color']> = {
- error: 'error',
- general: 'less-prominent',
- warning: 'warning',
- };
-
- return (
- <>
- {message && (
-
-
- {message}
-
-
- )}
- {maxLength && (
-
-
- {inputValue?.toString().length || 0} / {maxLength}
-
-
- )}
- >
- );
- }
-);
-
-HelperMessage.displayName = 'HelperMessage';
-export default HelperMessage;
diff --git a/src/components/TextField/TextField.scss b/src/components/TextField/TextField.scss
deleted file mode 100644
index b130873a..00000000
--- a/src/components/TextField/TextField.scss
+++ /dev/null
@@ -1,143 +0,0 @@
-.textfield {
- min-width: 12rem;
- width: 100%;
- position: relative;
- display: flex;
- flex-direction: column;
- gap: 0.2rem;
-
- &--error {
- .textfield__box,
- .textfield__box:hover {
- border: 1px solid var(--status-light-danger, #ec3f3f);
- }
-
- .textfield__box:has(.textfield__field:focus) {
- border: 1px solid var(--brand-blue, #ec3f3f);
- }
-
- .textfield__box:has(.textfield__field:valid) {
- border: 1px solid var(--brand-blue, #ec3f3f);
- }
-
- .textfield__label {
- color: #ec3f3f;
- }
-
- .textfield__field:focus ~ .textfield__label {
- color: #ec3f3f;
- }
- }
-
- &--disabled {
- & .textfield__box,
- .textfield__box:hover {
- border: 1px solid var(--system-light-5-active-background, #eaeced);
-
- & input {
- color: var(--system-light-5-active-background, #999);
- background: transparent;
- }
- }
- & .textfield__box:has(.textfield__field:focus) {
- border: 1px solid var(--system-light-5-active-background, #eaeced);
- }
- & .textfield__field {
- background: inherit;
- }
- }
-
- &__box {
- height: 4rem;
- width: 100%;
- border-radius: 0.4rem;
- padding: 1rem 1.6rem;
- border: 1px solid var(--system-light-5-active-background, #d6dadb);
- display: inline-flex;
- align-items: center;
- transition: border-color 0.2s;
-
- &:hover {
- border-color: var(--system-light-3-less-prominent-text, #999);
- }
- }
-
- &__box:has(&__field:focus) {
- border: 1px solid var(--brand-blue, #85acb0);
- }
-
- &__box:has(&__field:invalid) {
- border: 1px solid var(--status-light-danger, #ec3f3f);
- }
-
- &__field {
- min-width: 0;
- font-family: inherit;
- outline: 0;
- font-size: 1.4rem;
- color: var(--system-light-2-general-text, #333);
- transition: border-color 0.2s;
- flex: 1;
- }
-
- &__field::placeholder {
- color: transparent;
- }
-
- &__field:placeholder-shown ~ &__label {
- font-size: 1.4rem;
- cursor: text;
- top: 2rem;
- padding: 0;
- }
-
- label,
- &__field:focus ~ &__label {
- position: absolute;
- top: 0%;
- transform: translateY(-50%);
- display: block;
- transition: 0.2s;
- font-size: 1rem;
- color: var(--system-light-3-less-prominent-text, #999);
- background: var(--system-light-8-primary-background, #fff);
- padding-inline: 0.4rem;
- left: 1.6rem;
- -webkit-user-select: none;
- -ms-user-select: none;
- user-select: none;
- }
-
- &__field:focus ~ &__label {
- color: var(--brand-blue, #85acb0);
- }
-
- &__field:invalid ~ &__label {
- color: var(--status-light-danger, #ec3f3f);
- }
-
- &__icon {
- &-left {
- margin-right: 0.8rem;
- }
-
- &-right {
- margin-left: 1.6rem;
- }
- }
-
- &__message-container {
- height: 2rem;
- padding: 0rem 0rem 0rem 1.6rem;
- width: 100%;
-
- &--maxchar {
- float: right;
- }
-
- &--msg {
- float: left;
- text-align: left;
- }
- }
-}
diff --git a/src/components/TextField/TextField.tsx b/src/components/TextField/TextField.tsx
deleted file mode 100644
index 89c75860..00000000
--- a/src/components/TextField/TextField.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import { ChangeEvent, ComponentProps, forwardRef, ReactNode, Ref, useState } from 'react';
-import clsx from 'clsx';
-import HelperMessage, { HelperMessageProps } from './HelperMessage';
-import './TextField.scss';
-
-export interface TextFieldProps extends ComponentProps<'input'>, HelperMessageProps {
- defaultValue?: string;
- disabled?: boolean;
- errorMessage?: string[] | string;
- isInvalid?: boolean;
- label?: string;
- renderLeftIcon?: () => ReactNode;
- renderRightIcon?: () => ReactNode;
- shouldShowWarningMessage?: boolean;
- showMessage?: boolean;
-}
-
-const TextField = forwardRef(
- (
- {
- defaultValue = '',
- disabled,
- errorMessage,
- isInvalid,
- label,
- maxLength,
- message,
- messageVariant = 'general',
- name = 'textField',
- onChange,
- renderLeftIcon,
- renderRightIcon,
- shouldShowWarningMessage = false,
- showMessage = false,
- ...rest
- }: TextFieldProps,
- ref: Ref
- ) => {
- const [value, setValue] = useState(defaultValue);
-
- const handleChange = (e: ChangeEvent) => {
- const newValue = e.target.value;
- setValue(newValue);
- onChange?.(e);
- };
-
- return (
-
-
- {typeof renderLeftIcon === 'function' && (
-
{renderLeftIcon()}
- )}
-
- {label && (
-
- )}
- {typeof renderRightIcon === 'function' && (
-
{renderRightIcon()}
- )}
-
-
- {showMessage && !isInvalid && (
-
- )}
- {errorMessage && (isInvalid || (!isInvalid && shouldShowWarningMessage)) && (
-
- )}
-
-
- );
- }
-);
-
-TextField.displayName = 'TextField';
-export default TextField;
diff --git a/src/components/TextField/index.ts b/src/components/TextField/index.ts
deleted file mode 100644
index 97d7c656..00000000
--- a/src/components/TextField/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// TODO: Delete this component once @deriv-com/ui has it published
-export { default as TextField } from './TextField';
diff --git a/src/components/TooltipMenuIcon/TooltipMenuIcon.scss b/src/components/TooltipMenuIcon/TooltipMenuIcon.scss
deleted file mode 100644
index 94c03f33..00000000
--- a/src/components/TooltipMenuIcon/TooltipMenuIcon.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-.tooltip-menu-icon {
- &:hover {
- background: #e6e9e9;
- }
-}
diff --git a/src/components/TooltipMenuIcon/TooltipMenuIcon.tsx b/src/components/TooltipMenuIcon/TooltipMenuIcon.tsx
deleted file mode 100644
index 951a9615..00000000
--- a/src/components/TooltipMenuIcon/TooltipMenuIcon.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { ComponentProps, ElementType, PropsWithChildren } from 'react';
-import clsx from 'clsx';
-import { extendTheme, ThemeProvider, Tooltip, TooltipProps } from '@chakra-ui/react';
-import './TooltipMenuIcon.scss';
-
-const theme = extendTheme({
- zIndices: {
- tooltip: 1800,
- },
-});
-
-type AsElement = 'a' | 'button' | 'div';
-type TTooltipMenuIcon = ComponentProps & {
- as: T;
- disableHover?: boolean;
- tooltipContent: string;
- tooltipPosition?: TooltipProps['placement'];
-};
-
-// TODO replace this with deriv/ui
-const TooltipMenuIcon = ({
- as,
- children,
- className,
- disableHover = false,
- tooltipContent,
- tooltipPosition = 'top',
- ...rest
-}: PropsWithChildren>) => {
- const Tag = as as ElementType;
-
- return (
-
-
-
- {children}
-
-
-
- );
-};
-
-export default TooltipMenuIcon;
diff --git a/src/components/TooltipMenuIcon/__test__/TooltipMenuIcon.spec.tsx b/src/components/TooltipMenuIcon/__test__/TooltipMenuIcon.spec.tsx
deleted file mode 100644
index 3c34da64..00000000
--- a/src/components/TooltipMenuIcon/__test__/TooltipMenuIcon.spec.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { render, screen } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import { TooltipMenuIcon } from '..';
-
-describe('TooltipMenuIcon Component', () => {
- it('renders correctly with default props', () => {
- render(
-
- Hover me
-
- );
- expect(screen.getByRole('button')).toHaveTextContent('Hover me');
- });
-
- it('displays tooltip on hover', async () => {
- render(
-
- Hover me
-
- );
- await userEvent.hover(screen.getByRole('button'));
- expect(screen.getByText('Tooltip text')).toBeInTheDocument();
- });
-
- it('accepts and applies custom tooltip position', async () => {
- render(
-
- Hover me
-
- );
- await userEvent.hover(screen.getByRole('button'));
- expect(screen.getByText('Tooltip text')).toBeInTheDocument();
- });
-
- it("renders correctly with as='a'", () => {
- render(
-
- Hover me
-
- );
- expect(screen.getByRole('link')).toHaveTextContent('Hover me');
- });
-});
diff --git a/src/components/TooltipMenuIcon/index.ts b/src/components/TooltipMenuIcon/index.ts
deleted file mode 100644
index 6cb8e0f8..00000000
--- a/src/components/TooltipMenuIcon/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default as TooltipMenuIcon } from './TooltipMenuIcon';
diff --git a/src/components/index.ts b/src/components/index.ts
index a460022e..ed4d0a79 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -14,7 +14,6 @@ export * from './FileDropzone';
export * from './FloatingRate';
export * from './FormProgress';
export * from './FullPageMobileWrapper';
-export * from './Input';
export * from './LightDivider';
export * from './MobileTabs';
export * from './OnlineStatus';
@@ -33,7 +32,6 @@ export * from './Search';
export * from './StarRating';
export * from './Table';
export * from './TemporarilyBarredHint';
-export * from './TextField';
export * from './UserAvatar';
export * from './Verification';
export * from './Wizard';
diff --git a/src/constants/api-error-codes.ts b/src/constants/api-error-codes.ts
index 35900e95..731ee6db 100644
--- a/src/constants/api-error-codes.ts
+++ b/src/constants/api-error-codes.ts
@@ -1,4 +1,5 @@
export const ERROR_CODES = {
+ ACCOUNT_DISABLED: 'AccountDisabled',
AD_EXCEEDS_BALANCE: 'advertiser_balance',
AD_EXCEEDS_DAILY_LIMIT: 'advertiser_daily_limit',
ADVERT_INACTIVE: 'advert_inactive',
@@ -17,4 +18,5 @@ export const ERROR_CODES = {
ORDER_CREATE_FAIL_RATE_SLIPPAGE: 'OrderCreateFailRateSlippage',
ORDER_EMAIL_VERIFICATION_REQUIRED: 'OrderEmailVerificationRequired',
PERMISSION_DENIED: 'PermissionDenied',
+ TEMPORARY_BAR: 'TemporaryBar',
} as const;
diff --git a/src/constants/index.ts b/src/constants/index.ts
index 01676826..7a662fd7 100644
--- a/src/constants/index.ts
+++ b/src/constants/index.ts
@@ -4,6 +4,7 @@ export * from './buy-sell';
export * from './chat-constants';
export * from './firebase-init-data';
export * from './languages';
+export * from './notification';
export * from './orders';
export * from './p2p-logo';
export * from './payment-methods';
diff --git a/src/constants/notification.ts b/src/constants/notification.ts
new file mode 100644
index 00000000..c9d9f856
--- /dev/null
+++ b/src/constants/notification.ts
@@ -0,0 +1,40 @@
+import { TLocalize } from 'types';
+import { FormatUtils } from '@deriv-com/utils';
+
+export const getNotification = (localize: TLocalize, messageKey: string, payload?: string) => {
+ const values = payload ? JSON.parse(payload) : {};
+ const notification = {
+ actionText: '',
+ message: '',
+ title: '',
+ };
+
+ switch (messageKey) {
+ case 'p2p-order-complete':
+ notification.title = localize('Your order {{order_id}} is complete', values);
+ notification.message = localize(
+ '{{name}} has released your funds. Would you like to give your feedback?',
+ values
+ );
+ notification.actionText = localize('Give feedback', values);
+
+ break;
+ case 'p2p-limit-upgrade-available':
+ notification.title = localize('Enjoy higher daily limits', values);
+ notification.message = localize(
+ 'Would you like to increase your daily limits to {{new_buy_limit}} {{currency}} (buy) and {{new_sell_limit}} {{currency}} (sell)?',
+ {
+ currency: values.account_currency,
+ new_buy_limit: FormatUtils.formatMoney(values.new_buy_limit),
+ new_sell_limit: FormatUtils.formatMoney(values.new_sell_limit),
+ }
+ );
+ notification.actionText = localize('Yes, increase my limits');
+
+ break;
+ default:
+ notification.message = messageKey;
+ }
+
+ return notification;
+};
diff --git a/src/hooks/api/account/useActiveAccount.ts b/src/hooks/api/account/useActiveAccount.ts
index f72ace20..1e46b165 100644
--- a/src/hooks/api/account/useActiveAccount.ts
+++ b/src/hooks/api/account/useActiveAccount.ts
@@ -5,7 +5,7 @@ import { useAccountList, useAuthData } from '@deriv-com/api-hooks';
/** A custom hook that returns the account object for the current active account. */
const useActiveAccount = () => {
const { data, ...rest } = useAccountList();
- const { activeLoginid } = useAuthData();
+ const { activeLoginid, error } = useAuthData();
const { data: balanceData } = api.account.useBalance();
const activeAccount = useMemo(
() => data?.find(account => account.loginid === activeLoginid),
@@ -23,6 +23,7 @@ const useActiveAccount = () => {
return {
/** User's current active account. */
+ authError: error,
data: modifiedAccount,
...rest,
};
diff --git a/src/hooks/api/index.ts b/src/hooks/api/index.ts
index 46ae17ba..e296660f 100644
--- a/src/hooks/api/index.ts
+++ b/src/hooks/api/index.ts
@@ -4,6 +4,7 @@ export * from './advertiser';
export * from './chat';
export * from './counterparty';
export * from './exchange-rates';
+export * as notification from './notification';
export * from './order';
export * from './order-dispute';
export * from './order-review';
diff --git a/src/hooks/api/notification/__tests__/useNotificationList.spec.ts b/src/hooks/api/notification/__tests__/useNotificationList.spec.ts
new file mode 100644
index 00000000..bf2a772e
--- /dev/null
+++ b/src/hooks/api/notification/__tests__/useNotificationList.spec.ts
@@ -0,0 +1,39 @@
+import { renderHook } from '@testing-library/react';
+import useNotificationList from '../useNotificationList';
+
+const mockNotificationListData = {
+ data: {
+ notifications_list: {
+ messages: [
+ {
+ category: 'see',
+ id: 1,
+ message_key: 'p2p-limit-upgrade-available',
+ },
+ {
+ category: 'see',
+ id: 2,
+ message_key: 'poi-verified',
+ },
+ ],
+ },
+ },
+};
+
+jest.mock('@deriv-com/api-hooks', () => ({
+ useSubscribe: jest.fn(() => mockNotificationListData),
+}));
+
+describe('useNotificationList', () => {
+ it('should return the list of p2p-related notifications', () => {
+ const { result } = renderHook(() => useNotificationList());
+
+ expect(result.current.data).toEqual([
+ {
+ category: 'see',
+ id: 1,
+ message_key: 'p2p-limit-upgrade-available',
+ },
+ ]);
+ });
+});
diff --git a/src/hooks/api/notification/index.ts b/src/hooks/api/notification/index.ts
new file mode 100644
index 00000000..0db7a4b1
--- /dev/null
+++ b/src/hooks/api/notification/index.ts
@@ -0,0 +1,2 @@
+export { default as useGetList } from './useNotificationList';
+export { default as useUpdate } from './useNotificationUpdate';
diff --git a/src/hooks/api/notification/useNotificationList.ts b/src/hooks/api/notification/useNotificationList.ts
new file mode 100644
index 00000000..09a7aad1
--- /dev/null
+++ b/src/hooks/api/notification/useNotificationList.ts
@@ -0,0 +1,77 @@
+import { useEffect, useMemo, useState } from 'react';
+import { useSubscribe } from '@deriv-com/api-hooks';
+
+type TNotificationLinks = {
+ href: string;
+ rel: string;
+};
+type TNotification = {
+ category: string;
+ id: number;
+ links: TNotificationLinks[];
+ message_key: string;
+ payload: string;
+ read: boolean;
+ removed: boolean;
+};
+
+const handleData = (incomingMessages: TNotification[], prevMessages: TNotification[]) => {
+ if (!incomingMessages) return prevMessages;
+
+ let notifications = prevMessages;
+ for (let updateIdx = 0; updateIdx < incomingMessages.length; updateIdx++) {
+ const update = incomingMessages[updateIdx];
+
+ const existingMessageIndex = notifications.findIndex((message: TNotification) => message.id === update.id);
+ const existingMessage = notifications[existingMessageIndex];
+
+ if (existingMessage) {
+ if (update.removed)
+ notifications = notifications.filter((message: TNotification) => message.id !== update.id);
+ else notifications[existingMessageIndex] = { ...existingMessage, ...update };
+ } else notifications.unshift(update);
+ }
+
+ notifications.sort((a: TNotification, b: TNotification) => b.id - a.id);
+
+ return [...notifications];
+};
+
+/**
+ * Hook that returns the list of notifications.
+ *
+ * @example const { data: notifications } = useNotificationList();
+ */
+const useNotificationList = () => {
+ // @ts-expect-error Type undefined. This endpoint will be added to api-hooks.
+ const { data, ...rest } = useSubscribe('notifications_list');
+ const [messages, setMessages] = useState([]);
+
+ const modified_data = useMemo(() => {
+ if (!messages) return undefined;
+
+ // TODO: Remove this filter once all the notifications are implemented
+ const notifications = messages.filter((notification: { message_key: string }) =>
+ ['p2p-limit-upgrade-available'].includes(notification.message_key)
+ );
+
+ return notifications;
+ }, [messages]);
+
+ useEffect(() => {
+ // @ts-expect-error Type undefined.
+ if (data?.notifications_list) {
+ setMessages(prevMessages => {
+ // @ts-expect-error Type undefined.
+ return handleData(data.notifications_list.messages, prevMessages);
+ });
+ }
+ }, [data]);
+
+ return {
+ data: modified_data || [],
+ ...rest,
+ };
+};
+
+export default useNotificationList;
diff --git a/src/hooks/api/notification/useNotificationUpdate.ts b/src/hooks/api/notification/useNotificationUpdate.ts
new file mode 100644
index 00000000..8eeb47f5
--- /dev/null
+++ b/src/hooks/api/notification/useNotificationUpdate.ts
@@ -0,0 +1,25 @@
+import { useMutation } from '@deriv-com/api-hooks';
+
+/**
+ * Hook that updates the status of a notification. The notification can be removed or marked as read or unread.
+ *
+ * @example
+ * const { data, mutate } = useNotificationUpdate();
+ * mutate({ notifications_update_status: 'read', ids: [notification_id]});
+ * mutate({ notifications_update_status: 'unread', ids: [notification_id]});
+ * mutate({ notifications_update_status: 'remove', ids: []});
+ */
+const useNotificationUpdate = () => {
+ const { data, ...rest } = useMutation({
+ // @ts-expect-error Type undefined. This endpoint will be added to api-hooks.
+ name: 'notifications_update_status',
+ });
+
+ return {
+ // @ts-expect-error Type undefined.
+ data: data?.notifications_update_status,
+ ...rest,
+ };
+};
+
+export default useNotificationUpdate;
diff --git a/src/hooks/custom-hooks/index.ts b/src/hooks/custom-hooks/index.ts
index 64e06c1b..a8fe9d0b 100644
--- a/src/hooks/custom-hooks/index.ts
+++ b/src/hooks/custom-hooks/index.ts
@@ -6,6 +6,7 @@ export { default as useExtendedOrderDetails } from './useExtendedOrderDetails';
export { default as useFetchMore } from './useFetchMore';
export { default as useFloatingRate } from './useFloatingRate';
export { default as useFullScreen } from './useFullScreen';
+export { default as useGrowthbookGetFeatureValue } from './useGrowthbookGetFeatureValue';
export { default as useHandleRouteChange } from './useHandleRouteChange';
export { default as useIsAdvertiser } from './useIsAdvertiser';
export { default as useIsAdvertiserBarred } from './useIsAdvertiserBarred';
diff --git a/src/hooks/custom-hooks/useIsAdvertiserBarred.ts b/src/hooks/custom-hooks/useIsAdvertiserBarred.ts
index 26009ad7..fa6b31b2 100644
--- a/src/hooks/custom-hooks/useIsAdvertiserBarred.ts
+++ b/src/hooks/custom-hooks/useIsAdvertiserBarred.ts
@@ -1,4 +1,4 @@
-import { useEffect, useState } from 'react';
+import { useMemo } from 'react';
import useInvalidateQuery from '../api/useInvalidateQuery';
import { api } from '..';
@@ -8,16 +8,14 @@ import { api } from '..';
*/
const useIsAdvertiserBarred = (): boolean => {
const { data = {} } = api.advertiser.useGetInfo();
- const [isAdvertiserBarred, setIsAdvertiserBarred] = useState(false);
const invalidate = useInvalidateQuery();
- useEffect(() => {
- if (isAdvertiserBarred !== !!data.blocked_until) {
- invalidate('p2p_advertiser_adverts');
- setIsAdvertiserBarred(!!data.blocked_until);
- }
+ const isAdvertiserBarred = useMemo(() => {
+ invalidate('p2p_advertiser_adverts');
+ return !!data.blocked_until;
+
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [isAdvertiserBarred, data.blocked_until]);
+ }, [data.blocked_until]);
return isAdvertiserBarred;
};
diff --git a/src/pages/advertiser/screens/AdvertiserBlockOverlay/AdvertiserBlockOverlay.tsx b/src/pages/advertiser/screens/AdvertiserBlockOverlay/AdvertiserBlockOverlay.tsx
index 298aa820..480e605d 100644
--- a/src/pages/advertiser/screens/AdvertiserBlockOverlay/AdvertiserBlockOverlay.tsx
+++ b/src/pages/advertiser/screens/AdvertiserBlockOverlay/AdvertiserBlockOverlay.tsx
@@ -32,7 +32,10 @@ const AdvertiserBlockOverlay = ({
size={isDesktop ? 'md' : 'lg'}
weight='bold'
>
-
+
{children}
- setShowOverlay(false)}
- onRequestClose={hideModal}
- />
+ {isModalOpenFor('BlockUnblockUserModal') && (
+ setShowOverlay(false)}
+ onRequestClose={hideModal}
+ />
+ )}
);
}
diff --git a/src/pages/buy-sell/components/SortDropdown/SortDropdown.scss b/src/pages/buy-sell/components/SortDropdown/SortDropdown.scss
index 7de7291a..ab0dfd64 100644
--- a/src/pages/buy-sell/components/SortDropdown/SortDropdown.scss
+++ b/src/pages/buy-sell/components/SortDropdown/SortDropdown.scss
@@ -1,6 +1,6 @@
.sort-dropdown {
.deriv-dropdown__items {
- top: 4.4rem;
+ top: 3.6rem;
width: 24rem;
}
diff --git a/src/pages/buy-sell/components/SortDropdown/SortDropdown.tsx b/src/pages/buy-sell/components/SortDropdown/SortDropdown.tsx
index 28fc7e49..08662594 100644
--- a/src/pages/buy-sell/components/SortDropdown/SortDropdown.tsx
+++ b/src/pages/buy-sell/components/SortDropdown/SortDropdown.tsx
@@ -32,7 +32,7 @@ const SortDropdown = ({ list, onSelect, setIsFilterModalOpen, value }: TSortDrop
return (
}
+ chevronIcon={
}
label={localize('Sort by')}
list={list as unknown as MutableOption[]}
name='Sort by'
diff --git a/src/pages/buy-sell/screens/BuySell/BuySell.scss b/src/pages/buy-sell/screens/BuySell/BuySell.scss
index a5c7410a..59fcd8b2 100644
--- a/src/pages/buy-sell/screens/BuySell/BuySell.scss
+++ b/src/pages/buy-sell/screens/BuySell/BuySell.scss
@@ -1,5 +1,10 @@
.buy-sell {
height: 100%;
+
+ &--not-verified {
+ overflow-y: auto;
+ height: calc(100% - 11rem);
+ }
& .buy-sell-table {
& .table {
@include mobile-or-tablet-screen {
diff --git a/src/pages/buy-sell/screens/BuySell/BuySell.tsx b/src/pages/buy-sell/screens/BuySell/BuySell.tsx
index 23ae112b..35cb0b92 100644
--- a/src/pages/buy-sell/screens/BuySell/BuySell.tsx
+++ b/src/pages/buy-sell/screens/BuySell/BuySell.tsx
@@ -16,14 +16,14 @@ const BuySell = () => {
if (poiPoaVerified === 'false') {
return (
- <>
+
history.replace({ pathname: BUY_SELL_URL, search: '' })}
pageTitle={localize('Verification')}
weight='bold'
/>
- >
+
);
}
diff --git a/src/pages/buy-sell/screens/BuySellTable/BuySellTableRenderer/BuySellTableRenderer.tsx b/src/pages/buy-sell/screens/BuySellTable/BuySellTableRenderer/BuySellTableRenderer.tsx
index 976cfc05..a9a3964f 100644
--- a/src/pages/buy-sell/screens/BuySellTable/BuySellTableRenderer/BuySellTableRenderer.tsx
+++ b/src/pages/buy-sell/screens/BuySellTable/BuySellTableRenderer/BuySellTableRenderer.tsx
@@ -41,30 +41,32 @@ const BuySellTableRenderer = ({
if ((!data && !searchValue) || (data.length === 0 && !searchValue)) {
return (
-
-
history.push(MY_ADS_URL)}
- size='lg'
- textSize={isMobile ? 'md' : 'sm'}
- >
-
-
- }
- description={
-
-
-
- }
- icon={}
- title={
-
-
-
- }
- />
+
+
+
history.push(MY_ADS_URL)}
+ size='lg'
+ textSize={isMobile ? 'md' : 'sm'}
+ >
+
+
+ }
+ description={
+
+
+
+ }
+ icon={}
+ title={
+
+
+
+ }
+ />
+
);
}
diff --git a/src/pages/buy-sell/screens/BuySellTable/__tests__/BuySellTable.spec.tsx b/src/pages/buy-sell/screens/BuySellTable/__tests__/BuySellTable.spec.tsx
index a731c6ad..5aadb98a 100644
--- a/src/pages/buy-sell/screens/BuySellTable/__tests__/BuySellTable.spec.tsx
+++ b/src/pages/buy-sell/screens/BuySellTable/__tests__/BuySellTable.spec.tsx
@@ -235,6 +235,7 @@ describe('', () => {
});
it('should call history.replace if user clicks on Buy/Sell button and POA/POI is not verified', async () => {
+ mockUseIsAdvertiser = false;
mockUseModalManager.isModalOpenFor.mockReturnValue(false);
mockUsePoiPoaStatus.mockReturnValueOnce({ data: { isPoaVerified: false, isPoiVerified: false } });
diff --git a/src/pages/endpoint/screens/Endpoint/Endpoint.tsx b/src/pages/endpoint/screens/Endpoint/Endpoint.tsx
index 2e1face3..e1c4ff42 100644
--- a/src/pages/endpoint/screens/Endpoint/Endpoint.tsx
+++ b/src/pages/endpoint/screens/Endpoint/Endpoint.tsx
@@ -1,4 +1,5 @@
import { Controller, useForm } from 'react-hook-form';
+import { getServerInfo } from '@/constants';
import { Button, Input, Text } from '@deriv-com/ui';
import { LocalStorageConstants, LocalStorageUtils } from '@deriv-com/utils';
import './Endpoint.scss';
@@ -86,7 +87,7 @@ const Endpoint = () => {
LocalStorageUtils.setValue(LocalStorageConstants.configServerURL, '');
LocalStorageUtils.setValue(LocalStorageConstants.configAppId, '');
reset();
- window.location.reload();
+ getServerInfo();
}}
variant='outlined'
>
diff --git a/src/pages/endpoint/screens/Endpoint/__tests__/Endpoint.spec.tsx b/src/pages/endpoint/screens/Endpoint/__tests__/Endpoint.spec.tsx
index 984e5b81..bc22acc7 100644
--- a/src/pages/endpoint/screens/Endpoint/__tests__/Endpoint.spec.tsx
+++ b/src/pages/endpoint/screens/Endpoint/__tests__/Endpoint.spec.tsx
@@ -2,6 +2,12 @@ import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Endpoint from '../Endpoint';
+const mockGetServerInfo = jest.fn();
+
+jest.mock('@/constants', () => ({
+ getServerInfo: jest.fn(() => mockGetServerInfo()),
+}));
+
describe('', () => {
it('should render the endpoint component', () => {
render();
@@ -28,7 +34,7 @@ describe('', () => {
expect(JSON.parse(localStorage.getItem('config.app_id') || '')).toBe('123');
});
- it('should reset the server_url and app_id when user clicks on the reset button', async () => {
+ it('should call getServerInfo and reset the inputs when user clicks on the reset button', async () => {
render();
const serverUrlInput = screen.getByTestId('dt_endpoint_server_url_input');
@@ -41,5 +47,6 @@ describe('', () => {
expect(JSON.parse(localStorage.getItem('config.server_url') || '')).toBe('');
expect(JSON.parse(localStorage.getItem('config.app_id') || '')).toBe('');
+ expect(mockGetServerInfo).toHaveBeenCalled();
});
});
diff --git a/src/pages/my-ads/components/AdTypeSection/AdTypeSection.tsx b/src/pages/my-ads/components/AdTypeSection/AdTypeSection.tsx
index 2c96626e..c2852a8f 100644
--- a/src/pages/my-ads/components/AdTypeSection/AdTypeSection.tsx
+++ b/src/pages/my-ads/components/AdTypeSection/AdTypeSection.tsx
@@ -112,6 +112,7 @@ const AdTypeSection = ({ currency, localCurrency, onCancel, rateType, ...props }
}
isDisabled={isEdit}
label={localize('Total amount')}
+ maxLength={15}
name='amount'
rightPlaceholder={
@@ -142,6 +143,7 @@ const AdTypeSection = ({ currency, localCurrency, onCancel, rateType, ...props }
) : (
@@ -154,6 +156,7 @@ const AdTypeSection = ({ currency, localCurrency, onCancel, rateType, ...props }
@@ -164,6 +167,7 @@ const AdTypeSection = ({ currency, localCurrency, onCancel, rateType, ...props }
/>
diff --git a/src/pages/my-ads/components/AlertComponent/AlertComponent.scss b/src/pages/my-ads/components/AlertComponent/AlertComponent.scss
index f5eb5c0a..e9455b8c 100644
--- a/src/pages/my-ads/components/AlertComponent/AlertComponent.scss
+++ b/src/pages/my-ads/components/AlertComponent/AlertComponent.scss
@@ -3,7 +3,7 @@
justify-content: center;
position: absolute;
align-items: center;
- right: 3.6rem;
+ right: 4.6rem;
height: 100%;
span {
@@ -12,7 +12,8 @@
@include mobile-or-tablet-screen {
position: unset;
margin-top: -0.4rem;
- margin-left: 1rem;
+ margin-left: 2.5rem;
+ margin-right: 1.5rem;
}
& .tooltip-menu-icon:hover {
diff --git a/src/pages/my-ads/components/AlertComponent/AlertComponent.tsx b/src/pages/my-ads/components/AlertComponent/AlertComponent.tsx
index b0e296af..fdc13eaa 100644
--- a/src/pages/my-ads/components/AlertComponent/AlertComponent.tsx
+++ b/src/pages/my-ads/components/AlertComponent/AlertComponent.tsx
@@ -1,6 +1,6 @@
-import { TooltipMenuIcon } from '@/components/TooltipMenuIcon';
import { LegacyWarningIcon } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
+import { Tooltip, useDevice } from '@deriv-com/ui';
import './AlertComponent.scss';
type TAlertComponentProps = {
@@ -9,16 +9,12 @@ type TAlertComponentProps = {
const AlertComponent = ({ onClick }: TAlertComponentProps) => {
const { localize } = useTranslations();
+ const { isDesktop } = useDevice();
return (
-
+
-
+
);
};
diff --git a/src/pages/my-ads/components/AlertComponent/__tests__/AlertComponent.spec.tsx b/src/pages/my-ads/components/AlertComponent/__tests__/AlertComponent.spec.tsx
index 7ced1132..c9823418 100644
--- a/src/pages/my-ads/components/AlertComponent/__tests__/AlertComponent.spec.tsx
+++ b/src/pages/my-ads/components/AlertComponent/__tests__/AlertComponent.spec.tsx
@@ -5,6 +5,12 @@ import AlertComponent from '../AlertComponent';
const mockProps = {
onClick: jest.fn(),
};
+
+jest.mock('@deriv-com/ui', () => ({
+ ...jest.requireActual('@deriv-com/ui'),
+ useDevice: () => ({ isDesktop: true }),
+}));
+
describe('AlertComponent', () => {
it('should render the component as expected', () => {
render();
diff --git a/src/pages/my-ads/components/BuyPaymentMethodsList/BuyPaymentMethodsList.scss b/src/pages/my-ads/components/BuyPaymentMethodsList/BuyPaymentMethodsList.scss
index 615047e3..6ecdb73e 100644
--- a/src/pages/my-ads/components/BuyPaymentMethodsList/BuyPaymentMethodsList.scss
+++ b/src/pages/my-ads/components/BuyPaymentMethodsList/BuyPaymentMethodsList.scss
@@ -23,7 +23,7 @@
}
&__items {
top: unset;
- bottom: 5rem;
+ bottom: 7rem;
max-height: 20rem;
}
}
diff --git a/src/pages/my-ads/components/BuyPaymentMethodsList/BuyPaymentMethodsList.tsx b/src/pages/my-ads/components/BuyPaymentMethodsList/BuyPaymentMethodsList.tsx
index e7df9525..b89d6342 100644
--- a/src/pages/my-ads/components/BuyPaymentMethodsList/BuyPaymentMethodsList.tsx
+++ b/src/pages/my-ads/components/BuyPaymentMethodsList/BuyPaymentMethodsList.tsx
@@ -14,9 +14,9 @@ const BuyPaymentMethodsList = ({ list, onSelectPaymentMethod }: TBuyPaymentMetho
return (
}
className='buy-payment-methods-list__dropdown'
data-testid='dt_buy_payment_methods_list'
- dropdownIcon={
}
emptyResultMessage={localize('No results found')}
icon={
}
isFullWidth
diff --git a/src/pages/my-ads/components/OrderTimeSelection/OrderTimeSelection.scss b/src/pages/my-ads/components/OrderTimeSelection/OrderTimeSelection.scss
index 17c3b3e3..130dc77c 100644
--- a/src/pages/my-ads/components/OrderTimeSelection/OrderTimeSelection.scss
+++ b/src/pages/my-ads/components/OrderTimeSelection/OrderTimeSelection.scss
@@ -1,14 +1,19 @@
.order-time-selection {
margin: 1rem 0;
-
& .deriv-dropdown {
&__items {
+ top: 4rem;
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.16);
}
- & .deriv-input__container {
- width: 100%;
- }
+ & .deriv-input {
+ &__container {
+ width: 100%;
+ }
+ &__helper-message {
+ display: none;
+ }
+ }
& .deriv-dropdown__items--xs {
margin-top: 0.5rem;
max-height: 20rem;
diff --git a/src/pages/my-ads/components/OrderTimeSelection/OrderTimeSelection.tsx b/src/pages/my-ads/components/OrderTimeSelection/OrderTimeSelection.tsx
index 07274636..e5a9268f 100644
--- a/src/pages/my-ads/components/OrderTimeSelection/OrderTimeSelection.tsx
+++ b/src/pages/my-ads/components/OrderTimeSelection/OrderTimeSelection.tsx
@@ -1,13 +1,12 @@
-import { useEffect, useState } from 'react';
+import { MouseEvent, useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { TOrderExpiryOptions } from 'types';
import { OrderTimeTooltipModal } from '@/components/Modals';
-import { TooltipMenuIcon } from '@/components/TooltipMenuIcon';
import { getOrderTimeInfoMessage } from '@/constants';
import { formatTime, getOrderTimeCompletionList } from '@/utils';
-import { LabelPairedChevronDownMdRegularIcon, LabelPairedCircleInfoCaptionRegularIcon } from '@deriv/quill-icons';
+import { LabelPairedCircleInfoCaptionRegularIcon } from '@deriv/quill-icons';
import { Localize, useTranslations } from '@deriv-com/translations';
-import { Dropdown, Text, useDevice } from '@deriv-com/ui';
+import { Dropdown, Text, Tooltip, useDevice } from '@deriv-com/ui';
import './OrderTimeSelection.scss';
const OrderTimeSelection = ({ orderExpiryOptions }: { orderExpiryOptions: TOrderExpiryOptions }) => {
@@ -44,6 +43,10 @@ const OrderTimeSelection = ({ orderExpiryOptions }: { orderExpiryOptions: TOrder
return options;
};
+ const handleDropdownClick = (event: MouseEvent) => {
+ event.preventDefault();
+ };
+
return (
@@ -51,9 +54,8 @@ const OrderTimeSelection = ({ orderExpiryOptions }: { orderExpiryOptions: TOrder
- undefined : () => setIsModalOpen(true)}
tooltipContent={getOrderTimeInfoMessage(localize)}
type='button'
@@ -63,24 +65,27 @@ const OrderTimeSelection = ({ orderExpiryOptions }: { orderExpiryOptions: TOrder
height={24}
width={24}
/>
-
+
(
- }
- isFullWidth={isDesktop}
- list={getOptions().sort((a, b) => a.value - b.value)}
- name='order-completion-time'
- onSelect={onChange}
- shouldClearValue
- value={value}
- variant='comboBox'
- />
+
+ a.value - b.value)}
+ name='order-completion-time'
+ onSelect={onChange}
+ shouldClearValue
+ value={value}
+ variant='comboBox'
+ />
+
)}
/>
diff --git a/src/pages/my-ads/components/OrderTimeSelection/__tests__/OrderTimeSelection.spec.tsx b/src/pages/my-ads/components/OrderTimeSelection/__tests__/OrderTimeSelection.spec.tsx
index 35680858..685c418d 100644
--- a/src/pages/my-ads/components/OrderTimeSelection/__tests__/OrderTimeSelection.spec.tsx
+++ b/src/pages/my-ads/components/OrderTimeSelection/__tests__/OrderTimeSelection.spec.tsx
@@ -39,13 +39,13 @@ describe('OrderTimeSelection', () => {
it('should not do anything on clicking info icon in desktop view', async () => {
render();
await userEvent.click(screen.getByTestId('dt_order_info_icon'));
- expect(screen.queryByRole('button', { name: 'Ok' })).not.toBeInTheDocument();
+ expect(screen.queryByRole('button', { name: 'OK' })).not.toBeInTheDocument();
});
it('should handle the modal open in mobile view', async () => {
mockUseDevice.mockReturnValue({ isMobile: true });
render();
await userEvent.click(screen.getByTestId('dt_order_info_icon'));
- const okButton = screen.getByRole('button', { name: 'Ok' });
+ const okButton = screen.getByRole('button', { name: 'OK' });
expect(okButton).toBeInTheDocument();
await userEvent.click(okButton);
expect(screen.queryByRole('button', { name: 'Ok' })).not.toBeInTheDocument();
diff --git a/src/pages/my-ads/screens/MyAds/MyAds.tsx b/src/pages/my-ads/screens/MyAds/MyAds.tsx
index d7720417..e4aa57d0 100644
--- a/src/pages/my-ads/screens/MyAds/MyAds.tsx
+++ b/src/pages/my-ads/screens/MyAds/MyAds.tsx
@@ -1,13 +1,19 @@
import { TemporarilyBarredHint, Verification } from '@/components';
-import { useIsAdvertiserBarred, usePoiPoaStatus } from '@/hooks/custom-hooks';
+import { useIsAdvertiser, useIsAdvertiserBarred, usePoiPoaStatus } from '@/hooks/custom-hooks';
import { MyAdsTable } from './MyAdsTable';
const MyAds = () => {
+ const isAdvertiser = useIsAdvertiser();
const isAdvertiserBarred = useIsAdvertiserBarred();
const { data } = usePoiPoaStatus();
const { isPoaVerified, isPoiVerified } = data || {};
- if (!isPoaVerified || !isPoiVerified) return ;
+ if (!isAdvertiser && (!isPoaVerified || !isPoiVerified))
+ return (
+
+ ;
+
+ );
return (
diff --git a/src/pages/my-ads/screens/MyAds/__tests__/MyAds.spec.tsx b/src/pages/my-ads/screens/MyAds/__tests__/MyAds.spec.tsx
index f7f3f7ff..d703fa57 100644
--- a/src/pages/my-ads/screens/MyAds/__tests__/MyAds.spec.tsx
+++ b/src/pages/my-ads/screens/MyAds/__tests__/MyAds.spec.tsx
@@ -9,6 +9,7 @@ jest.mock('@/components', () => ({
}));
jest.mock('@/hooks/custom-hooks', () => ({
+ useIsAdvertiser: jest.fn(() => false),
useIsAdvertiserBarred: jest.fn().mockReturnValue(false),
usePoiPoaStatus: jest.fn().mockReturnValue({
data: {
diff --git a/src/pages/my-profile/screens/MyProfile/MyProfile.tsx b/src/pages/my-profile/screens/MyProfile/MyProfile.tsx
index 649908d6..761e28c6 100644
--- a/src/pages/my-profile/screens/MyProfile/MyProfile.tsx
+++ b/src/pages/my-profile/screens/MyProfile/MyProfile.tsx
@@ -49,8 +49,12 @@ const MyProfile = () => {
return
;
}
- if (!isPoiVerified || !isPoaVerified) {
- return
;
+ if (!isAdvertiser && (!isPoiVerified || !isPoaVerified)) {
+ return (
+
+
+
+ );
}
if (!isDesktop) {
diff --git a/src/pages/my-profile/screens/MyProfile/__tests__/MyProfile.spec.tsx b/src/pages/my-profile/screens/MyProfile/__tests__/MyProfile.spec.tsx
index 957ef026..4865140d 100644
--- a/src/pages/my-profile/screens/MyProfile/__tests__/MyProfile.spec.tsx
+++ b/src/pages/my-profile/screens/MyProfile/__tests__/MyProfile.spec.tsx
@@ -94,7 +94,8 @@ describe('MyProfile', () => {
render(
);
expect(screen.getByTestId('dt_derivs-loader')).toBeInTheDocument();
});
- it('should render the verification component if user has not completed POI ', () => {
+ it('should render the verification component if a new user has not completed POI ', () => {
+ (mockUseIsAdvertiser as jest.Mock).mockReturnValueOnce(false);
(mockUsePoiPoaStatus as jest.Mock).mockReturnValueOnce({
data: { isPoaVerified: true, isPoiVerified: false },
isLoading: false,
@@ -103,7 +104,8 @@ describe('MyProfile', () => {
render(
);
expect(screen.getByText('Verification')).toBeInTheDocument();
});
- it('should render the verification component if user has not completed POA', () => {
+ it('should render the verification component if a new user has not completed POA', () => {
+ (mockUseIsAdvertiser as jest.Mock).mockReturnValueOnce(false);
(mockUsePoiPoaStatus as jest.Mock).mockReturnValueOnce({
data: { isPoaVerified: false, isPoiVerified: true },
isLoading: false,
diff --git a/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesHeader/MyProfileCounterpartiesHeader.scss b/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesHeader/MyProfileCounterpartiesHeader.scss
index e9e287f5..097d3f8f 100644
--- a/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesHeader/MyProfileCounterpartiesHeader.scss
+++ b/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesHeader/MyProfileCounterpartiesHeader.scss
@@ -5,7 +5,7 @@
& .deriv-dropdown__items {
width: 20rem;
- margin-top: 0.8rem;
+ margin-top: 0.5rem;
}
& .deriv-input {
@@ -14,6 +14,10 @@
&__container {
width: 20rem;
+ & .deriv-input__helper-message {
+ display: none;
+ }
+
@include mobile-or-tablet-screen {
width: 100%;
}
diff --git a/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesHeader/MyProfileCounterpartiesHeader.tsx b/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesHeader/MyProfileCounterpartiesHeader.tsx
index adddf2e2..64cee15d 100644
--- a/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesHeader/MyProfileCounterpartiesHeader.tsx
+++ b/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesHeader/MyProfileCounterpartiesHeader.tsx
@@ -34,7 +34,7 @@ const MyProfileCounterpartiesHeader = ({
/>
{isDesktop ? (
}
+ chevronIcon={
}
label={localize('Filter by')}
list={getCounterpartiesDropdownList(localize) as unknown as MutableOption[]}
listHeight='sm'
diff --git a/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTable/MyProfileCounterpartiesTable.tsx b/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTable/MyProfileCounterpartiesTable.tsx
index 3fb98c44..085bddf2 100644
--- a/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTable/MyProfileCounterpartiesTable.tsx
+++ b/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTable/MyProfileCounterpartiesTable.tsx
@@ -1,6 +1,10 @@
-import { Dispatch, SetStateAction, useEffect, useState } from 'react';
+import { useEffect } from 'react';
+import { useShallow } from 'zustand/react/shallow';
import { Table } from '@/components';
+import { ERROR_CODES } from '@/constants';
import { api } from '@/hooks';
+import { useIsAdvertiserBarred } from '@/hooks/custom-hooks';
+import { useErrorStore } from '@/stores';
import { DerivLightIcBlockedAdvertisersBarredIcon } from '@deriv/quill-icons';
import { Localize } from '@deriv-com/translations';
import { Loader, Text, useDevice } from '@deriv-com/ui';
@@ -18,21 +22,14 @@ type TMyProfileCounterpartiesTableRowRendererProps = {
id?: string;
is_blocked: boolean;
name?: string;
- setErrorMessage: Dispatch
>;
};
const MyProfileCounterpartiesTableRowRenderer = ({
id,
is_blocked: isBlocked,
name,
- setErrorMessage,
}: TMyProfileCounterpartiesTableRowRendererProps) => (
-
+
);
const MyProfileCounterpartiesTable = ({
@@ -50,8 +47,14 @@ const MyProfileCounterpartiesTable = ({
is_blocked: dropdownValue === 'blocked' ? 1 : 0,
trade_partners: 1,
});
- const [errorMessage, setErrorMessage] = useState('');
const { isDesktop } = useDevice();
+ const { errorMessages, reset } = useErrorStore(
+ useShallow(state => ({ errorMessages: state.errorMessages, reset: state.reset }))
+ );
+ const isAdvertiserBarred = useIsAdvertiserBarred();
+ const errorMessage = errorMessages.find(
+ error => error.code === ERROR_CODES.TEMPORARY_BAR || error.code === ERROR_CODES.PERMISSION_DENIED
+ )?.message;
useEffect(() => {
if (data.length > 0) {
@@ -62,6 +65,13 @@ const MyProfileCounterpartiesTable = ({
}
}, [data, errorMessage, setShowHeader]);
+ useEffect(() => {
+ if (!isAdvertiserBarred && errorMessages.some(error => error.code === ERROR_CODES.TEMPORARY_BAR)) {
+ setShowHeader(true);
+ reset();
+ }
+ }, [errorMessage, errorMessages, isAdvertiserBarred, reset, setShowHeader]);
+
if (isLoading) {
return ;
}
@@ -93,7 +103,6 @@ const MyProfileCounterpartiesTable = ({
rowRender={(rowData: unknown) => (
)}
tableClassname='my-profile-counterparties-table'
diff --git a/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTable/__tests__/MyProfileCounterpartiesTable.spec.tsx b/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTable/__tests__/MyProfileCounterpartiesTable.spec.tsx
index a199320c..6c59ecd3 100644
--- a/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTable/__tests__/MyProfileCounterpartiesTable.spec.tsx
+++ b/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTable/__tests__/MyProfileCounterpartiesTable.spec.tsx
@@ -1,4 +1,5 @@
import { api } from '@/hooks';
+import { useIsAdvertiserBarred } from '@/hooks/custom-hooks';
import { render, screen, waitFor } from '@testing-library/react';
import MyProfileCounterpartiesTable from '../MyProfileCounterpartiesTable';
@@ -15,12 +16,18 @@ const mockApiValues = {
loadMoreAdvertisers: jest.fn(),
};
+const mockStore = {
+ errorMessages: [],
+ reset: jest.fn(),
+};
+
jest.mock('@deriv-com/ui', () => ({
...jest.requireActual('@deriv-com/ui'),
useDevice: jest.fn(() => ({ isMobile: false })),
}));
jest.mock('@/hooks', () => ({
+ ...jest.requireActual('@/hooks'),
api: {
advertiser: {
useGetList: jest.fn(() => mockApiValues),
@@ -29,6 +36,14 @@ jest.mock('@/hooks', () => ({
useIsRtl: jest.fn(() => false),
}));
+jest.mock('@/hooks/custom-hooks', () => ({
+ useIsAdvertiserBarred: jest.fn(() => false),
+}));
+
+jest.mock('@/stores', () => ({
+ useErrorStore: jest.fn(selector => (selector ? selector(mockStore) : mockStore)),
+}));
+
const mockUseModalManager = {
hideModal: jest.fn(),
isModalOpenFor: jest.fn(),
@@ -46,6 +61,8 @@ jest.mock('@/components/Modals/BlockUnblockUserModal', () => ({
}));
const mockUseGetList = api.advertiser.useGetList as jest.Mock;
+const mockUseIsAdvertiserBarred = useIsAdvertiserBarred as jest.Mock;
+
describe('MyProfileCounterpartiesTable', () => {
it('should render the empty results when there is no data', () => {
render();
@@ -82,4 +99,32 @@ describe('MyProfileCounterpartiesTable', () => {
render();
expect(screen.getByText('There are no matching name.')).toBeInTheDocument();
});
+
+ it('should show error message when error code is TEMPORARY_BAR', () => {
+ // @ts-expect-error - mock values
+ mockStore.errorMessages = [{ code: 'TemporaryBar', message: 'Temporary Bar' }];
+ mockUseIsAdvertiserBarred.mockReturnValue(true);
+ mockUseGetList.mockReturnValue({
+ ...mockApiValues,
+ data: [{ id: 'id1', is_blocked: false, name: 'name1' }],
+ });
+
+ render();
+ expect(screen.getByText('Temporary Bar')).toBeInTheDocument();
+ });
+
+ it('should call reset and setShowHeader if isAdvertiserBarred is false and error code is', () => {
+ // @ts-expect-error - mock values
+ mockStore.errorMessages = [{ code: 'TemporaryBar', message: 'Temporary Bar' }];
+ mockUseIsAdvertiserBarred.mockReturnValue(false);
+ mockUseGetList.mockReturnValue({
+ ...mockApiValues,
+ data: [{ id: 'id1', is_blocked: false, name: 'name1' }],
+ });
+
+ render();
+
+ expect(mockProps.setShowHeader).toHaveBeenCalledWith(true);
+ expect(mockStore.reset).toHaveBeenCalled();
+ });
});
diff --git a/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTableRow/MyProfileCounterpartiesTableRow.tsx b/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTableRow/MyProfileCounterpartiesTableRow.tsx
index 161b80c9..962e008e 100644
--- a/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTableRow/MyProfileCounterpartiesTableRow.tsx
+++ b/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTableRow/MyProfileCounterpartiesTableRow.tsx
@@ -1,4 +1,4 @@
-import { Dispatch, memo, SetStateAction } from 'react';
+import { memo } from 'react';
import clsx from 'clsx';
import { useHistory } from 'react-router-dom';
import { UserAvatar } from '@/components';
@@ -13,15 +13,9 @@ type TMyProfileCounterpartiesTableRowProps = {
id: string;
isBlocked: boolean;
nickname: string;
- setErrorMessage: Dispatch>;
};
-const MyProfileCounterpartiesTableRow = ({
- id,
- isBlocked,
- nickname,
- setErrorMessage,
-}: TMyProfileCounterpartiesTableRowProps) => {
+const MyProfileCounterpartiesTableRow = ({ id, isBlocked, nickname }: TMyProfileCounterpartiesTableRowProps) => {
const { isDesktop } = useDevice();
const history = useHistory();
const { hideModal, isModalOpenFor, showModal } = useModalManager();
@@ -54,14 +48,15 @@ const MyProfileCounterpartiesTableRow = ({
{isBlocked ? localize('Unblock') : localize('Block')}
-
+ {isModalOpenFor('BlockUnblockUserModal') && (
+
+ )}
>
);
};
diff --git a/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTableRow/__tests__/MyProfileCounterpartiesTableRow.spec.tsx b/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTableRow/__tests__/MyProfileCounterpartiesTableRow.spec.tsx
index 56561134..76313e30 100644
--- a/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTableRow/__tests__/MyProfileCounterpartiesTableRow.spec.tsx
+++ b/src/pages/my-profile/screens/MyProfileCounterparties/MyProfileCounterpartiesTableRow/__tests__/MyProfileCounterpartiesTableRow.spec.tsx
@@ -34,6 +34,7 @@ const mockModalManager = {
};
jest.mock('@/hooks', () => ({
+ ...jest.requireActual('@/hooks'),
api: {
counterparty: {
useBlock: () => ({
diff --git a/src/pages/orders/components/ChatFooter/ChatFooter.tsx b/src/pages/orders/components/ChatFooter/ChatFooter.tsx
index 4950b3b6..6b8ef6b3 100644
--- a/src/pages/orders/components/ChatFooter/ChatFooter.tsx
+++ b/src/pages/orders/components/ChatFooter/ChatFooter.tsx
@@ -29,7 +29,7 @@ const ChatFooter = ({ isClosed, sendFile, sendMessage }: TChatFooterProps) => {
return (
-
+
);
diff --git a/src/pages/orders/components/ChatFooter/__tests__/ChatFooter.spec.tsx b/src/pages/orders/components/ChatFooter/__tests__/ChatFooter.spec.tsx
index 140db0e1..ee6d7a48 100644
--- a/src/pages/orders/components/ChatFooter/__tests__/ChatFooter.spec.tsx
+++ b/src/pages/orders/components/ChatFooter/__tests__/ChatFooter.spec.tsx
@@ -19,7 +19,7 @@ describe('ChatFooter', () => {
});
it('should render the conversation closed message', () => {
render();
- expect(screen.getByText('This conversation is closed')).toBeInTheDocument();
+ expect(screen.getByText('This conversation is closed.')).toBeInTheDocument();
});
it('should expect value to be set on changing input', async () => {
render();
diff --git a/src/routes/AppContent/index.scss b/src/routes/AppContent/index.scss
index 3ed793dd..2b4454e1 100644
--- a/src/routes/AppContent/index.scss
+++ b/src/routes/AppContent/index.scss
@@ -63,3 +63,15 @@
.languages-modal__body {
margin: 1.6rem;
}
+
+// to fix the helper message position instead of individually changing for all the forms and fields
+.deriv-input__helper-message {
+ position: unset;
+}
+
+// to fix the issue with text capitalize in dropdown label
+.deriv-dropdown {
+ label {
+ text-transform: none;
+ }
+}
diff --git a/src/routes/AppContent/index.tsx b/src/routes/AppContent/index.tsx
index 098b73e2..82b34d66 100644
--- a/src/routes/AppContent/index.tsx
+++ b/src/routes/AppContent/index.tsx
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import { BlockedScenarios } from '@/components/BlockedScenarios';
import { BUY_SELL_URL, ERROR_CODES } from '@/constants';
-import { api, useIsP2PBlocked, useLiveChat } from '@/hooks';
+import { api, useIsP2PBlocked, useLiveChat, useOAuth } from '@/hooks';
import { GuideTooltip } from '@/pages/guide/components';
import { AdvertiserInfoStateProvider } from '@/providers/AdvertiserInfoStateProvider';
import { getCurrentRoute } from '@/utils';
@@ -17,10 +17,16 @@ const AppContent = () => {
const history = useHistory();
const location = useLocation();
const { isDesktop } = useDevice();
- const { data: activeAccountData, isFetched, isLoading: isLoadingActiveAccount } = api.account.useActiveAccount();
+ const {
+ authError,
+ data: activeAccountData,
+ isFetched,
+ isLoading: isLoadingActiveAccount,
+ } = api.account.useActiveAccount();
const { init: initLiveChat } = useLiveChat();
const { isP2PBlocked, status } = useIsP2PBlocked();
const { localize } = useTranslations();
+ const { oAuthLogout } = useOAuth();
const routes = getRoutes(localize);
const tabRoutesConfiguration = routes.filter(
@@ -79,6 +85,10 @@ const AppContent = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location]);
+ useEffect(() => {
+ if (authError?.code === ERROR_CODES.ACCOUNT_DISABLED) oAuthLogout();
+ }, [authError, oAuthLogout]);
+
useEffect(() => {
if (!isGtmTracking.current) {
window.dataLayer.push({ event: 'allow_tracking' });
diff --git a/src/stores/__tests__/useErrorStore.spec.ts b/src/stores/__tests__/useErrorStore.spec.ts
new file mode 100644
index 00000000..7084e4d4
--- /dev/null
+++ b/src/stores/__tests__/useErrorStore.spec.ts
@@ -0,0 +1,61 @@
+import { act } from 'react';
+import { renderHook } from '@testing-library/react';
+import useErrorStore from '../useErrorStore';
+
+describe('useErrorStore', () => {
+ it('should update the store state correctly', () => {
+ const { result } = renderHook(() => useErrorStore());
+
+ expect(result.current.errorMessages).toEqual([]);
+
+ act(() => {
+ result.current.setErrorMessages({ code: 'error-code', message: 'error-message' });
+ });
+
+ expect(result.current.errorMessages).toEqual([{ code: 'error-code', message: 'error-message' }]);
+
+ act(() => {
+ result.current.setErrorMessages(null);
+ });
+ });
+
+ it('should not add the same error message twice', () => {
+ const { result } = renderHook(() => useErrorStore());
+
+ expect(result.current.errorMessages).toEqual([]);
+
+ act(() => {
+ result.current.setErrorMessages({ code: 'error-code', message: 'error-message' });
+ });
+
+ expect(result.current.errorMessages).toEqual([{ code: 'error-code', message: 'error-message' }]);
+
+ act(() => {
+ result.current.setErrorMessages({ code: 'error-code', message: 'error-message' });
+ });
+
+ expect(result.current.errorMessages).toEqual([{ code: 'error-code', message: 'error-message' }]);
+
+ act(() => {
+ result.current.setErrorMessages(null);
+ });
+ });
+
+ it('should reset the error messages', () => {
+ const { result } = renderHook(() => useErrorStore());
+
+ expect(result.current.errorMessages).toEqual([]);
+
+ act(() => {
+ result.current.setErrorMessages({ code: 'error-code', message: 'error-message' });
+ });
+
+ expect(result.current.errorMessages).toEqual([{ code: 'error-code', message: 'error-message' }]);
+
+ act(() => {
+ result.current.reset();
+ });
+
+ expect(result.current.errorMessages).toEqual([]);
+ });
+});
diff --git a/src/stores/index.ts b/src/stores/index.ts
index 0d8fcad6..818cb5a4 100644
--- a/src/stores/index.ts
+++ b/src/stores/index.ts
@@ -1,2 +1,3 @@
export { default as useBuySellFiltersStore } from './useBuySellFiltersStore';
+export { default as useErrorStore } from './useErrorStore';
export { default as useTabsStore } from './useTabsStore';
diff --git a/src/stores/useErrorStore.ts b/src/stores/useErrorStore.ts
new file mode 100644
index 00000000..8d3b3876
--- /dev/null
+++ b/src/stores/useErrorStore.ts
@@ -0,0 +1,39 @@
+import { create } from 'zustand';
+
+type TError = {
+ code: string;
+ message: string;
+};
+
+type TErrorState = {
+ errorMessages: TError[];
+};
+
+type TErrorAction = {
+ reset: () => void;
+ setErrorMessages: (errorMessages: TError | null) => void;
+};
+
+const useErrorStore = create()(set => ({
+ errorMessages: [],
+ reset: () => set({ errorMessages: [] }),
+ setErrorMessages: errorMessages =>
+ set(state => {
+ if (!errorMessages) {
+ return { ...state, errorMessages: [] };
+ }
+
+ const isErrorMessageIncluded = state.errorMessages.some(
+ error => error.code === errorMessages.code && error.message === errorMessages.message
+ );
+
+ return {
+ ...state,
+ errorMessages: isErrorMessageIncluded
+ ? state.errorMessages
+ : [...state.errorMessages, { code: errorMessages.code, message: errorMessages.message }],
+ };
+ }),
+}));
+
+export default useErrorStore;
diff --git a/src/utils/ad-utils.ts b/src/utils/ad-utils.ts
index be6ea2e9..6d98ac54 100644
--- a/src/utils/ad-utils.ts
+++ b/src/utils/ad-utils.ts
@@ -108,7 +108,7 @@ export const getValidationRules = (
case 'min-order':
return {
validation_1: value => requiredValidation(value, localize('Min limit')),
- validation_2: value => !isNaN(Number(value)) || localize('Only numbers are allowed'),
+ validation_2: value => !isNaN(Number(value)) || localize('Only numbers are allowed.'),
validation_3: value => decimalPointValidation(value),
validation_4: value => {
const amount = getValues('amount');
@@ -128,7 +128,7 @@ export const getValidationRules = (
case 'max-order':
return {
validation_1: value => requiredValidation(value, localize('Max limit')),
- validation_2: value => !isNaN(Number(value)) || 'Only numbers are allowed',
+ validation_2: value => !isNaN(Number(value)) || 'Only numbers are allowed.',
validation_3: value => decimalPointValidation(value),
validation_4: value => {
const amount = getValues('amount');