diff --git a/package-lock.json b/package-lock.json index fab5ffa9..79a7e21e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@babel/preset-env": "^7.24.5", "@deriv-com/api-hooks": "^0.1.19", "@deriv-com/translations": "^1.2.3", - "@deriv-com/ui": "^1.21.1", + "@deriv-com/ui": "^1.27.3", "@deriv-com/utils": "latest", "@deriv/deriv-api": "^1.0.15", "@deriv/quill-design": "^1.2.24", @@ -2716,12 +2716,12 @@ } }, "node_modules/@deriv-com/ui": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/@deriv-com/ui/-/ui-1.21.1.tgz", - "integrity": "sha512-8aokCnRAQJqnpr8Kzz4qpdRoqy2EJrusfVtUSS8nb1uUvIQiMRwPMzqPt1A/OojFmTDHpES7YJPPLlu2e90LpQ==", + "version": "1.27.3", + "resolved": "https://registry.npmjs.org/@deriv-com/ui/-/ui-1.27.3.tgz", + "integrity": "sha512-Q6R+vYu//W/NYADMNZVY54z38WppGHMWyHsSK6ZOe1vmmbO9QTR7UH2QBWRsJV3CX1djkQwncrAKGYkQMtMG9g==", "dependencies": { - "@deriv/quill-icons": "^1.22.5", - "@types/react-modal": "^3.16.3" + "@types/react-modal": "^3.16.3", + "react-tiny-popover": "^8.0.4" }, "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "^4.13.0" @@ -16998,6 +16998,15 @@ "react-dom": ">=18.0.0" } }, + "node_modules/react-tiny-popover": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/react-tiny-popover/-/react-tiny-popover-8.0.4.tgz", + "integrity": "sha512-pn0Y/G0gyMdYTBEWSKCCnaZsXAa54PkfnRE4fnMM5633SSClYrXxwXKc6vPYgJ9shLatGginxMjnhXq6guZmng==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -22684,13 +22693,13 @@ } }, "@deriv-com/ui": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/@deriv-com/ui/-/ui-1.21.1.tgz", - "integrity": "sha512-8aokCnRAQJqnpr8Kzz4qpdRoqy2EJrusfVtUSS8nb1uUvIQiMRwPMzqPt1A/OojFmTDHpES7YJPPLlu2e90LpQ==", + "version": "1.27.3", + "resolved": "https://registry.npmjs.org/@deriv-com/ui/-/ui-1.27.3.tgz", + "integrity": "sha512-Q6R+vYu//W/NYADMNZVY54z38WppGHMWyHsSK6ZOe1vmmbO9QTR7UH2QBWRsJV3CX1djkQwncrAKGYkQMtMG9g==", "requires": { - "@deriv/quill-icons": "^1.22.5", "@rollup/rollup-linux-x64-gnu": "^4.13.0", - "@types/react-modal": "^3.16.3" + "@types/react-modal": "^3.16.3", + "react-tiny-popover": "^8.0.4" } }, "@deriv-com/utils": { @@ -32933,6 +32942,12 @@ "integrity": "sha512-NTFkW8W3uwvI82Fv7JW5i7gmDjEZKxJmj+Z9vn+BjYIXT6ILdnU9qnSUP2cWrWN/WAUlue81f9SgM4CQcenltQ==", "requires": {} }, + "react-tiny-popover": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/react-tiny-popover/-/react-tiny-popover-8.0.4.tgz", + "integrity": "sha512-pn0Y/G0gyMdYTBEWSKCCnaZsXAa54PkfnRE4fnMM5633SSClYrXxwXKc6vPYgJ9shLatGginxMjnhXq6guZmng==", + "requires": {} + }, "react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", diff --git a/package.json b/package.json index 1daa2d89..cb9a9c59 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@babel/preset-env": "^7.24.5", "@deriv-com/api-hooks": "^0.1.19", "@deriv-com/translations": "^1.2.3", - "@deriv-com/ui": "^1.21.1", + "@deriv-com/ui": "^1.27.3", "@deriv-com/utils": "latest", "@deriv/deriv-api": "^1.0.15", "@deriv/quill-design": "^1.2.24", diff --git a/src/components/AppHeader/AccountSwitcher/index.tsx b/src/components/AppHeader/AccountSwitcher/index.tsx new file mode 100644 index 00000000..e9388a0f --- /dev/null +++ b/src/components/AppHeader/AccountSwitcher/index.tsx @@ -0,0 +1,18 @@ +import { useActiveAccount } from '@/hooks/api/account'; +import { CurrencyUsdIcon } from '@deriv/quill-icons'; +import { AccountSwitcher as UIAccountSwitcher } from '@deriv-com/ui'; +import { FormatUtils } from '@deriv-com/utils'; + +export const AccountSwitcher = () => { + const { data } = useActiveAccount(); + const activeAccount = { + balance: FormatUtils.formatMoney(data?.balance ?? 0), + currency: data?.currency || 'USD', + currencyLabel: data?.currency || 'US Dollar', + icon: , + isActive: true, + isVirtual: Boolean(data?.is_virtual), + loginid: data?.loginid || '', + }; + return data && ; +}; diff --git a/src/components/AppHeader/AppHeader.tsx b/src/components/AppHeader/AppHeader.tsx index 0771f308..15af6313 100644 --- a/src/components/AppHeader/AppHeader.tsx +++ b/src/components/AppHeader/AppHeader.tsx @@ -1,20 +1,39 @@ -import { useState } from 'react'; +/* eslint-disable no-console */ +// import { useActiveAccount } from '@/hooks/api/account'; + +import { useEffect, useState } from 'react'; +import { useDevice } from '@/hooks'; import { - LabelPairedHouseBlankMdRegularIcon, - LegacyChevronRight2pxIcon, - LegacyMenuHamburger1pxIcon, + LegacyMenuHamburger2pxIcon, + LegacyNotificationIcon, + StandaloneCircleUserRegularIcon, } from '@deriv/quill-icons'; -import { useAuthData } from '@deriv-com/api-hooks'; -import { Localize } from '@deriv-com/translations'; -import { Button, DerivLogo, Drawer, Header, MenuItem, Text, useDevice, Wrapper } from '@deriv-com/ui'; +import { useAccountList, useAuthData } from '@deriv-com/api-hooks'; +import { useTranslations } from '@deriv-com/translations'; +import { + Button, + Drawer, + Header, + MenuItem, + PlatformSwitcher, + PlatformSwitcherItem, + Text, + TooltipMenuIcon, + Wrapper, +} from '@deriv-com/ui'; import { LocalStorageConstants, LocalStorageUtils, URLUtils } from '@deriv-com/utils'; +import { AccountSwitcher } from './AccountSwitcher'; +import { MenuItems, platformsConfig } from './HeaderConfig'; import './AppHeader.scss'; // TODO: handle local storage values not updating after changing local storage values const AppHeader = () => { const [isDrawerOpen, setIsDrawerOpen] = useState(false); + const { data: accounts } = useAccountList(); const { isDesktop } = useDevice(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { activeLoginid, logout } = useAuthData(); + const { localize } = useTranslations(); const appId = LocalStorageUtils.getValue(LocalStorageConstants.configAppId); const serverUrl = localStorage.getItem(LocalStorageConstants.configServerURL.toString()); const oauthUrl = @@ -22,29 +41,59 @@ const AppHeader = () => { ? `https://${serverUrl}/oauth2/authorize?app_id=${appId}&l=EN&&brand=deriv` : URLUtils.getOauthURL(); + useEffect(() => { + const shouldRedirectToLogin = () => { + if (typeof accounts !== 'undefined') { + const userHasNoP2PAccount = !accounts.find( + account => account.broker === 'CR' && account.currency === 'USD' + ); + const activeAccount = accounts.find(account => account.loginid === activeLoginid); + const activeAccountCurrency = activeAccount?.currency || null; + + if (userHasNoP2PAccount || activeAccountCurrency !== 'USD') { + window.open(oauthUrl, '_self'); + } + } + }; + + shouldRedirectToLogin(); + }, [accounts, activeLoginid, oauthUrl]); + return ( - + {isDesktop ? ( - - } + - - - - + {platformsConfig.map(({ active, description, href, icon }) => ( + + ))} + + {MenuItems.map(({ as, href, icon, label }) => ( + + {localize(label)} + + ))} ) : ( + setIsDrawerOpen(true)} + > + + { @@ -52,53 +101,59 @@ const AppHeader = () => { }} width='300px' > - { - setIsDrawerOpen(false); - }} - > - - - - - - - } - rightComponent={} - > - - - - - - + aaa - } - onClick={() => setIsDrawerOpen(true)} - variant='ghost' - /> + + {localize(MenuItems[1].label)} + )} - {activeLoginid ? ( - - - - ) : ( - window.open(oauthUrl, '_self')} - variant='ghost' - > - - - - - )} + + {activeLoginid ? ( + <> + + + + {isDesktop && ( + + + + )} + + + {localize('Logout')} + + > + ) : ( + window.open(oauthUrl, '_self')} + variant='ghost' + > + {localize('Log in')} + + )} + ); }; diff --git a/src/components/AppHeader/HeaderConfig.tsx b/src/components/AppHeader/HeaderConfig.tsx new file mode 100644 index 00000000..007ed388 --- /dev/null +++ b/src/components/AppHeader/HeaderConfig.tsx @@ -0,0 +1,159 @@ +/* eslint-disable sort-keys */ +import { ReactNode } from 'react'; +import { + CurrencyBtcIcon, + CurrencyDemoIcon, + CurrencyEthIcon, + CurrencyUsdIcon, + CurrencyUsdtIcon, + DerivProductDerivBotBrandLightLogoWordmarkHorizontalIcon as DerivBotLogo, + DerivProductDerivTraderBrandLightLogoWordmarkHorizontalIcon as DerivTraderLogo, + LabelPairedHouseBlankMdRegularIcon as TradershubLogo, + LegacyCashierIcon as CashierLogo, + LegacyReportsIcon as ReportsLogo, + PartnersProductBinaryBotBrandLightLogoWordmarkHorizontalIcon as BinaryBotLogo, + PartnersProductSmarttraderBrandLightLogoWordmarkIcon as SmarttraderLogo, +} from '@deriv/quill-icons'; + +export type PlatformsConfig = { + active: boolean; + buttonIcon: ReactNode; + description: string; + href: string; + icon: ReactNode; + showInEU: boolean; +}; + +export type MenuItemsConfig = { + as: 'a' | 'button'; + href: string; + icon: ReactNode; + label: string; +}; + +export type TAccount = { + balance: string; + currency: string; + icon: React.ReactNode; + isActive: boolean; + isEu: boolean; + isVirtual: boolean; + loginid: string; + token: string; + type: string; +}; + +export const platformsConfig: PlatformsConfig[] = [ + { + active: true, + buttonIcon: , + href: 'https://app.deriv.com', + showInEU: true, + description: 'A whole new trading experience on a powerful yet easy to use platform.', + icon: , + }, + { + href: 'https://app.deriv.com/bot', + showInEU: false, + active: false, + description: 'Automated trading at your fingertips. No coding needed.', + icon: , + buttonIcon: , + }, + { + href: 'https://smarttrader.deriv.com/en/trading', + showInEU: false, + active: false, + description: 'Trade the world’s markets with our popular user-friendly platform.', + icon: , + buttonIcon: , + }, + { + href: 'https://bot.deriv.com', + showInEU: false, + active: false, + description: + 'Our classic “drag-and-drop” tool for creating trading bots, featuring pop-up trading charts, for advanced users.', + icon: , + buttonIcon: , + }, +]; + +export const MenuItems: MenuItemsConfig[] = [ + { + as: 'a', + href: 'https://app.deriv.com/appstore/traders-hub', + icon: , + label: "Trader's Hub", + }, + { + as: 'button', + href: 'https://app.deriv.com/appstore/traders-hub', + icon: , + label: `Reports`, + }, + { + as: 'button', + href: 'https://app.deriv.com/appstore/traders-hub', + icon: , + label: `Cashier`, + }, +]; + +export const accountsList: TAccount[] = [ + { + icon: , + type: 'US Dollar', + loginid: 'id1', + balance: '1000', + currency: 'USD', + token: 'token1', + isVirtual: true, + isEu: true, + isActive: false, + }, + { + icon: , + type: 'Bitcoin', + loginid: 'id2', + balance: '0.00054', + currency: 'BTC', + token: 'token2', + isVirtual: false, + isEu: false, + isActive: true, + }, + { + icon: , + type: 'US Dollar', + loginid: 'id3', + balance: '10000', + currency: 'USD', + token: 'token3', + isVirtual: false, + isEu: false, + isActive: false, + }, + { + icon: , + type: 'Tether TRC20', + loginid: 'id4', + balance: '500', + currency: 'USD', + token: 'token4', + isVirtual: false, + isEu: true, + isActive: false, + }, + { + icon: , + type: 'Etherium', + loginid: 'id5', + balance: '1000', + currency: 'ETH', + token: 'token5', + isVirtual: false, + isEu: true, + isActive: false, + }, +]; diff --git a/src/hooks/api/account/index.ts b/src/hooks/api/account/index.ts index 83826fa1..59256f96 100644 --- a/src/hooks/api/account/index.ts +++ b/src/hooks/api/account/index.ts @@ -1,4 +1,5 @@ export { default as useActiveAccount } from './useActiveAccount'; export { default as useAuthentication } from './useAuthentication'; +export { default as useBalance } from './useBalance'; export { default as useSendbirdServiceToken } from './useSendbirdServiceToken'; export { default as useServerTime } from './useServerTime'; diff --git a/src/hooks/api/account/useActiveAccount.ts b/src/hooks/api/account/useActiveAccount.ts index 75eda1d6..3dbdf6db 100644 --- a/src/hooks/api/account/useActiveAccount.ts +++ b/src/hooks/api/account/useActiveAccount.ts @@ -1,18 +1,29 @@ import { useMemo } from 'react'; import { useAccountList, useAuthData } from '@deriv-com/api-hooks'; +import { useBalance } from '.'; /** A custom hook that returns the account object for the current active account. */ const useActiveAccount = () => { const { data, ...rest } = useAccountList(); const { activeLoginid } = useAuthData(); + const { data: balanceData } = useBalance(); const active_account = useMemo( () => data?.find(account => account.loginid === activeLoginid), [activeLoginid, data] ); + const modified_account = useMemo(() => { + return active_account + ? { + ...active_account, + balance: balanceData?.accounts?.[active_account?.loginid]?.balance ?? 0, + } + : undefined; + }, [active_account, balanceData]); + return { /** User's current active account. */ - data: active_account, + data: modified_account, ...rest, }; }; diff --git a/src/hooks/api/account/useBalance.ts b/src/hooks/api/account/useBalance.ts new file mode 100644 index 00000000..df4c4168 --- /dev/null +++ b/src/hooks/api/account/useBalance.ts @@ -0,0 +1,14 @@ +import { useMemo } from 'react'; +import { useBalance as useAPIBalance } from '@deriv-com/api-hooks'; + +const useBalance = () => { + const { data, ...rest } = useAPIBalance({ + payload: { account: 'all' }, + }); + + const modifiedBalance = useMemo(() => ({ ...data }), [data]); + + return { data: modifiedBalance, ...rest }; +}; + +export default useBalance;