diff --git a/public/assets/icons/IcBlockly.svg b/public/assets/icons/IcBlockly.svg new file mode 100644 index 00000000..d524b1f9 --- /dev/null +++ b/public/assets/icons/IcBlockly.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/assets/icons/IcBotBuilder.svg b/public/assets/icons/IcBotBuilder.svg new file mode 100644 index 00000000..21d1f647 --- /dev/null +++ b/public/assets/icons/IcBotBuilder.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/assets/icons/IcBotBuilderSkeleton.svg b/public/assets/icons/IcBotBuilderSkeleton.svg new file mode 100644 index 00000000..403fc8d0 --- /dev/null +++ b/public/assets/icons/IcBotBuilderSkeleton.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/assets/icons/IcChart.svg b/public/assets/icons/IcChart.svg new file mode 100644 index 00000000..c32d0961 --- /dev/null +++ b/public/assets/icons/IcChart.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/assets/icons/IcChevronDown.svg b/public/assets/icons/IcChevronDown.svg new file mode 100644 index 00000000..401a345d --- /dev/null +++ b/public/assets/icons/IcChevronDown.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/icons/IcChevronDownBold.svg b/public/assets/icons/IcChevronDownBold.svg new file mode 100644 index 00000000..401a345d --- /dev/null +++ b/public/assets/icons/IcChevronDownBold.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/icons/IcChevronUp.svg b/public/assets/icons/IcChevronUp.svg new file mode 100644 index 00000000..4f2dc70a --- /dev/null +++ b/public/assets/icons/IcChevronUp.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/icons/IcDashboard.svg b/public/assets/icons/IcDashboard.svg new file mode 100644 index 00000000..70a787c4 --- /dev/null +++ b/public/assets/icons/IcDashboard.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/assets/icons/IcGoogleDrive.svg b/public/assets/icons/IcGoogleDrive.svg new file mode 100644 index 00000000..85afcf2d --- /dev/null +++ b/public/assets/icons/IcGoogleDrive.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/assets/icons/IcMapTripFold.svg b/public/assets/icons/IcMapTripFold.svg new file mode 100644 index 00000000..e35d111c --- /dev/null +++ b/public/assets/icons/IcMapTripFold.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/assets/icons/IcMyComputer.svg b/public/assets/icons/IcMyComputer.svg new file mode 100644 index 00000000..96c4fef2 --- /dev/null +++ b/public/assets/icons/IcMyComputer.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/assets/icons/IcOpen.svg b/public/assets/icons/IcOpen.svg new file mode 100644 index 00000000..48c930de --- /dev/null +++ b/public/assets/icons/IcOpen.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/assets/icons/IcPlay.svg b/public/assets/icons/IcPlay.svg new file mode 100644 index 00000000..b9788c90 --- /dev/null +++ b/public/assets/icons/IcPlay.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/assets/icons/IcReports.svg b/public/assets/icons/IcReports.svg new file mode 100644 index 00000000..0d7f97e5 --- /dev/null +++ b/public/assets/icons/IcReports.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/assets/icons/IcSave.svg b/public/assets/icons/IcSave.svg new file mode 100644 index 00000000..83f6b056 --- /dev/null +++ b/public/assets/icons/IcSave.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/icons/IcStop.svg b/public/assets/icons/IcStop.svg new file mode 100644 index 00000000..539e1d0c --- /dev/null +++ b/public/assets/icons/IcStop.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/icons/IcTrash.svg b/public/assets/icons/IcTrash.svg new file mode 100644 index 00000000..5bfb51e5 --- /dev/null +++ b/public/assets/icons/IcTrash.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/icons/IcTutorials.svg b/public/assets/icons/IcTutorials.svg new file mode 100644 index 00000000..31411e95 --- /dev/null +++ b/public/assets/icons/IcTutorials.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/components/trade-animation/trade-animation.tsx b/src/components/trade-animation/trade-animation.tsx index b2257f4e..c1fe4a6c 100644 --- a/src/components/trade-animation/trade-animation.tsx +++ b/src/components/trade-animation/trade-animation.tsx @@ -73,7 +73,7 @@ const TradeAnimation = observer(({ className, should_show_overlay }: TTradeAnima const button_props = React.useMemo(() => { if (is_stop_button_visible) { - return { id: 'db-animation__stop-button', text: localize('Stop'), icon: 'IcBotStop' }; + return { id: 'db-animation__stop-button', text: localize('Stop'), icon: 'IcStop' }; } return { id: 'db-animation__run-button', text: localize('Run'), icon: 'IcPlay' }; }, [is_stop_button_visible]); diff --git a/src/constants/dashboard.ts b/src/constants/dashboard.ts index b1480c06..2b97f1e7 100644 --- a/src/constants/dashboard.ts +++ b/src/constants/dashboard.ts @@ -1,6 +1,7 @@ import { localize } from '@/utils/tmp/dummy'; export const STRATEGY = { + OPEN: 'open', EDIT: 'edit', SAVE: 'save', DELETE: 'delete', @@ -11,8 +12,8 @@ export const STRATEGY = { export const MENU_DESKTOP = [ { - type: STRATEGY.EDIT, - icon: 'IcEdit', + type: STRATEGY.OPEN, + icon: 'IcOpen', }, { type: STRATEGY.SAVE, @@ -20,20 +21,15 @@ export const MENU_DESKTOP = [ }, { type: STRATEGY.DELETE, - icon: 'IcDelete', + icon: 'IcTrash', }, ]; export const CONTEXT_MENU_MOBILE = [ { - type: STRATEGY.PREVIEW_LIST, - icon: 'IcPreview', - label: localize('Preview'), - }, - { - type: STRATEGY.EDIT, - icon: 'IcEdit', - label: localize('Edit'), + type: STRATEGY.OPEN, + icon: 'IcOpen', + label: localize('Open'), }, { type: STRATEGY.SAVE, @@ -42,7 +38,7 @@ export const CONTEXT_MENU_MOBILE = [ }, { type: STRATEGY.DELETE, - icon: 'IcDelete', + icon: 'IcTrash', label: localize('Delete'), }, ]; diff --git a/src/main.tsx b/src/main.tsx index cd67dfc8..fe126d27 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,4 +2,6 @@ import ReactDOM from 'react-dom/client'; import App from './app/App.tsx'; +import './styles/index.scss'; + ReactDOM.createRoot(document.getElementById('root')!).render(); diff --git a/src/pages/dashboard/cards.tsx b/src/pages/dashboard/cards.tsx index 56fc8179..c7dcac0e 100644 --- a/src/pages/dashboard/cards.tsx +++ b/src/pages/dashboard/cards.tsx @@ -15,8 +15,8 @@ import { Icon, localize } from '@/utils/tmp/dummy'; import { rudderStackSendQsOpenEventFromDashboard } from '../bot-builder/quick-strategy/analytics/rudderstack-quick-strategy'; +import DashboardBotList from './load-bot-preview/dashboard-bot-list'; import GoogleDrive from './load-bot-preview/google-drive'; -import Recent from './load-bot-preview/recent'; type TCardProps = { has_dashboard_strategies: boolean; @@ -50,7 +50,6 @@ const Cards = observer(({ is_mobile, has_dashboard_strategies }: TCardProps) => rudderStackSendQsOpenEventFromDashboard(); }; - const [is_file_supported, setIsFileSupported] = React.useState(true); const file_input_ref = React.useRef(null); const openGoogleDriveDialog = () => { @@ -72,7 +71,7 @@ const Cards = observer(({ is_mobile, has_dashboard_strategies }: TCardProps) => }, { type: 'google-drive', - icon: 'IcGoogleDriveDbot', + icon: 'IcGoogleDrive', content: localize('Google Drive'), method: openGoogleDriveDialog, }, @@ -86,7 +85,7 @@ const Cards = observer(({ is_mobile, has_dashboard_strategies }: TCardProps) => }, { type: 'quick-strategy', - icon: 'IcQuickStrategy', + icon: 'IcBlockly', content: localize('Quick strategy'), method: () => { setActiveTab(DBOT_TABS.BOT_BUILDER); @@ -142,7 +141,7 @@ const Cards = observer(({ is_mobile, has_dashboard_strategies }: TCardProps) => accept='application/xml, text/xml' hidden onChange={e => { - setIsFileSupported(handleFileChange(e, false)); + handleFileChange(e, false); loadFileFromLocal(); setFileLoaded(true); setOpenSettings(NOTIFICATION_TYPE.BOT_IMPORT); @@ -178,7 +177,7 @@ const Cards = observer(({ is_mobile, has_dashboard_strategies }: TCardProps) => - + ), // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/src/pages/dashboard/dashboard.scss b/src/pages/dashboard/dashboard.scss index 75090691..e19c728e 100644 --- a/src/pages/dashboard/dashboard.scss +++ b/src/pages/dashboard/dashboard.scss @@ -1,3 +1,5 @@ +@import '../../styles/mixins'; + .bot-dashboard { position: relative; height: calc(100vh - 8.4rem); @@ -18,11 +20,14 @@ .run-panel__container { height: var(--tab-content-height) !important; position: sticky; + // TODO: need to set global variables for translation transform: translateX(calc(367px)); + @include is-RTL { transform: translateX(calc(-367px)); } + transition: all 0.4s; margin-top: 1rem; @@ -81,6 +86,7 @@ background: var(--general-section-6); } } + &-info-panel { &__close-action { position: absolute; @@ -121,30 +127,36 @@ width: 100%; } } + &__header { - padding: 1.6rem; - margin-top: 4.8rem; &--listed { - margin-top: 0.6rem; + margin-bottom: 0.8rem; } + @include mobile { height: auto; margin-top: 0; } + .title { text-align: center; height: 7.2; + @include mobile { height: auto; } } + .subtitle { text-align: center; margin-top: 1.6rem; + &__has-list { + text-align: center; margin-top: 0; - text-align: start; + @include mobile { + text-align: start; width: 85%; } } @@ -154,7 +166,6 @@ &__content { display: flex; align-items: flex-start; - height: var(--tab-content-height); flex-grow: 1; background: var(--general-main-1); @@ -162,21 +173,26 @@ position: relative; @include desktop { - padding: 1.6rem 0 0; + padding: 80px 216px 0; } @include mobile { height: calc(100vh - 19rem); align-items: flex-start; + padding: 1.6rem; } + .quick-panel { flex: 1 1 44%; } + .preview-panel { display: none; flex: 1 1 56%; + &--active { display: block; + @include mobile { display: none; } @@ -186,6 +202,7 @@ &__mobile-container { @include flex-center; + margin: 1.4rem 0; column-gap: 1rem; height: 3.2rem; @@ -195,34 +212,6 @@ } } - &__centered { - @include flex-center; - flex-direction: column; - - @include tablet { - width: 100%; - } - - @include mobile { - width: 100%; - padding: 0 1rem; - } - - &--not-listed { - @include desktop { - width: 100%; - margin-top: 4.8rem; - } - } - - &--listed { - align-items: initial; - justify-content: flex-start; - padding: 0.4rem 2.4rem; - height: 100%; - } - } - &__preview { height: calc(100% + 2rem); @@ -242,7 +231,9 @@ border: none; height: 4rem; padding: 1rem; + @include flex-center; + cursor: pointer; @include mobile { @@ -278,7 +269,9 @@ border: none; height: 4rem; padding: 1rem; + @include flex-center; + cursor: pointer; } @@ -317,19 +310,17 @@ } &__table { - @include mobile-tablet-mix { - width: calc(100% - 9.3rem); - } - &--minimized { width: 100%; } &__tiles { @include flex-center(center, flex-start); + word-wrap: break-word; font-size: 1.3rem; text-align: center; + padding: 3.2rem; @include mobile { flex-wrap: wrap; @@ -342,6 +333,7 @@ display: flex; justify-content: space-around; flex-flow: unset; + padding: 2.4rem 0 1.6rem; } } } @@ -354,8 +346,9 @@ &__block { @include flex-center; + flex-direction: column; - padding-inline-end: 2.4rem; + padding-inline-end: 4rem; @include mobile { padding: 1rem; @@ -369,7 +362,7 @@ width: 9.1rem; word-wrap: break-word; text-align: center; - height: 4.2rem; + @include mobile { height: auto; } @@ -383,9 +376,6 @@ text-align: center; } } - &:nth-last-child(2) { - padding-inline: 0; - } } &__images { @@ -408,11 +398,17 @@ .user-guide { position: absolute; - inset-inline-end: 1.6rem; + inset-inline-end: 2.4rem; top: 1.8rem; z-index: 1; + + @include mobile { + inset-inline-end: 1.6rem; + } + &__button { @include flex-center; + padding: 0.8rem 1.4rem; border: none; outline: none; @@ -426,18 +422,22 @@ text-align: center; padding: 0; } + &:hover { cursor: pointer; } } + &__icon { height: 24px; width: 24px; + @include mobile { height: 16px; width: 16px; } } + &__label { margin-inline-start: 0.4rem; } diff --git a/src/pages/dashboard/dashboard.tsx b/src/pages/dashboard/dashboard.tsx index 7eea1564..6581aeb8 100644 --- a/src/pages/dashboard/dashboard.tsx +++ b/src/pages/dashboard/dashboard.tsx @@ -9,7 +9,6 @@ import { localize } from '@/utils/tmp/dummy'; import OnboardTourHandler from '../tutorials/dbot-tours/onboarding-tour'; -import Local from './load-bot-preview/local'; import Cards from './cards'; import InfoPanel from './info-panel'; import UserGuide from './user-guide'; @@ -69,22 +68,7 @@ const DashboardComponent = observer(({ handleTabChange }: TMobileIconGuide) => { )} - - - - {is_mobile && } - - - {has_dashboard_strategies && !is_mobile && ( - - - - )} + diff --git a/src/pages/dashboard/load-bot-preview/dashboard-bot-list.tsx b/src/pages/dashboard/load-bot-preview/dashboard-bot-list.tsx new file mode 100644 index 00000000..4b9b3bc3 --- /dev/null +++ b/src/pages/dashboard/load-bot-preview/dashboard-bot-list.tsx @@ -0,0 +1,100 @@ +import React from 'react'; +import { observer } from 'mobx-react-lite'; + +import MobileWrapper from '@/components/shared_ui/mobile-wrapper'; +import Text from '@/components/shared_ui/text'; +import { getSavedWorkspaces } from '@/external/bot-skeleton'; +import { useStore } from '@/hooks/useStore'; +import { Localize, localize } from '@/utils/tmp/dummy'; + +import DeleteDialog from './delete-dialog'; +import RecentWorkspace from './recent-workspace'; +import SaveModal from './save-modal'; + +import './index.scss'; + +type THeader = { + label: string; + className: string; +}; + +const HEADERS: THeader[] = [ + { + label: localize('Bot name'), + className: 'bot-list__header__label', + }, + { + label: localize('Last modified'), + className: 'bot-list__header__time-stamp', + }, + { + label: localize('Status'), + className: 'bot-list__header__load-type', + }, +]; + +const DashboardBotList = observer(() => { + const { load_modal, dashboard } = useStore(); + const { setDashboardStrategies, dashboard_strategies } = load_modal; + const { setStrategySaveType, strategy_save_type } = dashboard; + const { ui } = useStore(); + const { is_mobile } = ui; + const get_first_strategy_info = React.useRef(false); + const get_instacee = React.useRef(false); + + React.useEffect(() => { + setStrategySaveType(''); + const getStrategies = async () => { + const recent_strategies = await getSavedWorkspaces(); + setDashboardStrategies(recent_strategies); + if (!get_instacee.current) { + get_instacee.current = true; + } + }; + getStrategies(); + //this dependency is used when we use the save modal popup + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [strategy_save_type]); + + React.useEffect(() => { + if (!dashboard_strategies?.length && !get_first_strategy_info.current) { + get_first_strategy_info.current = true; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + if (!dashboard_strategies?.length) return null; + return ( + + + + + + + + + {HEADERS.map(({ label, className }) => { + return ( + + + {label} + + + ); + })} + + + {dashboard_strategies.map((workspace, index) => { + return ; + })} + + + + + + + + ); +}); + +export default DashboardBotList; diff --git a/src/pages/dashboard/load-bot-preview/index.scss b/src/pages/dashboard/load-bot-preview/index.scss index 0a0329c5..07608e0f 100644 --- a/src/pages/dashboard/load-bot-preview/index.scss +++ b/src/pages/dashboard/load-bot-preview/index.scss @@ -1,6 +1,7 @@ -@import './google-drive.scss'; -@import './delete-dialog.scss'; -@import './save-modal.scss'; +@import '../../../styles/mixins'; +@import './google-drive'; +@import './delete-dialog'; +@import './save-modal'; .load-strategy { &__wrapper { @@ -17,6 +18,7 @@ &__body { height: 100%; } + .google-drive-label { width: 100%; } @@ -69,18 +71,6 @@ } } - &__title { - font-size: var(--text-size-s); - font-weight: 700; - margin: 1.6rem 0; - margin-inline-end: 1.6rem; - color: var(--text-general); - - &--listed { - margin: 0 !important; - } - } - &__button-group { display: flex; justify-content: flex-end; @@ -155,6 +145,7 @@ &-area { @include flex-center; + flex-direction: column; border: dashed 0.2rem var(--border-normal); border-radius: $BORDER_RADIUS; @@ -218,6 +209,7 @@ &__google-drive { @include flex-center; + flex-direction: column; @include mobile { @@ -304,6 +296,7 @@ } @include flex-center; + height: 4rem; box-shadow: inset 0 -0.1rem 0 $color-grey-2; } @@ -337,7 +330,7 @@ } &__button-group { - box-shadow: inset 0 0.2rem 0rem $color-grey-2; + box-shadow: inset 0 0.2rem 0 $color-grey-2; margin: 0; padding: 1.6rem; } @@ -358,59 +351,100 @@ } } -.flex-align-center { +%flex-align-center { display: flex; align-items: center; } .bot-list { + &__container { + display: flex; + justify-content: center; + width: 100%; + + @include mobile { + display: unset; + } + } + &__wrapper { - height: calc(100vh - 54.8rem); + width: 623px; + + @include mobile { + width: unset; + } + } + + &__table { + height: calc(100vh - 57rem); overflow: auto; } + + &__title { + font-size: var(--text-size-s); + font-weight: 700; + margin-block-end: 0.8rem; + color: var(--text-general); + + &--listed { + margin: 0 !important; + } + } + &__header { - @extend .flex-align-center; - padding: 0 0.8rem; + @extend %flex-align-center; + border-bottom: 1px solid var(--border-divider); @include mobile { - padding: 0; + padding: 0.8rem 0; } &__label { - padding: 0.8rem; + padding: 0.8rem 1.6rem; width: 35%; + @include mobile { width: 40%; - padding-inline-start: 0; + padding: 0; } } + &__time-stamp { width: 20%; - padding: 0.8rem; + padding: 0.8rem 1.6rem; + @include mobile { width: 25%; - padding-inline-end: 0; + padding: 0; } } + &__load-type { width: 25%; - padding: 0.8rem; + padding: 0.8rem 1.6rem; + + @include mobile { + padding: 0; + } } } + &__item { - @extend .flex-align-center; - padding: 0 0.8rem; + @extend %flex-align-center; + border-bottom: 1px solid var(--border-divider); user-select: none; position: relative; + &:hover { cursor: pointer; } &__label { - padding: 0.8rem; + padding: 0.8rem 1.6rem; width: 35%; + .text-wrapper { -webkit-line-clamp: 1; -webkit-box-orient: vertical; @@ -429,16 +463,23 @@ &__time-stamp { width: 20%; - padding: 0.8rem; + padding: 0.8rem 1.6rem; + @include mobile { width: 25%; + padding: 0; } } &__load-type { - @extend .flex-align-center; + @extend %flex-align-center; + width: 25%; - padding: 0.8rem; + padding: 0.8rem 1.6rem; + + @include mobile { + padding: 0; + } .dc-icon { margin-inline-end: 0.8rem; @@ -446,12 +487,14 @@ } &__actions { - @include flex-center(flex-end); + @include flex-center(center); + width: 20%; - padding: 0.8rem; - justify-content: flex-end; + padding: 0.8rem 1.6rem; + &__action-item { margin-inline-end: 1.6rem; + &:last-child { margin-inline: 0; } @@ -459,6 +502,7 @@ @include mobile { width: 10%; + padding: 0; } button { @@ -472,14 +516,15 @@ &__responsive { background: var(--general-main-1); box-shadow: - 0 0 2rem rgba(0, 0, 0, 0.05), - 0 1.6rem 2rem rgba(0, 0, 0, 0.05); + 0 0 2rem rgb(0 0 0 / 5%), + 0 1.6rem 2rem rgb(0 0 0 / 5%); display: none; min-width: 15rem; position: absolute; inset-inline-end: 3rem; top: 4rem; z-index: 100; + &--active { display: block; } @@ -489,19 +534,17 @@ display: flex; height: 4rem; padding: 0.8rem 1.1rem; + .dc-icon { margin-inline-end: 0.8rem; } - - &:last-child { - box-shadow: inset 0 0.1rem 0 var(--border-divider); - } } } &:nth-last-child(-n + 4) { .bot-list__item__responsive { top: -15rem; + &--min { top: 1rem; inset-inline-end: 6rem; diff --git a/src/pages/dashboard/load-bot-preview/recent-footer.tsx b/src/pages/dashboard/load-bot-preview/recent-footer.tsx deleted file mode 100644 index fb796496..00000000 --- a/src/pages/dashboard/load-bot-preview/recent-footer.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { observer } from 'mobx-react-lite'; - -import { Button } from '@deriv-com/ui'; - -import { useStore } from '@/hooks/useStore'; -import { localize } from '@/utils/tmp/dummy'; - -import './index.scss'; - -const RecentFooter = observer(() => { - const { load_modal } = useStore(); - const { is_open_button_loading, loadFileFromRecent } = load_modal; - return ( - - ); -}); - -export default RecentFooter; diff --git a/src/pages/dashboard/user-guide.tsx b/src/pages/dashboard/user-guide.tsx index 2e5fd77f..f28f07a8 100644 --- a/src/pages/dashboard/user-guide.tsx +++ b/src/pages/dashboard/user-guide.tsx @@ -22,7 +22,7 @@ const UserGuide: React.FC = ({ is_mobile, handleTabChange, setActive }} data-testid='btn-user-guide' > - + {!is_mobile && ( {localize('User Guide')} diff --git a/src/styles/index.scss b/src/styles/index.scss new file mode 100644 index 00000000..38e36f79 --- /dev/null +++ b/src/styles/index.scss @@ -0,0 +1,5 @@ +@import 'https://fonts.googleapis.com/css?family=IBM+Plex+Sans:300,400,500,700&display=swap&subset=cyrillic,cyrillic-ext,latin-ext,vietnamese'; + +body { + font-family: 'IBM Plex Sans', sans-serif; +} diff --git a/src/utils/tmp/dummy.tsx b/src/utils/tmp/dummy.tsx index 276b9400..9d499259 100644 --- a/src/utils/tmp/dummy.tsx +++ b/src/utils/tmp/dummy.tsx @@ -1,3 +1,5 @@ +import { memo, SyntheticEvent } from 'react'; + export const localize = (str: unknown, obj: unknown = {}) => `${str} ${JSON.stringify(obj)}`; export const Localize = ({ i18n_default_text = '', values = {}, components = [] }) => { @@ -26,11 +28,28 @@ export const i18n = { language: 'en', }; -export const Icon = ({ icon }) => { - // Simulate placeholder icon rendering - return {icon}; +type TIconComponent = { + icon: string; + className?: string; + onClick?: () => void; }; +const IconComponent: React.FC = ({ icon, ...rest }) => { + const onError = (e: SyntheticEvent) => { + // eslint-disable-next-line no-console + console.info(`${icon} not found, redirecting to fallback`); + (e.target as HTMLImageElement).src = '/assets/icons/IcDashboard.svg'; + }; + + return ( + + + + ); +}; + +export const Icon = memo(IconComponent); + export const IconTradeTypes = ({ children }) => { // Simulate scrollbars return {children};