diff --git a/package.json b/package.json
index 5a4708f..8b556f3 100644
--- a/package.json
+++ b/package.json
@@ -13,11 +13,11 @@
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "6.5.1",
+ "@fortawesome/free-brands-svg-icons": "6.5.1",
"@fortawesome/free-solid-svg-icons": "6.5.1",
- "@fortawesome/react-fontawesome": "0.2.2",
"@multiversx/sdk-core": "14.2.6",
- "@multiversx/sdk-dapp": "^5.x",
- "@multiversx/sdk-dapp-ui": "^0.x",
+ "@multiversx/sdk-dapp": "5.1.3",
+ "@multiversx/sdk-dapp-ui": "0.0.27",
"@multiversx/sdk-dapp-utils": "2.0.2",
"@solidjs/router": "0.13.6",
"axios": "^1.10.0",
@@ -26,11 +26,19 @@
"immer": "10.1.1",
"moment": "2.29.4",
"solid-fa": "0.2.0",
- "solid-js": "1.8.11"
+ "solid-js": "1.8.11",
+ "vite-plugin-svgr": "^4.5.0"
},
"devDependencies": {
+ "@tailwindcss/cli": "4.0.17",
+ "@tailwindcss/postcss": "4.1.3",
+ "@tailwindcss/vite": "4.1.11",
+ "@testing-library/jest-dom": "^6.4.6",
+ "@types/jest": "^29.5.13",
"@typescript-eslint/eslint-plugin": "6.7.0",
"@typescript-eslint/parser": "6.7.0",
+ "@vitejs/plugin-basic-ssl": "^1.1.0",
+ "autoprefixer": "^10.4.19",
"eslint": "8.50.0",
"eslint-config-prettier": "9.0.0",
"eslint-config-standard": "17.1.0",
@@ -40,21 +48,19 @@
"eslint-plugin-node": "11.1.0",
"eslint-plugin-prettier": "5.0.0",
"eslint-plugin-promise": "6.1.1",
- "@testing-library/jest-dom": "^6.4.6",
- "@types/jest": "^29.5.13",
- "@vitejs/plugin-basic-ssl": "^1.1.0",
- "autoprefixer": "^10.4.19",
"jest": "^29.7.0",
"postcss": "^8.4.39",
+ "prettier": "3.2.5",
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
"solid-devtools": "^0.30.1",
- "tailwindcss": "^3.4.4",
+ "tailwindcss": "4.0.15",
"typescript": "^5.3.3",
"vite": "^5.0.11",
"vite-plugin-externalize-deps": "^0.8.0",
"vite-plugin-node-polyfills": "^0.22.0",
"vite-plugin-solid": "^2.10.2",
"vite-plugin-solid-svg": "^0.8.1",
- "vite-tsconfig-paths": "^4.3.2",
- "prettier": "3.2.5"
+ "vite-tsconfig-paths": "^4.3.2"
}
}
diff --git a/postcss.config.js b/postcss.config.js
index 33ad091..8ed13da 100644
--- a/postcss.config.js
+++ b/postcss.config.js
@@ -1,6 +1,6 @@
module.exports = {
plugins: {
- tailwindcss: {},
- autoprefixer: {},
- },
-}
+ '@tailwindcss/postcss': {},
+ autoprefixer: {}
+ }
+};
diff --git a/public/dark-theme-bg.png b/public/dark-theme-bg.png
new file mode 100644
index 0000000..63b49d2
Binary files /dev/null and b/public/dark-theme-bg.png differ
diff --git a/public/light-theme-bg.png b/public/light-theme-bg.png
new file mode 100644
index 0000000..ed4efe2
Binary files /dev/null and b/public/light-theme-bg.png differ
diff --git a/public/vibe-theme-bg.png b/public/vibe-theme-bg.png
new file mode 100644
index 0000000..5142029
Binary files /dev/null and b/public/vibe-theme-bg.png differ
diff --git a/src/assets/fonts/Satoshi-Bold.woff2 b/src/assets/fonts/Satoshi-Bold.woff2
new file mode 100644
index 0000000..0a8db7a
Binary files /dev/null and b/src/assets/fonts/Satoshi-Bold.woff2 differ
diff --git a/src/assets/fonts/Satoshi-Medium.woff2 b/src/assets/fonts/Satoshi-Medium.woff2
new file mode 100644
index 0000000..ffd0ac9
Binary files /dev/null and b/src/assets/fonts/Satoshi-Medium.woff2 differ
diff --git a/src/assets/fonts/Satoshi-Regular.woff2 b/src/assets/fonts/Satoshi-Regular.woff2
new file mode 100644
index 0000000..81c40ab
Binary files /dev/null and b/src/assets/fonts/Satoshi-Regular.woff2 differ
diff --git a/src/assets/img/arc-logo.svg b/src/assets/img/arc-logo.svg
new file mode 100644
index 0000000..8278277
--- /dev/null
+++ b/src/assets/img/arc-logo.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/background.svg b/src/assets/img/background.svg
new file mode 100644
index 0000000..76e0a6a
--- /dev/null
+++ b/src/assets/img/background.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/batch-tx.svg b/src/assets/img/batch-tx.svg
new file mode 100644
index 0000000..748e6c6
--- /dev/null
+++ b/src/assets/img/batch-tx.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/img/brave-logo.svg b/src/assets/img/brave-logo.svg
new file mode 100644
index 0000000..c325a78
--- /dev/null
+++ b/src/assets/img/brave-logo.svg
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/bright-light-icon.svg b/src/assets/img/bright-light-icon.svg
new file mode 100644
index 0000000..8107e91
--- /dev/null
+++ b/src/assets/img/bright-light-icon.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/img/chrome-logo.svg b/src/assets/img/chrome-logo.svg
new file mode 100644
index 0000000..247b9e6
--- /dev/null
+++ b/src/assets/img/chrome-logo.svg
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/circles.svg b/src/assets/img/circles.svg
new file mode 100644
index 0000000..4027d04
--- /dev/null
+++ b/src/assets/img/circles.svg
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/expand-up-down.svg b/src/assets/img/expand-up-down.svg
new file mode 100644
index 0000000..56451d3
--- /dev/null
+++ b/src/assets/img/expand-up-down.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/img/extension-image.png b/src/assets/img/extension-image.png
new file mode 100644
index 0000000..d459da1
Binary files /dev/null and b/src/assets/img/extension-image.png differ
diff --git a/src/assets/img/firefox-logo.svg b/src/assets/img/firefox-logo.svg
new file mode 100644
index 0000000..e0fac39
--- /dev/null
+++ b/src/assets/img/firefox-logo.svg
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/heart copy.svg b/src/assets/img/heart copy.svg
new file mode 100644
index 0000000..5811a38
--- /dev/null
+++ b/src/assets/img/heart copy.svg
@@ -0,0 +1,9 @@
+
+
+
+ heart
+ Created with Sketch.
+
+
+
+
\ No newline at end of file
diff --git a/src/assets/img/ledger-icon.svg b/src/assets/img/ledger-icon.svg
new file mode 100644
index 0000000..a2a1d0b
--- /dev/null
+++ b/src/assets/img/ledger-icon.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/assets/img/metamask-icon.svg b/src/assets/img/metamask-icon.svg
new file mode 100644
index 0000000..77b6b3c
--- /dev/null
+++ b/src/assets/img/metamask-icon.svg
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/multiversx-logo copy.svg b/src/assets/img/multiversx-logo copy.svg
new file mode 100644
index 0000000..8ae687d
--- /dev/null
+++ b/src/assets/img/multiversx-logo copy.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/passkey-icon.svg b/src/assets/img/passkey-icon.svg
new file mode 100644
index 0000000..2262c20
--- /dev/null
+++ b/src/assets/img/passkey-icon.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/assets/img/ping-pong-abi.svg b/src/assets/img/ping-pong-abi.svg
new file mode 100644
index 0000000..68fe13d
--- /dev/null
+++ b/src/assets/img/ping-pong-abi.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/img/ping-pong-backend.svg b/src/assets/img/ping-pong-backend.svg
new file mode 100644
index 0000000..d2acc65
--- /dev/null
+++ b/src/assets/img/ping-pong-backend.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/img/teal-lab-icon.svg b/src/assets/img/teal-lab-icon.svg
new file mode 100644
index 0000000..7fe0a03
--- /dev/null
+++ b/src/assets/img/teal-lab-icon.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/img/template-logo.svg b/src/assets/img/template-logo.svg
new file mode 100644
index 0000000..c73a330
--- /dev/null
+++ b/src/assets/img/template-logo.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/img/vibe-mode-icon.svg b/src/assets/img/vibe-mode-icon.svg
new file mode 100644
index 0000000..17f2c6a
--- /dev/null
+++ b/src/assets/img/vibe-mode-icon.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/img/wallet-brave-logo.svg b/src/assets/img/wallet-brave-logo.svg
new file mode 100644
index 0000000..222b8cd
--- /dev/null
+++ b/src/assets/img/wallet-brave-logo.svg
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/wallet-chrome-logo.svg b/src/assets/img/wallet-chrome-logo.svg
new file mode 100644
index 0000000..47e432d
--- /dev/null
+++ b/src/assets/img/wallet-chrome-logo.svg
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/wallet-firefox-logo.svg b/src/assets/img/wallet-firefox-logo.svg
new file mode 100644
index 0000000..8819cd5
--- /dev/null
+++ b/src/assets/img/wallet-firefox-logo.svg
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/web-wallet-icon.svg b/src/assets/img/web-wallet-icon.svg
new file mode 100644
index 0000000..4abf0c4
--- /dev/null
+++ b/src/assets/img/web-wallet-icon.svg
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/x-logo.svg b/src/assets/img/x-logo.svg
new file mode 100644
index 0000000..79f7141
--- /dev/null
+++ b/src/assets/img/x-logo.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/img/x.svg b/src/assets/img/x.svg
new file mode 100644
index 0000000..77aeac4
--- /dev/null
+++ b/src/assets/img/x.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/img/xportal-icon.svg b/src/assets/img/xportal-icon.svg
new file mode 100644
index 0000000..fdea7ff
--- /dev/null
+++ b/src/assets/img/xportal-icon.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
deleted file mode 100644
index 31a7349..0000000
--- a/src/components/Button.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { IPropsWithChildren, IPropsWithClass } from 'types';
-
-interface ButtonType extends IPropsWithClass, IPropsWithChildren {
- onClick?: (e: MouseEvent) => void;
- disabled?: boolean;
- dataCy?: string;
- id?: string;
- type?: 'button' | 'submit' | 'reset';
-}
-
-export const Button = ({
- children,
- onClick,
- disabled = false,
- type = 'button',
- id,
- class:
- className = 'flex items-center justify-center rounded-lg px-3 py-2 text-center hover:no-underline my-0 bg-blue-600 text-white hover:bg-blue-700 mr-0 disabled:bg-gray-200 disabled:text-black disabled:cursor-not-allowed',
- ...otherProps
-}: ButtonType) => {
- return (
-
- {children}
-
- );
-};
diff --git a/src/components/Card.tsx b/src/components/Card.tsx
deleted file mode 100644
index 2775c66..0000000
--- a/src/components/Card.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
-import Fa from 'solid-fa';
-import { IPropsWithChildren, IPropsWithClass } from 'types';
-
-interface CardType extends IPropsWithChildren, IPropsWithClass {
- title: string;
- description?: string;
- reference: string;
- anchor?: string;
-}
-
-export const Card = (props: CardType) => {
- const { title, children, description, reference, anchor } = props;
-
- return (
-
-
- {title}
-
-
-
-
- {description &&
{description}
}
- {children}
-
- );
-};
diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx
new file mode 100644
index 0000000..a4289ef
--- /dev/null
+++ b/src/components/Card/Card.tsx
@@ -0,0 +1,40 @@
+import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
+import Fa from 'solid-fa';
+import { IPropsWithChildren, IPropsWithClass } from 'types';
+
+// prettier-ignore
+const styles = {
+ cardContainer: 'card-container flex flex-col gap-4 flex-1 rounded-xl bg-primary transition-all duration-200 ease-out p-6 lg:p-10 justify-center border border-secondary',
+ cardTitle: 'card-title flex justify-between items-center text-2xl font-medium group text-primary transition-all duration-200 ease-out',
+ cardRef: 'card-ref text-link hover:text-primary transition-all duration-200 ease-out flex items-center',
+ cardRefIcon: 'max-w-3.5 max-h-3.5',
+ cardDescription: 'card-description text-secondary transition-all duration-200 ease-out mb-6 text-lg font-medium'
+} satisfies Record;
+
+interface CardType extends IPropsWithChildren, IPropsWithClass {
+ title: string;
+ description?: string;
+ reference: string;
+ anchor?: string;
+}
+
+export const Card = ({
+ title,
+ children,
+ description,
+ reference,
+ anchor,
+ 'data-testid': dataTestId
+}: CardType) => (
+
+
+ {title}
+
+
+
+
+
+ {description &&
{description}
}
+ {children}
+
+);
diff --git a/src/components/Card/index.ts b/src/components/Card/index.ts
new file mode 100644
index 0000000..ca0b060
--- /dev/null
+++ b/src/components/Card/index.ts
@@ -0,0 +1 @@
+export * from './Card';
diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx
new file mode 100644
index 0000000..a756136
--- /dev/null
+++ b/src/components/Footer/Footer.tsx
@@ -0,0 +1,42 @@
+import { faHeart } from '@fortawesome/free-solid-svg-icons';
+import moment from 'moment';
+
+import Fa from 'solid-fa';
+import { getState, networkSelector } from 'lib';
+import { version } from '../../../package.json';
+
+// prettier-ignore
+const styles = {
+ footer: 'footer mx-auto w-full max-w-prose py-4 text-center',
+ footerContainer: 'footer-container flex flex-col gap-2 font-medium items-center justify-center text-sm text-[#989898]',
+ footerDescription: 'footer-description flex items-center justify-center gap-1 text-sm text-neutral-500 gap-1',
+ footerDescriptionNetwork: 'footer-description-network capitalize',
+ footerHeartIcon: 'text-red-500'
+} satisfies Record;
+
+export const Footer = () => {
+ const network = networkSelector(getState());
+ const currentYear = moment().year();
+
+ return (
+
+ );
+};
diff --git a/src/components/Footer/index.ts b/src/components/Footer/index.ts
new file mode 100644
index 0000000..ddcc5a9
--- /dev/null
+++ b/src/components/Footer/index.ts
@@ -0,0 +1 @@
+export * from './Footer';
diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx
new file mode 100644
index 0000000..a170ff4
--- /dev/null
+++ b/src/components/Header/Header.tsx
@@ -0,0 +1,190 @@
+import { faGithub } from '@fortawesome/free-brands-svg-icons';
+import {
+ faBell,
+ faCreditCard,
+ faPowerOff,
+ IconDefinition
+} from '@fortawesome/free-solid-svg-icons';
+
+import { useNavigate } from '@solidjs/router';
+import Fa from 'solid-fa';
+import { Logo, Tooltip } from 'components';
+import { GITHUB_REPO_URL } from 'config';
+import {
+ ACCOUNTS_ENDPOINT,
+ getAccountProvider,
+ MvxDataWithExplorerLink,
+ NotificationsFeedManager,
+ getAccount,
+ getIsLoggedIn,
+ networkSelector,
+ UnlockPanelManager,
+ getState,
+ MvxButton
+} from 'lib';
+import { RouteNamesEnum } from 'localConstants';
+
+import { ThemeTooltip } from './components';
+
+// prettier-ignore
+const styles = {
+ header: 'header flex items-center justify-between px-4 h-16 md:h-20 md:px-10',
+ headerLogo: 'header-logo cursor-pointer transition-opacity duration-200 hover:opacity-75',
+ headerNavigation: 'header-navigation flex items-center gap-2 lg:gap-4',
+ headerNavigationButtons: 'header-navigation-buttons flex gap-2 lg:gap-4',
+ headerNavigationButton: 'header-navigation-button flex justify-center items-center w-8 lg:w-10 h-8 lg:h-10 rounded-xl cursor-pointer relative after:rounded-xl after:absolute after:bg-btn-variant hover:after:bg-btn-hover after:transition-all after:duration-200 after:ease-out after:left-0 after:right-0 after:top-0 after:bottom-0 after:pointer-events-none hover:after:opacity-100',
+ headerNavigationButtonIcon: 'header-navigation-button-icon flex justify-center relative text-xs lg:text-base z-1 items-center text-tertiary',
+ headerNavigationTooltip: 'header-navigation-tooltip p-1 leading-none whitespace-nowrap text-tertiary',
+ headerNavigationNetwork: 'header-navigation-network h-8 border border-secondary rounded-xl lg:h-10 relative w-22 flex items-center justify-center leading-none capitalize text-tertiary before:absolute before:rounded-full before:w-2 before:lg:w-2.5 before:h-2 before:lg:h-2.5 before:bg-btn-primary before:z-2 before:-top-0.25 before:lg:-top-0.5 before:-left-0.25 before:lg:-left-0.5 after:absolute after:bg-primary after:rounded-lg after:opacity-40 after:left-0 after:right-0 after:top-0 after:bottom-0 after:pointer-events-none',
+ headerNavigationNetworkLabel: 'header-navigation-network-label relative z-1',
+ headerNavigationConnect: 'header-navigation-connect h-8 lg:h-10',
+ headerNavigationAddress: 'header-navigation-address h-8 lg:h-10 w-8 lg:w-full justify-center text-xs rounded-xl lg:text-base lg:pr-4 lg:pl-5 max-w-100 flex relative lg:border lg:border-secondary lg:rounded-full items-center gap-3 after:absolute after:bg-btn-tertiary after:rounded-xl lg:after:rounded-full after:opacity-40 after:left-0 after:right-0 after:top-0 after:bottom-0 after:pointer-events-none',
+ headerNavigationAddressWallet: 'header-navigation-address-wallet relative z-1 text-accent hidden lg:flex!',
+ headerNavigationAddressExplorer: 'header-navigation-address-explorer min-w-0 relative z-1 hidden lg:block!',
+ headerNavigationAddressLogout: 'header-navigation-address-logout text-tertiary cursor-pointer relative z-1 transition-all duration-200 ease-out hover:text-accent',
+ trimmedAddress: 'text-primary'
+} satisfies Record;
+
+interface HeaderBrowseButtonType {
+ handleClick: (event: MouseEvent) => void;
+ icon: IconDefinition;
+ isVisible: boolean;
+ label: string;
+}
+
+export const Header = () => {
+ const network = networkSelector(getState());
+ const address = getAccount()?.address;
+
+ const isLoggedIn = getIsLoggedIn();
+ const provider = getAccountProvider();
+ const navigate = useNavigate();
+ const unlockPanelManager = UnlockPanelManager.init({
+ loginHandler: () => {
+ navigate(RouteNamesEnum.dashboard);
+ }
+ });
+
+ const handleOpenUnlockPanel = () => {
+ unlockPanelManager.openUnlockPanel();
+ };
+
+ const handleLogout = async (event: MouseEvent) => {
+ event.preventDefault();
+ await provider.logout();
+ navigate(RouteNamesEnum.home);
+ };
+
+ const handleGitHubBrowsing = (event: MouseEvent) => {
+ event.preventDefault();
+ window.open(GITHUB_REPO_URL);
+ };
+
+ const handleNotificationsBrowsing = (event: MouseEvent) => {
+ event.preventDefault();
+ NotificationsFeedManager.getInstance().openNotificationsFeed();
+ };
+
+ const headerBrowseButtons: HeaderBrowseButtonType[] = [
+ {
+ label: 'GitHub',
+ handleClick: handleGitHubBrowsing,
+ icon: faGithub as IconDefinition,
+ isVisible: true
+ },
+ {
+ label: 'Notifications',
+ handleClick: handleNotificationsBrowsing,
+ icon: faBell,
+ isVisible: isLoggedIn
+ }
+ ];
+
+ const handleLogoClick = (event: MouseEvent) => {
+ event.preventDefault();
+ navigate(isLoggedIn ? RouteNamesEnum.dashboard : RouteNamesEnum.home);
+ };
+
+ return (
+
+ );
+};
diff --git a/src/components/Header/components/ThemeTooltip/ThemeTooltip.tsx b/src/components/Header/components/ThemeTooltip/ThemeTooltip.tsx
new file mode 100644
index 0000000..28f0daf
--- /dev/null
+++ b/src/components/Header/components/ThemeTooltip/ThemeTooltip.tsx
@@ -0,0 +1,140 @@
+import {
+ faArrowRightLong,
+ faChevronDown
+} from '@fortawesome/free-solid-svg-icons';
+import classNames from 'classnames';
+
+import Fa from 'solid-fa';
+import { createMemo, createSignal, onMount } from 'solid-js';
+
+import { Tooltip } from 'components';
+
+import { ThemeTooltipDots } from './components';
+
+interface ThemeTooltipOptionType {
+ label: string;
+ identifier: string;
+ dotColors: string[];
+}
+
+// prettier-ignore
+const styles = {
+ themeTooltip: 'theme-tooltip',
+ themeTooltipTrigger: 'theme-tooltip-trigger flex h-8 lg:h-10 cursor-pointer gap-1 lg:gap-2 items-center justify-center w-12 min-w-12 max-w-12 lg:min-w-16 lg:max-w-16 lg:w-16 relative after:absolute after:bg-btn-variant after:left-0 after:right-0 after:top-0 after:bottom-0 after:pointer-events-none after:rounded-xl after:duration-200 after:ease-out after:transition-all hover:after:opacity-100',
+ themeTooltipTriggerDots: 'theme-tooltip-trigger-dots relative z-1',
+ themeTooltipTriggerIcon: 'theme-tooltip-trigger-icon transition-all text-xs relative z-1 duration-200 ease-out text-tertiary',
+ themeTooltipTriggerIconRotated: 'rotate-180',
+ themeTooltipOptions: 'theme-tooltip-options flex-col gap-1 flex',
+ themeTooltipOption: 'theme-tooltip-option w-60 flex p-3 flex group text-primary font-normal gap-3 items-center transition-all duration-200 ease-out cursor-pointer relative after:transition-all after:duration-200 after:ease-out after:absolute after:opacity-0 hover:after:opacity-40 after:left-0 after:right-0 after:bottom-0 after:top-0 after:bg-accent after:pointer-events-none after:rounded-lg',
+ themeTooltipOptionActive: 'after:bg-accent after:opacity-100 !text-accent !font-medium transition-all duration-200 ease-out',
+ themeTooltipOptionDots: 'theme-tooltip-option-dots flex w-5 h-5 justify-between relative z-1',
+ themeTooltipOptionLabel: 'theme-tooltip-option-label leading-none relative z-1 text-base',
+ themeTooltipOptionArrow: 'theme-tooltip-option-arrow ml-auto duration-200 transition-all ease-out opacity-0 text-link group-hover:opacity-100'
+} satisfies Record;
+
+export const ThemeTooltip = () => {
+ const [rootTheme, setRootTheme] = createSignal(
+ document.documentElement.getAttribute('data-mvx-theme')
+ );
+
+ const themeOptions: ThemeTooltipOptionType[] = [
+ {
+ label: 'TealLab',
+ identifier: 'mvx:dark-theme',
+ dotColors: ['#23F7DD', '#262626', '#B6B3AF', '#FFFFFF']
+ },
+ {
+ label: 'VibeMode',
+ identifier: 'mvx:vibe-theme',
+ dotColors: ['#471150', '#5A2A62', '#D200FA', '#FFFFFF']
+ },
+ {
+ label: 'BrightLight',
+ identifier: 'mvx:light-theme',
+ dotColors: ['#000000', '#A5A5A5', '#E2DEDC', '#F3EFED']
+ }
+ ];
+
+ const activeTheme = createMemo(() =>
+ themeOptions.find((themeOption) => themeOption.identifier === rootTheme())
+ );
+
+ const handleThemeSwitch =
+ (themeOption: ThemeTooltipOptionType) => (event: MouseEvent) => {
+ event.preventDefault();
+ setRootTheme(themeOption.identifier);
+
+ document.documentElement.setAttribute(
+ 'data-mvx-theme',
+ themeOption.identifier
+ );
+ };
+
+ onMount(() => {
+ const observer = new MutationObserver(() => {
+ const theme = document.documentElement.getAttribute('data-mvx-theme');
+ setRootTheme(theme);
+ });
+
+ observer.observe(document.documentElement, {
+ attributes: true,
+ attributeFilter: ['data-mvx-theme']
+ });
+
+ return () => observer.disconnect();
+ });
+
+ return (
+ {
+ const currentTheme = activeTheme();
+
+ return (
+
+
+
+
+
+ );
+ }}
+ >
+
+
+ );
+};
diff --git a/src/components/Header/components/ThemeTooltip/components/ThemeTooltipDots/ThemeTooltipDots.tsx b/src/components/Header/components/ThemeTooltip/components/ThemeTooltipDots/ThemeTooltipDots.tsx
new file mode 100644
index 0000000..9406acb
--- /dev/null
+++ b/src/components/Header/components/ThemeTooltip/components/ThemeTooltipDots/ThemeTooltipDots.tsx
@@ -0,0 +1,27 @@
+import classNames from 'classnames';
+
+import { IPropsWithClass } from 'types';
+
+// prettier-ignore
+const styles = {
+ themeTooltipDots: 'theme-tooltip-dots flex w-4 h-4 lg:w-5 gap-1 min-w-4 lg:min-w-5 lg:h-5 justify-between flex-wrap',
+ themeTooltipDot: 'theme-tooltip-dot w-1.5 h-1.5 lg:w-2 lg:h-2 lg:min-w-2 lg:min-h-2 min-w-1.5 lg:min-w-2 rounded-full transition-all duration-200 ease-out'
+} satisfies Record;
+
+interface ThemeTooltipDotsPropsType extends IPropsWithClass {
+ dotColors: string[];
+}
+
+export const ThemeTooltipDots = ({
+ dotColors,
+ class: className
+}: ThemeTooltipDotsPropsType) => (
+
+);
diff --git a/src/components/Header/components/ThemeTooltip/components/ThemeTooltipDots/index.ts b/src/components/Header/components/ThemeTooltip/components/ThemeTooltipDots/index.ts
new file mode 100644
index 0000000..a0c78a4
--- /dev/null
+++ b/src/components/Header/components/ThemeTooltip/components/ThemeTooltipDots/index.ts
@@ -0,0 +1 @@
+export * from './ThemeTooltipDots';
diff --git a/src/components/Header/components/ThemeTooltip/components/index.ts b/src/components/Header/components/ThemeTooltip/components/index.ts
new file mode 100644
index 0000000..a0c78a4
--- /dev/null
+++ b/src/components/Header/components/ThemeTooltip/components/index.ts
@@ -0,0 +1 @@
+export * from './ThemeTooltipDots';
diff --git a/src/components/Header/components/ThemeTooltip/index.ts b/src/components/Header/components/ThemeTooltip/index.ts
new file mode 100644
index 0000000..3deaf1f
--- /dev/null
+++ b/src/components/Header/components/ThemeTooltip/index.ts
@@ -0,0 +1 @@
+export * from './ThemeTooltip';
diff --git a/src/components/Header/components/index.ts b/src/components/Header/components/index.ts
new file mode 100644
index 0000000..3deaf1f
--- /dev/null
+++ b/src/components/Header/components/index.ts
@@ -0,0 +1 @@
+export * from './ThemeTooltip';
diff --git a/src/components/Header/index.ts b/src/components/Header/index.ts
new file mode 100644
index 0000000..266dec8
--- /dev/null
+++ b/src/components/Header/index.ts
@@ -0,0 +1 @@
+export * from './Header';
diff --git a/src/components/Label.tsx b/src/components/Label.tsx
deleted file mode 100644
index fcd5618..0000000
--- a/src/components/Label.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { IPropsWithChildren } from 'types';
-
-export const Label = ({ children }: IPropsWithChildren) => {
- return {children} ;
-};
diff --git a/src/components/Label/Label.tsx b/src/components/Label/Label.tsx
new file mode 100644
index 0000000..4e8555d
--- /dev/null
+++ b/src/components/Label/Label.tsx
@@ -0,0 +1,10 @@
+import { IPropsWithChildren } from 'types';
+
+// prettier-ignore
+const styles = {
+ labelContainer: 'label-container text-secondary transition-all duration-200 ease-out text-sm font-normal'
+} satisfies Record;
+
+export const Label = ({ children }: IPropsWithChildren) => (
+ {children}
+);
diff --git a/src/components/Label/index.ts b/src/components/Label/index.ts
new file mode 100644
index 0000000..ca58c61
--- /dev/null
+++ b/src/components/Label/index.ts
@@ -0,0 +1 @@
+export * from './Label';
diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx
index 99f2e0a..5ddf786 100644
--- a/src/components/Layout/Layout.tsx
+++ b/src/components/Layout/Layout.tsx
@@ -1,16 +1,19 @@
-import type { Component } from 'solid-js';
+import { Component } from 'solid-js';
+import { Footer, Header } from 'components';
import { IPropsWithChildren } from 'types';
-import { Footer } from './components/Footer';
-import { Header } from './components/Header';
-export const Layout: Component = ({ children }) => {
- return (
-
-
-
- {children}
-
-
-
- );
-};
+// prettier-ignore
+const styles = {
+ layoutContainer: 'layout-container flex min-h-screen flex-col bg-accent transition-all duration-200 ease-out',
+ mainContainer: 'main-container flex flex-grow items-stretch justify-center'
+} satisfies Record;
+
+export const Layout: Component = ({ children }) => (
+
+
+
+ {children}
+
+
+
+);
diff --git a/src/components/Layout/components/ConnectButton.tsx b/src/components/Layout/components/ConnectButton.tsx
deleted file mode 100644
index 2181e81..0000000
--- a/src/components/Layout/components/ConnectButton.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { useNavigate } from '@solidjs/router';
-import { Button } from 'components';
-import { UnlockPanelManager } from 'lib';
-import { RouteNamesEnum } from 'localConstants';
-
-export const ConnectButton = () => {
- const navigate = useNavigate();
- const unlockPanelManager = UnlockPanelManager.init({
- loginHandler: () => {
- navigate(RouteNamesEnum.dashboard);
- }
- });
-
- const handleOpenUnlockPanel = () => {
- unlockPanelManager.openUnlockPanel();
- };
-
- return Connect ;
-};
diff --git a/src/components/Layout/components/Footer.tsx b/src/components/Layout/components/Footer.tsx
deleted file mode 100644
index f791337..0000000
--- a/src/components/Layout/components/Footer.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import HeartIcon from 'assets/img/heart.svg?component-solid';
-
-export const Footer = () => {
- return (
-
- );
-};
diff --git a/src/components/Layout/components/GitHubButton.tsx b/src/components/Layout/components/GitHubButton.tsx
deleted file mode 100644
index 869a7fc..0000000
--- a/src/components/Layout/components/GitHubButton.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { GITHUB_REPO_URL } from 'config';
-
-export const GitHubButton = () => {
- return (
-
-
-
-
-
- );
-};
diff --git a/src/components/Layout/components/Header.tsx b/src/components/Layout/components/Header.tsx
deleted file mode 100644
index 1b2af33..0000000
--- a/src/components/Layout/components/Header.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import { useNavigate } from '@solidjs/router';
-import MultiversXLogo from 'assets/img/multiversx-logo.svg?component-solid';
-import { Button } from 'components/Button';
-import { MxLink } from 'components/MxLink';
-import { environment } from 'config';
-import { getAccountProvider, getIsLoggedIn } from 'lib';
-import { RouteNamesEnum } from 'localConstants';
-import { ConnectButton } from './ConnectButton';
-import { GitHubButton } from './GitHubButton';
-import { NotificationsButton } from './NotificationsButton';
-
-export const Header = () => {
- const isLoggedIn = getIsLoggedIn();
- const navigate = useNavigate();
- const provider = getAccountProvider();
-
- const handleLogout = async () => {
- await provider.logout();
- navigate(RouteNamesEnum.home);
- };
-
- return (
-
-
-
-
-
-
-
-
-
-
-
- {isLoggedIn ? (
- <>
-
-
- Close
-
- >
- ) : (
-
- )}
-
-
-
- );
-};
diff --git a/src/components/Layout/components/NotificationsButton.tsx b/src/components/Layout/components/NotificationsButton.tsx
deleted file mode 100644
index 8a15126..0000000
--- a/src/components/Layout/components/NotificationsButton.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { faBell } from '@fortawesome/free-solid-svg-icons';
-import Fa from 'solid-fa';
-import { Button } from 'components';
-import { NotificationsFeedManager } from 'lib';
-
-export const NotificationsButton = () => {
- const handleOpenNotificationsFeed = () => {
- NotificationsFeedManager.getInstance().openNotificationsFeed();
- };
-
- return (
-
-
-
- );
-};
diff --git a/src/components/Loader.tsx b/src/components/Loader/Loader.tsx
similarity index 79%
rename from src/components/Loader.tsx
rename to src/components/Loader/Loader.tsx
index 6d2529e..67cf9fe 100644
--- a/src/components/Loader.tsx
+++ b/src/components/Loader/Loader.tsx
@@ -5,7 +5,7 @@ import { Component } from 'solid-js';
export const Loader: Component = () => {
return (
-
+
);
};
diff --git a/src/components/Loader/index.ts b/src/components/Loader/index.ts
new file mode 100644
index 0000000..d5ce981
--- /dev/null
+++ b/src/components/Loader/index.ts
@@ -0,0 +1 @@
+export * from './Loader';
diff --git a/src/components/Logo/Logo.tsx b/src/components/Logo/Logo.tsx
new file mode 100644
index 0000000..9fb5ae1
--- /dev/null
+++ b/src/components/Logo/Logo.tsx
@@ -0,0 +1,32 @@
+import classNames from 'classnames';
+
+// prettier-ignore
+const styles = {
+ logo: 'logo flex items-center justify-center gap-3',
+ logoIcon: 'logo-icon relative',
+ logoIconEmpty: 'logo-icon-empty w-4 h-4 bg-accent border-2 border-logo z-1 relative transition-all duration-200 ease-out',
+ logoIconFilled: 'logo-icon-filled w-4 h-4 bg-logo-primary absolute left-0.75 bottom-0.75 transition-all duration-200 ease-out',
+ logoText: 'logo-text text-xl lg:text-2xl font-medium flex text-primary transition-all duration-200 ease-out relative -top-0.5 leading-none',
+ logoTextHidden: 'logo-text-hidden hidden lg:!flex'
+} satisfies Record;
+
+interface LogoPropsType {
+ hideTextOnMobile?: boolean;
+}
+
+export const Logo = ({ hideTextOnMobile }: LogoPropsType) => (
+
+
+
+
+ dApp Template
+
+
+);
diff --git a/src/components/Logo/index.ts b/src/components/Logo/index.ts
new file mode 100644
index 0000000..d97c695
--- /dev/null
+++ b/src/components/Logo/index.ts
@@ -0,0 +1 @@
+export * from './Logo';
diff --git a/src/components/MissingNativeAuthError/MissingNativeAuthError.tsx b/src/components/MissingNativeAuthError/MissingNativeAuthError.tsx
new file mode 100644
index 0000000..520838f
--- /dev/null
+++ b/src/components/MissingNativeAuthError/MissingNativeAuthError.tsx
@@ -0,0 +1,22 @@
+import { OutputContainer } from '../OutputContainer';
+
+// prettier-ignore
+const styles = {
+ errorContainer: 'error-container flex items-center gap-1',
+ emphasizedText: 'emphasized-text text-red-500 font-bold',
+ nativeAuthText: 'native-auth-text font-bold italic leading-none text-primary transition-all duration-200 ease-out'
+} satisfies Record;
+
+export const MissingNativeAuthError = () => (
+
+
+
+ Information could
+ NOT
+ be displayed because
+ nativeAuth
+ is not active
+
+
+
+);
diff --git a/src/components/MissingNativeAuthError/index.ts b/src/components/MissingNativeAuthError/index.ts
new file mode 100644
index 0000000..b91f784
--- /dev/null
+++ b/src/components/MissingNativeAuthError/index.ts
@@ -0,0 +1 @@
+export * from './MissingNativeAuthError';
diff --git a/src/components/MxLink.tsx b/src/components/MxLink.tsx
deleted file mode 100644
index d7163d4..0000000
--- a/src/components/MxLink.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { A } from '@solidjs/router';
-import { IPropsWithChildren, IPropsWithClass } from 'types';
-
-interface MxLinkPropsType extends IPropsWithChildren, IPropsWithClass {
- to: string;
-}
-
-export const MxLink = ({
- to,
- children,
- class:
- className = 'inline-block rounded-lg px-3 py-2 text-center hover:no-underline my-0 bg-blue-600 text-white hover:bg-blue-700 ml-2 mr-0'
-}: MxLinkPropsType) => {
- return (
-
- {children}
-
- );
-};
diff --git a/src/components/OutputContainer/OutputContainer.tsx b/src/components/OutputContainer/OutputContainer.tsx
index cdfe1b2..81956e7 100644
--- a/src/components/OutputContainer/OutputContainer.tsx
+++ b/src/components/OutputContainer/OutputContainer.tsx
@@ -1,24 +1,28 @@
import classNames from 'classnames';
-import { Loader } from 'components/Loader';
+
+import { Loader } from 'components';
import { IPropsWithChildren, IPropsWithClass } from 'types';
+// prettier-ignore
+const styles = {
+ outputContainer: 'output-container text-sm text-primary font-normal bg-secondary transition-all duration-300 rounded-xl'
+} satisfies Record;
+
type OutputContainerPropsType = IPropsWithChildren &
IPropsWithClass & {
isLoading?: boolean;
};
-export const OutputContainer = (props: OutputContainerPropsType) => {
- const { children, isLoading = false, class: className = 'p-4' } = props;
-
- return (
-
- {isLoading ? : children}
-
- );
-};
+export const OutputContainer = ({
+ children,
+ isLoading = false,
+ class: className = 'p-4',
+ 'data-testid': dataTestId
+}: OutputContainerPropsType) => (
+
+ {isLoading ? : children}
+
+);
diff --git a/src/components/OutputContainer/components/PingPongOutput.tsx b/src/components/OutputContainer/components/PingPongOutput.tsx
index 9f9471c..5e20a77 100644
--- a/src/components/OutputContainer/components/PingPongOutput.tsx
+++ b/src/components/OutputContainer/components/PingPongOutput.tsx
@@ -1,12 +1,27 @@
-import { ContractAddress } from 'components/ContractAddress';
-import { Label } from 'components/Label';
-import { SignedTransactionType } from 'lib';
+import { Label } from 'components';
+import { contractAddress } from 'config';
+import { useStore } from 'hooks';
+import {
+ ACCOUNTS_ENDPOINT,
+ getExplorerLink,
+ MvxDataWithExplorerLink,
+ networkSelector,
+ SignedTransactionType
+} from 'lib';
+
import { TransactionsOutput } from './TransactionsOutput';
+// prettier-ignore
+const styles = {
+ pingPongAddressContainer: 'ping-pong-address-container flex justify-between mb-4',
+ pingPongButtons: 'ping-pong-buttons flex gap-3',
+ timeRemaining: 'time-remaining text-red-600'
+} satisfies Record;
+
type PingPongOutputType = {
timeRemaining: string;
pongAllowed: boolean;
- transactions?: SignedTransactionType[];
+ transactions?: SignedTransactionType[] | null;
};
export const PingPongOutput = ({
@@ -14,18 +29,34 @@ export const PingPongOutput = ({
pongAllowed,
transactions
}: PingPongOutputType) => {
- if (!transactions) {
+ const store = useStore();
+ const network = networkSelector(store());
+
+ if (!transactions || transactions?.length === 0) {
return null;
}
+ const explorerAddress = network.explorerAddress;
+ const explorerLink = getExplorerLink({
+ to: `/${ACCOUNTS_ENDPOINT}/${contractAddress}`,
+ explorerAddress
+ });
+
return (
<>
-
+
+
+
{!pongAllowed && (
Time remaining:
- {timeRemaining} until able to pong
+ {timeRemaining} until able
+ to pong
)}
>
diff --git a/src/components/OutputContainer/components/TransactionOutput.tsx b/src/components/OutputContainer/components/TransactionOutput.tsx
index 2596995..e7aa937 100644
--- a/src/components/OutputContainer/components/TransactionOutput.tsx
+++ b/src/components/OutputContainer/components/TransactionOutput.tsx
@@ -1,50 +1,90 @@
-import { Label } from 'components/Label';
-import { ExplorerLink, FormatAmount } from 'lib';
+import { Label } from 'components';
+import { useStore } from 'hooks';
import {
ACCOUNTS_ENDPOINT,
+ DECIMALS,
+ DIGITS,
+ FormatAmount,
+ FormatAmountController,
+ getAccount,
+ getExplorerLink,
getState,
+ MvxDataWithExplorerLink,
networkSelector,
SignedTransactionType,
TRANSACTIONS_ENDPOINT
} from 'lib';
+// prettier-ignore
+const styles = {
+ transactionContainer: 'transaction-container flex flex-col',
+ transactionElementContainer: 'transaction-elem-container flex gap-2',
+ transactionElement: 'transaction-elem flex justify-between w-full',
+ buttons: 'buttons flex gap-3',
+ dataContainer: 'data-container whitespace-nowrap'
+} satisfies Record;
+
export const TransactionOutput = ({
transaction
}: {
transaction: SignedTransactionType;
}) => {
+ const store = useStore();
const network = networkSelector(getState());
+ const account = getAccount(store());
+ const { isValid, valueDecimal, valueInteger, label } =
+ FormatAmountController.getData({
+ digits: DIGITS,
+ decimals: DECIMALS,
+ egldLabel: network.egldLabel,
+ input: account.balance
+ });
const decodedData = transaction.data
? Buffer.from(transaction.data, 'base64').toString('ascii')
: 'N/A';
+ const explorerAddress = network.explorerAddress;
+ const hashExplorerLink = getExplorerLink({
+ to: `/${TRANSACTIONS_ENDPOINT}/${transaction.hash}`,
+ explorerAddress
+ });
+ const receiverExplorerLink = getExplorerLink({
+ to: `/${ACCOUNTS_ENDPOINT}/${transaction.receiver}`,
+ explorerAddress
+ });
+
return (
-
-
+
+
Hash:
-
- {transaction.hash}
-
-
-
+
+
+
+
+
Receiver:
-
- {transaction.receiver}
-
-
+
+
+
Amount:
@@ -55,7 +95,7 @@ export const TransactionOutput = ({
Gas limit:
{transaction.gasLimit}
-
+
Data: {decodedData}
diff --git a/src/components/OutputContainer/components/TransactionsOutput.tsx b/src/components/OutputContainer/components/TransactionsOutput.tsx
index ee397a1..d253a73 100644
--- a/src/components/OutputContainer/components/TransactionsOutput.tsx
+++ b/src/components/OutputContainer/components/TransactionsOutput.tsx
@@ -1,13 +1,19 @@
import { SignedTransactionType } from 'lib';
+
import { TransactionOutput } from './TransactionOutput';
+// prettier-ignore
+const styles = {
+ transactionsContainer: 'transactions-container flex flex-col gap-4'
+} satisfies Record
;
+
export const TransactionsOutput = ({
transactions
}: {
transactions: SignedTransactionType[];
}) => {
return (
-
+
{transactions?.map((transaction) => {
return
;
})}
diff --git a/src/components/PingPongComponent/PingPongComponent.tsx b/src/components/PingPongComponent/PingPongComponent.tsx
new file mode 100644
index 0000000..bcff3d5
--- /dev/null
+++ b/src/components/PingPongComponent/PingPongComponent.tsx
@@ -0,0 +1,160 @@
+import { faArrowDown, faArrowUp } from '@fortawesome/free-solid-svg-icons';
+import { TokenLoginType } from '@multiversx/sdk-dapp/out/types/login.types';
+import moment from 'moment';
+
+import Fa from 'solid-fa';
+import { createEffect, createSignal, onCleanup } from 'solid-js';
+import { Label, OutputContainer, PingPongOutput } from 'components';
+import { contractAddress } from 'config';
+import { getCountdownSeconds, setTimeRemaining } from 'helpers';
+import {
+ ACCOUNTS_ENDPOINT,
+ MvxDataWithExplorerLink,
+ getPendingTransactions,
+ getStore,
+ MvxButton,
+ Transaction
+} from 'lib';
+import { ItemsIdentifiersEnum } from 'pages/Dashboard/dashboard.types';
+
+// prettier-ignore
+const styles = {
+ pingPongContainer: 'ping-pong-container flex flex-col gap-6',
+ infosContainer: 'infos-container flex flex-col gap-2',
+ addressComponent: 'address-component flex w-full justify-between',
+ timeRemaining: 'text-red-600',
+ buttonsContainer: 'buttons-container flex flex-col gap-2',
+ buttons: 'buttons flex justify-start gap-2',
+ buttonContent: 'button-content text-sm font-normal'
+} satisfies Record
;
+
+interface PingPongComponentPropsType {
+ id: ItemsIdentifiersEnum;
+ sendPingTransaction: (amount: any) => Promise;
+ sendPongTransaction: (transaction?: any) => Promise;
+ getTimeToPong: () => Promise;
+ pingAmount?: string;
+ getPingTransaction?: () => Promise;
+ getPongTransaction?: () => Promise;
+ tokenLogin?: TokenLoginType | null;
+}
+
+export const PingPongComponent = ({
+ id,
+ sendPingTransaction,
+ sendPongTransaction,
+ getTimeToPong,
+ pingAmount
+}: PingPongComponentPropsType) => {
+ const store = getStore();
+ const [transactions, setTransactions] = createSignal(
+ getPendingTransactions()
+ );
+ const unsubscribe = store.subscribe(() => {
+ setTransactions(getPendingTransactions());
+ });
+
+ onCleanup(() => unsubscribe());
+ const hasPendingTransactions = () => transactions().length > 0;
+
+ const [hasPing, setHasPing] = createSignal(true);
+ const [secondsLeft, setSecondsLeft] = createSignal(0);
+
+ const setSecondsRemaining = async () => {
+ const secondsRemaining = await getTimeToPong();
+ const { canPing, timeRemaining } = setTimeRemaining(secondsRemaining);
+
+ setHasPing(canPing);
+ if (timeRemaining !== undefined && timeRemaining >= 0) {
+ setSecondsLeft(timeRemaining);
+ }
+ };
+
+ const onSendPingTransaction = async () => {
+ await sendPingTransaction(pingAmount);
+ };
+
+ const onSendPongTransaction = async () => {
+ await sendPongTransaction();
+ };
+
+ const timeRemaining = () =>
+ moment()
+ .startOf('day')
+ .seconds(secondsLeft() ?? 0)
+ .format('mm:ss');
+
+ const pongAllowed = () => secondsLeft() === 0;
+
+ createEffect(() => {
+ getCountdownSeconds({ secondsLeft: secondsLeft(), setSecondsLeft });
+ });
+
+ createEffect(() => {
+ hasPing();
+ hasPendingTransactions();
+ setSecondsRemaining();
+ });
+
+ return (
+
+
+
Contract:
+
+
+ {!hasPendingTransactions() && (
+ <>
+
+
+ {!pongAllowed() && (
+
+ Time remaining:
+ {timeRemaining()}
+
+ until able to pong
+
+ )}
+ >
+ )}
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/PingPongComponent/index.ts b/src/components/PingPongComponent/index.ts
new file mode 100644
index 0000000..7bf86b5
--- /dev/null
+++ b/src/components/PingPongComponent/index.ts
@@ -0,0 +1 @@
+export * from './PingPongComponent';
diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx
new file mode 100644
index 0000000..b40c80e
--- /dev/null
+++ b/src/components/Tooltip/Tooltip.tsx
@@ -0,0 +1,104 @@
+import classNames from 'classnames';
+
+import { createSignal, JSX } from 'solid-js';
+
+import { IPropsWithClass, IPropsWithChildren } from 'types';
+
+interface TooltipPropsType extends IPropsWithChildren, IPropsWithClass {
+ trigger: (isTooltipVisible: boolean) => JSX.Element;
+ triggerOnClick?: boolean;
+ position?: 'top' | 'bottom';
+}
+
+// prettier-ignore
+const styles = {
+ tooltip: 'tooltip flex relative',
+ tooltipContentWrapper: 'tooltip-content-wrapper left-1/2 -translate-x-1/2 absolute z-10',
+ tooltipContentWrapperTop: 'bottom-full pb-3',
+ tooltipContentWrapperBottom: 'top-full pt-3',
+ tooltipContent: 'tooltip-content flex-row text-left text-xs gap-1 cursor-default bg-primary rounded-xl border border-secondary p-1 relative after:w-2 after:h-2 after:border after:border-secondary after:bg-primary after:absolute after:left-1/2 after:origin-center after:-translate-x-1/2 after:-rotate-45 transition-all duration-200 ease-out',
+ tooltipContentTop: 'after:-bottom-1 after:border-t-0 after:border-r-0',
+ tooltipContentBottom: 'after:-top-1 after:border-b-0 after:border-l-0',
+ tooltipTrigger: 'tooltip-trigger'
+} satisfies Record;
+
+export const Tooltip = ({
+ position = 'top',
+ triggerOnClick = false,
+ class: className,
+ children,
+ trigger
+}: TooltipPropsType) => {
+ const [isTooltipVisible, setIsTooltipVisible] = createSignal(false);
+
+ const handleTriggerClick = (event: MouseEvent) => {
+ if (!triggerOnClick) {
+ return;
+ }
+
+ event.preventDefault();
+ setIsTooltipVisible(!isTooltipVisible());
+ };
+
+ const handleBlur = (event: FocusEvent) => {
+ if (
+ !(event.currentTarget as HTMLElement)?.contains(
+ event.relatedTarget as Node
+ )
+ ) {
+ setIsTooltipVisible(false);
+ }
+ };
+
+ const handleMouseEnter = (event: MouseEvent) => {
+ if (triggerOnClick) {
+ return;
+ }
+
+ event.preventDefault();
+ setIsTooltipVisible(true);
+ };
+
+ const handleMouseLeave = (event: MouseEvent) => {
+ if (triggerOnClick) {
+ return;
+ }
+
+ event.preventDefault();
+ setIsTooltipVisible(false);
+ };
+
+ return (
+
+ );
+};
diff --git a/src/components/Tooltip/index.ts b/src/components/Tooltip/index.ts
new file mode 100644
index 0000000..7594a8f
--- /dev/null
+++ b/src/components/Tooltip/index.ts
@@ -0,0 +1 @@
+export * from './Tooltip';
diff --git a/src/components/index.ts b/src/components/index.ts
index c294228..57840f8 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -1,7 +1,11 @@
export * from './OutputContainer';
export * from './Loader';
export * from './Layout';
-export * from './Button';
-export * from './MxLink';
export * from './Label';
export * from './Card';
+export * from './Header';
+export * from './Footer';
+export * from './Tooltip';
+export * from './Logo';
+export * from './MissingNativeAuthError';
+export * from './PingPongComponent';
diff --git a/src/config/config.mainnet.ts b/src/config/config.mainnet.ts
index b77743a..bcb05a9 100644
--- a/src/config/config.mainnet.ts
+++ b/src/config/config.mainnet.ts
@@ -3,5 +3,7 @@ export * from './sharedConfig';
export const contractAddress =
'erd1qqqqqqqqqqqqqpgqtmcuh307t6kky677ernjj9ulk64zq74w9l5qxyhdn7';
export const API_URL = 'https://template-api.multiversx.com';
+export const ID_API_URL = 'https://id-api.multiversx.com';
+export const USERS_API_URL = '/users/api/v1/users/';
export const sampleAuthenticatedDomains = [API_URL];
export const environment = 'mainnet';
diff --git a/src/global.d.ts b/src/global.d.ts
new file mode 100644
index 0000000..4844dc3
--- /dev/null
+++ b/src/global.d.ts
@@ -0,0 +1,6 @@
+import { JSX } from 'solid-js';
+
+declare module '*.svg' {
+ const content: (props: { class?: string }) => JSX.Element;
+ export default content;
+}
diff --git a/src/helpers/getDetectedBrowser.ts b/src/helpers/getDetectedBrowser.ts
new file mode 100644
index 0000000..527f0bd
--- /dev/null
+++ b/src/helpers/getDetectedBrowser.ts
@@ -0,0 +1,23 @@
+import { safeWindow } from '@multiversx/sdk-dapp';
+
+import { BrowserEnum } from 'localConstants/browser.enum';
+
+export const getDetectedBrowser = () => {
+ const nav = safeWindow?.navigator;
+ const userAgent = nav?.userAgent || '';
+
+ if (/Firefox/.test(userAgent)) {
+ return BrowserEnum.Firefox;
+ }
+
+ if (nav && typeof (nav as any).brave !== 'undefined') {
+ return BrowserEnum.Brave;
+ }
+ if (userAgent.toLowerCase().includes('brave')) {
+ return BrowserEnum.Brave;
+ }
+
+ if (/Chrome/.test(userAgent)) {
+ return BrowserEnum.Chrome;
+ }
+};
diff --git a/src/hooks/index.ts b/src/hooks/index.ts
index a8ce727..1b0c61f 100644
--- a/src/hooks/index.ts
+++ b/src/hooks/index.ts
@@ -1,3 +1,4 @@
export * from './withPageTitle';
export * from './useScrollToElement';
export * from './useStore';
+export * from './transactions';
diff --git a/src/hooks/transactions/useSendPingPongTransaction.ts b/src/hooks/transactions/useSendPingPongTransaction.ts
index 435b458..9c8317a 100644
--- a/src/hooks/transactions/useSendPingPongTransaction.ts
+++ b/src/hooks/transactions/useSendPingPongTransaction.ts
@@ -29,7 +29,7 @@ const PONG_TRANSACTION_INFO = {
export const useSendPingPongTransaction = () => {
const store = useStore();
const network = networkSelector(store());
- const { address } = getAccount(store());
+ const address = getAccount()?.address;
const getSmartContractFactory = async () => {
const response = await axios.get('src/contracts/ping-pong.abi.json');
diff --git a/src/initConfig.ts b/src/initConfig.ts
index ed9a8fe..d4e4508 100644
--- a/src/initConfig.ts
+++ b/src/initConfig.ts
@@ -1,5 +1,6 @@
import { EnvironmentsEnum, InitAppType } from 'lib';
import './styles/globals.css';
+import './styles/tailwind.css';
import { walletConnectV2ProjectId } from './config';
const DEFAULT_TOAST_LIEFTIME = 5000;
@@ -19,7 +20,8 @@ export const config: InitAppType = {
},
transactionTracking: {
successfulToastLifetime: DEFAULT_TOAST_LIEFTIME
- }
+ },
+ theme: 'mvx:dark-theme'
}
// Option 2: Add providers using the config `customProviders` array
diff --git a/src/lib/sdkDapp/components/FormatAmount/FormatAmount.tsx b/src/lib/sdkDapp/components/FormatAmount/FormatAmount.tsx
index cbd8363..a13c1e6 100644
--- a/src/lib/sdkDapp/components/FormatAmount/FormatAmount.tsx
+++ b/src/lib/sdkDapp/components/FormatAmount/FormatAmount.tsx
@@ -1,35 +1,27 @@
-import { createEffect, createMemo } from 'solid-js';
-import { FormatAmountController } from 'lib';
+import { createEffect } from 'solid-js';
import { FormatAmountSDKPropsType } from 'lib/sdkDappUI/sdkDappUI.types';
-import { DECIMALS, DIGITS } from 'lib/sdkDappUtils/sdkDappUtils.constants';
import { IPropsWithClass } from 'types';
interface FormatAmountPropsType
extends Partial,
IPropsWithClass {
- egldLabel?: string;
- value: string;
+ isValid: boolean;
+ valueInteger: string;
+ valueDecimal: string;
+ label?: string;
+ decimalClass?: string;
+ labelClass?: string;
+ showLabel?: boolean;
}
export const FormatAmount = (props: FormatAmountPropsType) => {
let elementRef: Partial | undefined;
- const formatData = createMemo(() =>
- FormatAmountController.getData({
- digits: DIGITS,
- decimals: DECIMALS,
- egldLabel: props.egldLabel,
- input: props.value
- })
- );
createEffect(() => {
- const data = formatData();
-
if (!elementRef) {
return;
}
-
- Object.assign(elementRef, props, data);
+ Object.assign(elementRef, props);
});
return ;
diff --git a/src/lib/sdkDapp/components/MvxButton/MvxButton.tsx b/src/lib/sdkDapp/components/MvxButton/MvxButton.tsx
new file mode 100644
index 0000000..c81674f
--- /dev/null
+++ b/src/lib/sdkDapp/components/MvxButton/MvxButton.tsx
@@ -0,0 +1,33 @@
+import { IPropsWithClass } from 'types';
+
+interface MvxButtonPropsType extends IPropsWithClass {
+ disabled?: boolean;
+ size?: 'small' | 'large';
+ variant?: 'primary' | 'secondary' | 'neutral';
+ 'data-testid'?: string;
+ onClick?: (event: MouseEvent) => void;
+ children?: any;
+}
+
+export const MvxButton = ({
+ class: className,
+ 'data-testid': dataTestId,
+ disabled,
+ size,
+ variant,
+ onClick,
+ children
+}: MvxButtonPropsType) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/lib/sdkDapp/components/MvxButton/index.ts b/src/lib/sdkDapp/components/MvxButton/index.ts
new file mode 100644
index 0000000..5156442
--- /dev/null
+++ b/src/lib/sdkDapp/components/MvxButton/index.ts
@@ -0,0 +1 @@
+export * from './MvxButton';
diff --git a/src/lib/sdkDapp/components/MvxDataWithExplorerLink/MvxDataWithExplorerLink.tsx b/src/lib/sdkDapp/components/MvxDataWithExplorerLink/MvxDataWithExplorerLink.tsx
new file mode 100644
index 0000000..e05a2d3
--- /dev/null
+++ b/src/lib/sdkDapp/components/MvxDataWithExplorerLink/MvxDataWithExplorerLink.tsx
@@ -0,0 +1,32 @@
+import { IPropsWithClass } from 'types';
+
+interface MvxDataWithExplorerLinkPropsType extends IPropsWithClass {
+ data: string;
+ explorerLink: string;
+ withTooltip?: boolean;
+ showExplorerButton?: boolean;
+ showCopyButton?: boolean;
+ 'data-testid'?: string;
+}
+
+export const MvxDataWithExplorerLink = ({
+ data,
+ explorerLink,
+ class: className,
+ 'data-testid': dataTestId,
+ withTooltip,
+ showExplorerButton,
+ showCopyButton
+}: MvxDataWithExplorerLinkPropsType) => {
+ return (
+
+ );
+};
diff --git a/src/lib/sdkDapp/components/MvxDataWithExplorerLink/index.ts b/src/lib/sdkDapp/components/MvxDataWithExplorerLink/index.ts
new file mode 100644
index 0000000..1962698
--- /dev/null
+++ b/src/lib/sdkDapp/components/MvxDataWithExplorerLink/index.ts
@@ -0,0 +1 @@
+export * from './MvxDataWithExplorerLink';
diff --git a/src/lib/sdkDapp/components/MvxTrim/MvxTrim.tsx b/src/lib/sdkDapp/components/MvxTrim/MvxTrim.tsx
new file mode 100644
index 0000000..b431e4e
--- /dev/null
+++ b/src/lib/sdkDapp/components/MvxTrim/MvxTrim.tsx
@@ -0,0 +1,26 @@
+import { IPropsWithClass } from 'types';
+
+interface MvxTrimPropsType extends IPropsWithClass {
+ text: string;
+ shouldTrim?: boolean;
+ trimFontSize?: string;
+ 'data-testid'?: string;
+}
+
+export const MvxTrim = ({
+ text,
+ class: className,
+ 'data-testid': dataTestId,
+ shouldTrim,
+ trimFontSize
+}: MvxTrimPropsType) => {
+ return (
+
+ );
+};
diff --git a/src/lib/sdkDapp/components/MvxTrim/index.ts b/src/lib/sdkDapp/components/MvxTrim/index.ts
new file mode 100644
index 0000000..d08fb1c
--- /dev/null
+++ b/src/lib/sdkDapp/components/MvxTrim/index.ts
@@ -0,0 +1 @@
+export * from './MvxTrim';
diff --git a/src/lib/sdkDapp/components/index.tsx b/src/lib/sdkDapp/components/index.tsx
index 111ca43..c730f08 100644
--- a/src/lib/sdkDapp/components/index.tsx
+++ b/src/lib/sdkDapp/components/index.tsx
@@ -1,3 +1,6 @@
export * from './ExplorerLink';
export * from './FormatAmount';
export * from './TransactionsTable';
+export * from './MvxTrim';
+export * from './MvxDataWithExplorerLink';
+export * from './MvxButton';
diff --git a/src/lib/sdkDapp/index.ts b/src/lib/sdkDapp/index.ts
index a967065..46f5c5d 100644
--- a/src/lib/sdkDapp/index.ts
+++ b/src/lib/sdkDapp/index.ts
@@ -3,3 +3,4 @@ export * from './sdkDapp.constants';
export * from './sdkDapp.helpers';
export * from './sdkDapp.selectors';
export * from './sdkDapp.types';
+export * from './sdkDapp.hooks';
diff --git a/src/lib/sdkDapp/sdkDapp.helpers.ts b/src/lib/sdkDapp/sdkDapp.helpers.ts
index 565ff20..ddef169 100644
--- a/src/lib/sdkDapp/sdkDapp.helpers.ts
+++ b/src/lib/sdkDapp/sdkDapp.helpers.ts
@@ -11,3 +11,5 @@ export { initApp } from '@multiversx/sdk-dapp/out/methods/initApp/initApp';
export { setAxiosInterceptors } from '@multiversx/sdk-dapp/out/utils/network/setAxiosInterceptors';
export { refreshAccount } from '@multiversx/sdk-dapp/out/utils/account/refreshAccount';
export { getStore } from '@multiversx/sdk-dapp/out/store/store';
+export { trimUsernameDomain } from '@multiversx/sdk-dapp/out/utils/account/trimUsernameDomain';
+export { getExplorerLink } from '@multiversx/sdk-dapp/out/utils/transactions/getExplorerLink';
diff --git a/src/lib/sdkDapp/sdkDapp.hooks.ts b/src/lib/sdkDapp/sdkDapp.hooks.ts
new file mode 100644
index 0000000..eab4bf6
--- /dev/null
+++ b/src/lib/sdkDapp/sdkDapp.hooks.ts
@@ -0,0 +1,6 @@
+export { useGetAccount } from '@multiversx/sdk-dapp/out/react/account/useGetAccount';
+export { useGetAccountInfo } from '@multiversx/sdk-dapp/out/react/account/useGetAccountInfo';
+export { useGetIsLoggedIn } from '@multiversx/sdk-dapp/out/react/account/useGetIsLoggedIn';
+export { useGetLoginInfo } from '@multiversx/sdk-dapp/out/react/loginInfo/useGetLoginInfo';
+export { useGetNetworkConfig } from '@multiversx/sdk-dapp/out/react/network/useGetNetworkConfig';
+export { useGetPendingTransactions } from '@multiversx/sdk-dapp/out/react/transactions/useGetPendingTransactions';
diff --git a/src/localConstants/browser.enum.ts b/src/localConstants/browser.enum.ts
new file mode 100644
index 0000000..c9d7a35
--- /dev/null
+++ b/src/localConstants/browser.enum.ts
@@ -0,0 +1,5 @@
+export enum BrowserEnum {
+ Chrome = 'Chrome',
+ Firefox = 'Firefox',
+ Brave = 'Brave'
+}
diff --git a/src/localConstants/dashboard/dashboardLinks.ts b/src/localConstants/dashboard/dashboardLinks.ts
new file mode 100644
index 0000000..4609887
--- /dev/null
+++ b/src/localConstants/dashboard/dashboardLinks.ts
@@ -0,0 +1,9 @@
+export const DOCUMENTATION_LINK =
+ 'https://docs.multiversx.com/developers/tutorials/your-first-dapp/#set-up-the-dapp-template';
+
+export const REACT_LINK = 'https://react.dev/';
+
+export const SDK_DAPP_PACKAGE_LINK =
+ 'https://www.npmjs.com/package/@multiversx/sdk-dapp';
+
+export const TYPESCRIPT_LINK = 'https://www.typescriptlang.org/';
diff --git a/src/localConstants/dashboard/index.ts b/src/localConstants/dashboard/index.ts
new file mode 100644
index 0000000..d23a16e
--- /dev/null
+++ b/src/localConstants/dashboard/index.ts
@@ -0,0 +1 @@
+export * from './dashboardLinks';
diff --git a/src/localConstants/index.ts b/src/localConstants/index.ts
index a382098..38faf17 100644
--- a/src/localConstants/index.ts
+++ b/src/localConstants/index.ts
@@ -1 +1,4 @@
export * from './routes';
+export * from './dashboard';
+export * from './browser.enum';
+export * from './installExtensionsLinks';
diff --git a/src/localConstants/installExtensionsLinks.ts b/src/localConstants/installExtensionsLinks.ts
new file mode 100644
index 0000000..6719dab
--- /dev/null
+++ b/src/localConstants/installExtensionsLinks.ts
@@ -0,0 +1,17 @@
+export const CHROME_EXTENSION_LINK =
+ 'https://chrome.google.com/webstore/detail/multiversx-defi-wallet/dngmlblcodfobpdpecaadgfbcggfjfnm';
+
+export const CHROME_METAMASK_EXTENSION_LINK =
+ 'https://chromewebstore.google.com/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn?hl=en';
+
+export const FIREFOX_ADDON_LINK =
+ 'https://addons.mozilla.org/en-US/firefox/addon/multiversx-defi-wallet';
+
+export const FIREFOX_METAMASK_ADDON_LINK =
+ 'https://addons.mozilla.org/en-US/firefox/addon/ether-metamask';
+
+export const GET_LEDGER = 'https://www.ledger.com/';
+
+export const GET_XPORTAL = 'https://xportal.com/';
+
+export const WALLET_ADDRESS = 'https://devnet-wallet.multiversx.com';
diff --git a/src/pages/Dashboard/Dashboard.tsx b/src/pages/Dashboard/Dashboard.tsx
index 275192a..a5a93dc 100644
--- a/src/pages/Dashboard/Dashboard.tsx
+++ b/src/pages/Dashboard/Dashboard.tsx
@@ -1,54 +1,90 @@
+import classNames from 'classnames';
+import { createSignal } from 'solid-js';
+
import { contractAddress } from 'config';
-import { WidgetType } from 'types';
-import { Widget } from './components';
-import { Account, PingPongRaw, SignMessage, Transactions } from './widgets';
-const WIDGETS: WidgetType[] = [
- {
- title: 'Account',
- widget: Account,
- description: 'Connected account details',
- reference: 'https://docs.multiversx.com/sdk-and-tools/sdk-dapp/#account'
- },
+import { WidgetType } from 'types/widget.types';
+
+import { DashboardHeader, LeftPanel, Widget } from './components';
+
+import { ItemsIdentifiersEnum } from './dashboard.types';
+import { PingPongRaw, SignMessage, Transactions } from './widgets';
+
+// prettier-ignore
+const styles = {
+ dashboardContainer: 'dashboard-container flex w-screen min-h-screen relative border-t border-b border-secondary transition-all duration-200 ease-out',
+ mobilePanelContainer: 'mobile-panel-container fixed bottom-0 left-0 right-0 z-50 max-h-full overflow-y-auto lg:static lg:max-h-none lg:overflow-visible',
+ desktopPanelContainer: 'desktop-panel-container lg:flex',
+ dashboardContent: 'dashboard-content flex flex-col gap-6 justify-center items-center flex-1 w-full overflow-auto border-l border-secondary p-4 lg:p-6 transition-all duration-200 ease-out',
+ dashboardContentMobilePanelOpen: 'dashboard-content-mobile-panel-open opacity-20 lg:opacity-100 pointer-events-none',
+ dashboardWidgets: 'dashboard-widgets flex flex-col gap-6 w-full max-w-320'
+} satisfies Record;
+
+const dashboardWidgets: WidgetType[] = [
{
title: 'Ping & Pong (Manual)',
widget: PingPongRaw,
description:
'Smart Contract interactions using manually formulated transactions',
reference:
- 'https://docs.multiversx.com/sdk-and-tools/indices/es-index-transactions/',
- anchor: 'ping-pong-manual'
+ 'https://docs.multiversx.com/sdk-and-tools/indices/es-index-transactions/'
},
{
title: 'Sign message',
widget: SignMessage,
description: 'Message signing using the connected account',
- reference: 'https://docs.multiversx.com/sdk-and-tools/sdk-dapp/#account-1',
- anchor: 'sign-message'
+ reference: 'https://docs.multiversx.com/sdk-and-tools/sdk-dapp/#account-1'
},
{
title: 'Transactions (All)',
- widget: Transactions,
+ widget: () => ,
description: 'List transactions for the connected account',
reference:
- 'https://api.elrond.com/#/accounts/AccountController_getAccountTransactions'
+ 'https://api.multiversx.com/#/accounts/AccountController_getAccountTransactions'
},
{
title: 'Transactions (Ping & Pong)',
- widget: Transactions,
+ widget: () => (
+
+ ),
props: { receiver: contractAddress },
description: 'List transactions filtered for a given Smart Contract',
reference:
- 'https://api.elrond.com/#/accounts/AccountController_getAccountTransactions'
+ 'https://api.multiversx.com/#/accounts/AccountController_getAccountTransactions'
}
];
export const Dashboard = () => {
+ const [isMobilePanelOpen, setIsMobilePanelOpen] = createSignal(false);
+
return (
-
- {WIDGETS.map((element) => (
-
- ))}
+
+
+
+
+
+
+
+
+
+ {dashboardWidgets.map((element) => (
+
+ ))}
+
+
);
};
diff --git a/src/pages/Dashboard/components/DashboardHeader/DashboardHeader.tsx b/src/pages/Dashboard/components/DashboardHeader/DashboardHeader.tsx
new file mode 100644
index 0000000..ddea118
--- /dev/null
+++ b/src/pages/Dashboard/components/DashboardHeader/DashboardHeader.tsx
@@ -0,0 +1,34 @@
+import {
+ REACT_LINK,
+ SDK_DAPP_PACKAGE_LINK,
+ TYPESCRIPT_LINK
+} from 'localConstants';
+
+import { LinkComponent } from './components';
+
+// prettier-ignore
+const styles = {
+ dashboardHeaderContainer: 'dashboard-header-container flex flex-col p-8 lg:p-10 justify-center items-center gap-6 self-stretch',
+ dashboardHeaderTitle: 'dashboard-header-title text-primary transition-all duration-200 ease-out text-center text-3xl xs:text-5xl lg:text-6xl font-medium',
+ dashboardHeaderDescription: 'dashboard-header-description text-secondary transition-all duration-200 ease-out text-center text-base xs:text-lg lg:text-xl font-medium'
+} satisfies Record
;
+
+export const DashboardHeader = () => (
+
+);
diff --git a/src/pages/Dashboard/components/DashboardHeader/components/LinkComponent/LinkComponent.tsx b/src/pages/Dashboard/components/DashboardHeader/components/LinkComponent/LinkComponent.tsx
new file mode 100644
index 0000000..cc78887
--- /dev/null
+++ b/src/pages/Dashboard/components/DashboardHeader/components/LinkComponent/LinkComponent.tsx
@@ -0,0 +1,24 @@
+import { IPropsWithChildren } from 'types';
+
+// prettier-ignore
+const styles = {
+ linkAddress: 'link-address underline hover:text-primary transition-all duration-200'
+} satisfies Record;
+
+interface LinkComponentPropsType extends IPropsWithChildren {
+ linkAddress: string;
+}
+
+export const LinkComponent = ({
+ linkAddress,
+ children
+}: LinkComponentPropsType) => (
+
+ {children}
+
+);
diff --git a/src/pages/Dashboard/components/DashboardHeader/components/LinkComponent/index.ts b/src/pages/Dashboard/components/DashboardHeader/components/LinkComponent/index.ts
new file mode 100644
index 0000000..3f165f5
--- /dev/null
+++ b/src/pages/Dashboard/components/DashboardHeader/components/LinkComponent/index.ts
@@ -0,0 +1 @@
+export * from './LinkComponent';
diff --git a/src/pages/Dashboard/components/DashboardHeader/components/index.ts b/src/pages/Dashboard/components/DashboardHeader/components/index.ts
new file mode 100644
index 0000000..3f165f5
--- /dev/null
+++ b/src/pages/Dashboard/components/DashboardHeader/components/index.ts
@@ -0,0 +1 @@
+export * from './LinkComponent';
diff --git a/src/pages/Dashboard/components/DashboardHeader/index.ts b/src/pages/Dashboard/components/DashboardHeader/index.ts
new file mode 100644
index 0000000..c4a59a5
--- /dev/null
+++ b/src/pages/Dashboard/components/DashboardHeader/index.ts
@@ -0,0 +1 @@
+export * from './DashboardHeader';
diff --git a/src/pages/Dashboard/components/LeftPanel/LeftPanel.tsx b/src/pages/Dashboard/components/LeftPanel/LeftPanel.tsx
new file mode 100644
index 0000000..cd4847f
--- /dev/null
+++ b/src/pages/Dashboard/components/LeftPanel/LeftPanel.tsx
@@ -0,0 +1,122 @@
+import {
+ faClose,
+ faPowerOff,
+ faWallet
+} from '@fortawesome/free-solid-svg-icons';
+import { useNavigate } from '@solidjs/router';
+import classNames from 'classnames';
+
+import Fa from 'solid-fa';
+import IconExpand from 'assets/img/expand-up-down.svg';
+import { Logo } from 'components';
+import {
+ ACCOUNTS_ENDPOINT,
+ getAccount,
+ getAccountProvider,
+ getIsLoggedIn,
+ MvxDataWithExplorerLink
+} from 'lib';
+import { RouteNamesEnum } from 'localConstants';
+
+import { Account, SideMenu } from './components';
+
+// prettier-ignore
+const styles = {
+ leftPanelContainer: 'left-panel-container flex flex-col w-screen lg:w-80 gap-8 lg:gap-0 py-4 p-6 sticky lg:h-screen top-0 bg-primary lg:bg-accent transition-all duration-200 ease-out overflow-y-scroll',
+ leftPanelContainerOpen: 'left-panel-container-open rounded-t-2xl lg:rounded-t-none p-6',
+ leftPanelMobileHeader: 'left-panel-mobile-header flex lg:hidden justify-between items-center pt-2 pb-1 transition-all duration-200 ease-out',
+ leftPanelMobileHeaderIconClose: 'left-panel-mobile-header-icon-close text-link transition-all duration-200 ease-out',
+ leftPanelMobileHeaderIconOpen: 'left-panel-mobile-header-icon-open fill-primary transition-all duration-200 ease-out',
+ leftPanel: 'left-panel flex flex-col gap-4 lg:!block',
+ leftPanelHidden: 'hidden',
+ leftPanelMobileAddressSection: 'left-panel-mobile-address-section lg:hidden bg-accent transition-all duration-200 ease-out h-8 flex items-center justify-between rounded-full border border-secondary px-6 py-4',
+ leftPanelMobileAddress: 'left-panel-mobile-address flex gap-2 items-center justify-start w-[calc(100%-50px)]',
+ leftPanelMobileAddressIcon: 'left-panel-mobile-address-icon text-accent transition-all duration-200 ease-out',
+ logoutButton: 'text-center text-link hover:text-primary transition-all duration-200 ease-out cursor-pointer',
+ leftPanelComponents: 'flex flex-col gap-4 bg-accent p-6 lg:p-0 rounded-2xl transition-all duration-200 ease-out',
+ leftPanelBar: 'w-full h-0.25 bg-neutral-700 opacity-40 transition-all duration-200 ease-out',
+ trimmedAddress: 'w-full text-primary'
+} satisfies Record;
+
+interface LeftPanelPropsType {
+ isOpen: boolean;
+ setIsOpen: (isOpen: boolean) => void;
+}
+
+export const LeftPanel = ({
+ isOpen = false,
+ setIsOpen
+}: LeftPanelPropsType) => {
+ const handleOpenPanel = () => {
+ setIsOpen(!isOpen);
+ };
+
+ const address = getAccount()?.address;
+
+ const isLoggedIn = getIsLoggedIn();
+
+ const provider = getAccountProvider();
+ const navigate = useNavigate();
+
+ const handleLogout = async () => {
+ await provider.logout();
+ navigate(RouteNamesEnum.home);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {isLoggedIn && (
+
+
+
+ )}
+
+
+
+
+
+ );
+};
diff --git a/src/pages/Dashboard/components/LeftPanel/components/Account/Account.tsx b/src/pages/Dashboard/components/LeftPanel/components/Account/Account.tsx
new file mode 100644
index 0000000..40cf0ed
--- /dev/null
+++ b/src/pages/Dashboard/components/LeftPanel/components/Account/Account.tsx
@@ -0,0 +1,167 @@
+import {
+ faChevronUp,
+ faLayerGroup,
+ faWallet
+} from '@fortawesome/free-solid-svg-icons';
+import classNames from 'classnames';
+import { Fa } from 'solid-fa';
+
+import { Component, createSignal, JSX } from 'solid-js';
+import xLogo from 'assets/img/x-logo.svg';
+import { Label } from 'components';
+import { useStore } from 'hooks';
+import {
+ DECIMALS,
+ DIGITS,
+ FormatAmount,
+ FormatAmountController,
+ getAccount,
+ networkSelector,
+ MvxTrim
+} from 'lib';
+
+import { Username } from './components';
+import { getUserHerotag } from './hooks/getUserHerotag';
+
+// prettier-ignore
+const styles = {
+ connectedAccountContainer: 'connected-account flex flex-col gap-4',
+ connectedAccountHeader: 'connected-account-header flex justify-between items-center',
+ connectedAccountHeaderTitle: 'connected-account-header-title text-base transition-all duration-200 ease-out text-secondary',
+ connectedAccountHeaderIcon: 'connected-account-header-icon text-primary transition-transform duration-200 ease-out',
+ connectedAccountHeaderIconRotated: 'rotate-180',
+ connectedAccountDetails: 'connected-account-details flex flex-col',
+ connectedAccountDetailsHidden: 'hidden',
+ connectedAccountInfo: 'connected-account-info flex h-14 gap-2 items-center',
+ connectedAccountInfoIcon: 'connected-account-info-icon min-w-10 min-h-10 max-h-10 max-w-10 flex items-center justify-center text-tertiary border border-secondary rounded-lg overflow-hidden p-1.5 transition-all duration-200 ease-out',
+ connectedAccountInfoText: 'connected-account-info-text truncate flex flex-col',
+ connectedAccountInfoTextValue: 'connected-account-info-text-value text-primary transition-all duration-200 ease-out text-base',
+ connectedAccountDetailsIcon: 'connected-account-details-icon w-6 h-6',
+ connectedAccountDetailsHerotag: 'connected-account-details-herotag rounded-full',
+ connectedAccountDetailsXLogo: 'connected-account-details-xlogo fill-primary w-8 h-8 transition-all duration-200 ease-out',
+ connectedAccountDetailsTrimAddress: 'w-max'
+} satisfies Record;
+
+interface AccountDetailsType {
+ icon: JSX.Element | string;
+ label: string;
+ value: number | JSX.Element;
+}
+
+export const Account = () => {
+ const store = useStore();
+ const network = networkSelector(store());
+ const account = getAccount(store());
+ const address = getAccount()?.address;
+
+ const { isValid, valueDecimal, valueInteger, label } =
+ FormatAmountController.getData({
+ digits: DIGITS,
+ decimals: DECIMALS,
+ egldLabel: network.egldLabel,
+ input: account.balance
+ });
+
+ const [isCollapsed, setIsCollapsed] = createSignal(false);
+
+ const toggleCollapse = () => {
+ setIsCollapsed(!isCollapsed());
+ };
+
+ const [herotag, profileUrl] = getUserHerotag(address);
+ const XLogo = xLogo as unknown as Component<
+ JSX.SvgSVGAttributes
+ >;
+
+ const accountDetails: AccountDetailsType[] = [
+ {
+ icon: ,
+ label: 'Address',
+ value: (
+
+ ) as JSX.Element
+ },
+ {
+ icon: herotag() ? (
+ profileUrl ? (
+
+ ) : (
+ herotag().slice(0, 3)
+ )
+ ) : (
+ ('@' as any)
+ ),
+ label: 'Herotag',
+ value: ( ) as JSX.Element
+ },
+ {
+ icon: (
+
+ ),
+ label: 'Shard',
+ value: account.shard as number
+ },
+ {
+ icon: ,
+ label: 'Balance',
+ value: (
+
+ ) as JSX.Element
+ }
+ ];
+
+ return (
+
+
+
+
+ {accountDetails.map((accountDetail) => (
+
+
+ {accountDetail.icon as any}
+
+
+
+ {accountDetail.label}
+
+ {accountDetail.value as any}
+
+
+
+ ))}
+
+
+ );
+};
diff --git a/src/pages/Dashboard/components/LeftPanel/components/Account/components/Username.tsx b/src/pages/Dashboard/components/LeftPanel/components/Account/components/Username.tsx
new file mode 100644
index 0000000..b69a56f
--- /dev/null
+++ b/src/pages/Dashboard/components/LeftPanel/components/Account/components/Username.tsx
@@ -0,0 +1,27 @@
+import { AccountType, trimUsernameDomain } from 'lib';
+import { getUserHerotag } from '../hooks';
+
+// prettier-ignore
+const styles = {
+ usernameContainer: 'username-container flex gap-0.5',
+ herotag: 'herotag text-accent transition-all duration-200 ease-out'
+} satisfies Record;
+
+export const Username = (props: {
+ account?: AccountType | null;
+ address: string;
+}) => {
+ const { address } = props;
+
+ const [herotag] = getUserHerotag(address);
+
+ return (
+
+ {herotag() ? '@' : ''}
+
+
+ {herotag() ? trimUsernameDomain(herotag()) : 'N/A'}
+
+
+ );
+};
diff --git a/src/pages/Dashboard/widgets/Account/components/index.ts b/src/pages/Dashboard/components/LeftPanel/components/Account/components/index.ts
similarity index 100%
rename from src/pages/Dashboard/widgets/Account/components/index.ts
rename to src/pages/Dashboard/components/LeftPanel/components/Account/components/index.ts
diff --git a/src/pages/Dashboard/components/LeftPanel/components/Account/hooks/getUserHerotag.tsx b/src/pages/Dashboard/components/LeftPanel/components/Account/hooks/getUserHerotag.tsx
new file mode 100644
index 0000000..033aaa5
--- /dev/null
+++ b/src/pages/Dashboard/components/LeftPanel/components/Account/hooks/getUserHerotag.tsx
@@ -0,0 +1,39 @@
+import axios from 'axios';
+
+import { createEffect, createSignal } from 'solid-js';
+import { ID_API_URL, USERS_API_URL } from 'config/config.mainnet';
+
+export const getUserHerotag = (address: string) => {
+ const [profileUrl, setProfileUrl] = createSignal('');
+ const [herotag, setHerotag] = createSignal('');
+
+ const getUserProfileData = async (userAddress?: string) => {
+ if (!userAddress) {
+ return;
+ }
+
+ try {
+ const { data } = await axios.get(`${USERS_API_URL}${userAddress}`, {
+ baseURL: ID_API_URL
+ });
+
+ return data;
+ } catch (err) {
+ console.error('Unable to fetch profile url');
+ }
+ };
+
+ createEffect(() => {
+ if (!address) return;
+
+ const fetchUserProfileUrl = async () => {
+ const data = await getUserProfileData(address);
+ setProfileUrl(data?.profile?.url);
+ setHerotag(data?.herotag);
+ };
+
+ fetchUserProfileUrl();
+ });
+
+ return [herotag, profileUrl];
+};
diff --git a/src/pages/Dashboard/components/LeftPanel/components/Account/hooks/index.ts b/src/pages/Dashboard/components/LeftPanel/components/Account/hooks/index.ts
new file mode 100644
index 0000000..dfc360b
--- /dev/null
+++ b/src/pages/Dashboard/components/LeftPanel/components/Account/hooks/index.ts
@@ -0,0 +1 @@
+export * from './getUserHerotag';
diff --git a/src/pages/Dashboard/widgets/Account/index.ts b/src/pages/Dashboard/components/LeftPanel/components/Account/index.ts
similarity index 100%
rename from src/pages/Dashboard/widgets/Account/index.ts
rename to src/pages/Dashboard/components/LeftPanel/components/Account/index.ts
diff --git a/src/pages/Dashboard/components/LeftPanel/components/SideMenu/SideMenu.tsx b/src/pages/Dashboard/components/LeftPanel/components/SideMenu/SideMenu.tsx
new file mode 100644
index 0000000..a7d1664
--- /dev/null
+++ b/src/pages/Dashboard/components/LeftPanel/components/SideMenu/SideMenu.tsx
@@ -0,0 +1,131 @@
+import {
+ faChevronUp,
+ faFilter,
+ faPenNib,
+ faRectangleList,
+ faTableTennisPaddleBall,
+ IconDefinition
+} from '@fortawesome/free-solid-svg-icons';
+import classNames from 'classnames';
+
+import Fa from 'solid-fa';
+import { createSignal, JSX } from 'solid-js';
+import { ItemsIdentifiersEnum } from 'pages/Dashboard/dashboard.types';
+
+// prettier-ignore
+const styles = {
+ sideMenuContainer: 'side-menu-container flex flex-col gap-4',
+ sideMenuHeader: 'side-menu-header flex items-center justify-between',
+ sideMenuHeaderTitle: 'side-menu-header-title text-base transition-all duration-200 ease-out text-secondary',
+ sideMenuHeaderIcon: 'side-menu-header-icon text-primary transition-transform duration-200 ease-out',
+ sideMenuHeaderIconRotated: 'rotate-180',
+ sideMenuItems: 'side-menu-items flex flex-col transition-all duration-200 ease-out',
+ sideMenuItemsHidden: 'hidden',
+ sideMenuItem: 'side-menu-item flex items-center gap-2 p-2 cursor-pointer text-tertiary hover:text-primary hover:bg-primary hover:font-bold transition-all duration-200 ease-out fill-tertiary hover:fill-primary hover:rounded-lg',
+ sideMenuItemActive: 'side-menu-item-active text-primary bg-primary fill-primary rounded-lg font-bold transition-all duration-200 ease-out',
+ sideMenuItemTitle: 'side-menu-item-title transition-all duration-200 ease-out text-sm'
+} satisfies Record;
+
+interface SideMenuPropsType {
+ setIsOpen: (isOpen: boolean) => void;
+}
+interface MenuItemsType {
+ title: string;
+ icon?:
+ | string
+ | IconDefinition
+ | ((props: JSX.SvgSVGAttributes) => JSX.Element);
+ id: ItemsIdentifiersEnum;
+}
+
+const menuItems: MenuItemsType[] = [
+ {
+ title: 'Ping & Pong (Manual)',
+ icon: faTableTennisPaddleBall,
+ id: ItemsIdentifiersEnum.pingPongRaw
+ },
+ {
+ title: 'Sign message',
+ icon: faPenNib,
+ id: ItemsIdentifiersEnum.signMessage
+ },
+ {
+ title: 'Transactions (All)',
+ icon: faRectangleList,
+ id: ItemsIdentifiersEnum.transactionsAll
+ },
+ {
+ title: 'Transactions (Ping & Pong)',
+ icon: faFilter,
+ id: ItemsIdentifiersEnum.transactionsPingPong
+ }
+];
+
+export const SideMenu = ({ setIsOpen }: SideMenuPropsType) => {
+ const [isCollapsed, setIsCollapsed] = createSignal(false);
+ const [activeItem, setActiveItem] = createSignal(
+ ItemsIdentifiersEnum.pingPongRaw
+ );
+
+ const toggleCollapse = () => {
+ setIsCollapsed(!isCollapsed());
+ };
+
+ const handleMenuItemClick = (id: ItemsIdentifiersEnum) => {
+ setIsOpen(false);
+ const target = document.getElementById(id);
+ if (target) {
+ const y = target.getBoundingClientRect().top + window.scrollY - 250;
+ window.scrollTo({ top: y, behavior: 'smooth' });
+
+ setActiveItem(id);
+ }
+ };
+
+ const setItemIcon = (
+ icon:
+ | IconDefinition
+ | ((props: JSX.SvgSVGAttributes) => JSX.Element)
+ ) => {
+ if ('iconName' in icon) return ;
+
+ const IconComponent = icon;
+ return ;
+ };
+
+ return (
+
+ );
+};
diff --git a/src/pages/Dashboard/components/LeftPanel/components/SideMenu/index.ts b/src/pages/Dashboard/components/LeftPanel/components/SideMenu/index.ts
new file mode 100644
index 0000000..6b65bcf
--- /dev/null
+++ b/src/pages/Dashboard/components/LeftPanel/components/SideMenu/index.ts
@@ -0,0 +1 @@
+export * from './SideMenu';
diff --git a/src/pages/Dashboard/components/LeftPanel/components/index.ts b/src/pages/Dashboard/components/LeftPanel/components/index.ts
new file mode 100644
index 0000000..3ba9126
--- /dev/null
+++ b/src/pages/Dashboard/components/LeftPanel/components/index.ts
@@ -0,0 +1,2 @@
+export * from './Account';
+export * from './SideMenu';
diff --git a/src/pages/Dashboard/components/LeftPanel/index.ts b/src/pages/Dashboard/components/LeftPanel/index.ts
new file mode 100644
index 0000000..aedb0f9
--- /dev/null
+++ b/src/pages/Dashboard/components/LeftPanel/index.ts
@@ -0,0 +1 @@
+export * from './LeftPanel';
diff --git a/src/pages/Dashboard/components/Widget.tsx b/src/pages/Dashboard/components/Widget/Widget.tsx
similarity index 89%
rename from src/pages/Dashboard/components/Widget.tsx
rename to src/pages/Dashboard/components/Widget/Widget.tsx
index 0bb1199..9b7a7a9 100644
--- a/src/pages/Dashboard/components/Widget.tsx
+++ b/src/pages/Dashboard/components/Widget/Widget.tsx
@@ -1,4 +1,4 @@
-import { Card } from 'components/Card';
+import { Card } from 'components';
import { WidgetType } from 'types/widget.types';
export const Widget = ({
diff --git a/src/pages/Dashboard/components/Widget/index.ts b/src/pages/Dashboard/components/Widget/index.ts
new file mode 100644
index 0000000..b42adc8
--- /dev/null
+++ b/src/pages/Dashboard/components/Widget/index.ts
@@ -0,0 +1 @@
+export * from './Widget';
diff --git a/src/pages/Dashboard/components/index.ts b/src/pages/Dashboard/components/index.ts
index b42adc8..83da9c3 100644
--- a/src/pages/Dashboard/components/index.ts
+++ b/src/pages/Dashboard/components/index.ts
@@ -1 +1,3 @@
export * from './Widget';
+export * from './DashboardHeader';
+export * from './LeftPanel';
diff --git a/src/pages/Dashboard/dashboard.types.ts b/src/pages/Dashboard/dashboard.types.ts
new file mode 100644
index 0000000..c7abf2e
--- /dev/null
+++ b/src/pages/Dashboard/dashboard.types.ts
@@ -0,0 +1,10 @@
+export enum ItemsIdentifiersEnum {
+ pingPongRaw = 'ping-pong-raw',
+ pingPongAbi = 'ping-pong-abi',
+ pingPongService = 'ping-pong-service',
+ signMessage = 'sign-message',
+ nativeAuth = 'native-auth',
+ batchTransactions = 'batch-transactions',
+ transactionsAll = 'transactions-all',
+ transactionsPingPong = 'transactions-ping-pong'
+}
diff --git a/src/pages/Dashboard/widgets/Account/Account.tsx b/src/pages/Dashboard/widgets/Account/Account.tsx
deleted file mode 100644
index 1a8dbba..0000000
--- a/src/pages/Dashboard/widgets/Account/Account.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { createMemo, Show } from 'solid-js';
-import { Label } from 'components/Label';
-import { OutputContainer } from 'components/OutputContainer/OutputContainer';
-import { useStore } from 'hooks';
-import { getAccount, networkSelector } from 'lib';
-import { FormatAmount } from 'lib/sdkDapp/components/FormatAmount/FormatAmount';
-import { Username } from './components';
-
-export const Account = () => {
- const store = useStore();
- const network = createMemo(() => networkSelector(store()));
- const account = createMemo(() => getAccount(store()));
-
- return (
-
-
-
-
- Address:
- {account().address}
-
-
-
-
- Shard: {account().shard}
-
-
-
- Balance:
-
-
-
-
-
- );
-};
diff --git a/src/pages/Dashboard/widgets/Account/components/Username.tsx b/src/pages/Dashboard/widgets/Account/components/Username.tsx
deleted file mode 100644
index 865d4ba..0000000
--- a/src/pages/Dashboard/widgets/Account/components/Username.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Label } from 'components/Label';
-import { AccountType } from 'lib';
-
-export const Username = ({ account }: { account: AccountType | null }) => {
- if (!account) {
- return null;
- }
-
- return (
-
- Herotag:
-
- {account.username ? account.username : 'N/A'}
-
-
- );
-};
diff --git a/src/pages/Dashboard/widgets/PingPongRaw/PingPongRaw.tsx b/src/pages/Dashboard/widgets/PingPongRaw/PingPongRaw.tsx
index 43ff869..a72cc30 100644
--- a/src/pages/Dashboard/widgets/PingPongRaw/PingPongRaw.tsx
+++ b/src/pages/Dashboard/widgets/PingPongRaw/PingPongRaw.tsx
@@ -1,102 +1,25 @@
-import { faArrowUp, faArrowDown } from '@fortawesome/free-solid-svg-icons';
-import moment from 'moment';
-import Fa from 'solid-fa';
-import { createEffect, createSignal, onCleanup } from 'solid-js';
-import { Button } from 'components/Button';
-import { PingPongOutput } from 'components/OutputContainer/components';
-import { OutputContainer } from 'components/OutputContainer/OutputContainer';
-import { getCountdownSeconds, setTimeRemaining } from 'helpers';
-import { useSendPingPongTransaction } from 'hooks/transactions/useSendPingPongTransaction';
-import { getPendingTransactions, getStore } from 'lib';
-import { useGetTimeToPong, useGetPingAmount } from './hooks';
+import { PingPongComponent } from 'components';
+import { useSendPingPongTransaction } from 'hooks';
+import { ItemsIdentifiersEnum } from 'pages/Dashboard/dashboard.types';
+
+import { useGetPingAmount, useGetTimeToPong } from './hooks';
// Raw transaction are being done by directly requesting to API instead of calling the smartcontract
export const PingPongRaw = () => {
const getTimeToPong = useGetTimeToPong();
- const store = getStore();
- const [transactions, setTransactions] = createSignal(
- getPendingTransactions()
- );
- const unsubscribe = store.subscribe(() => {
- setTransactions(getPendingTransactions());
- });
- onCleanup(() => unsubscribe());
-
- const hasPendingTransactions = transactions().length > 0;
const { sendPingTransaction, sendPongTransaction } =
useSendPingPongTransaction();
- const pingAmount = useGetPingAmount();
-
- const [hasPing, setHasPing] = createSignal(true);
- const [secondsLeft, setSecondsLeft] = createSignal(0);
-
- const setSecondsRemaining = async () => {
- const secondsRemaining = await getTimeToPong();
- const { canPing, timeRemaining } = setTimeRemaining(secondsRemaining);
-
- setHasPing(canPing);
- if (timeRemaining && timeRemaining >= 0) {
- setSecondsLeft(timeRemaining);
- }
- };
-
- const onSendPingTransaction = async () => {
- await sendPingTransaction(pingAmount());
- };
- const onSendPongTransaction = async () => {
- await sendPongTransaction();
- };
-
- const timeRemaining = moment()
- .startOf('day')
- .seconds(secondsLeft() ?? 0)
- .format('mm:ss');
-
- const pongAllowed = () => secondsLeft() === 0;
-
- createEffect(() => {
- getCountdownSeconds({ secondsLeft: secondsLeft(), setSecondsLeft });
- });
-
- createEffect(() => {
- setSecondsRemaining();
- });
+ const pingAmount = useGetPingAmount();
return (
-
-
-
-
-
- Ping
-
-
-
-
- Pong
-
-
-
-
-
-
-
-
+
);
};
diff --git a/src/pages/Dashboard/widgets/PingPongRaw/hooks/useGetTimeToPong.ts b/src/pages/Dashboard/widgets/PingPongRaw/hooks/useGetTimeToPong.ts
index b593e3d..9defc94 100644
--- a/src/pages/Dashboard/widgets/PingPongRaw/hooks/useGetTimeToPong.ts
+++ b/src/pages/Dashboard/widgets/PingPongRaw/hooks/useGetTimeToPong.ts
@@ -1,13 +1,9 @@
import axios from 'axios';
import BigNumber from 'bignumber.js';
+
import { contractAddress } from 'config';
-import {
- Address,
- AddressValue,
- getAccount,
- getState,
- networkSelector
-} from 'lib';
+import { Address, getAccount, getState, networkSelector } from 'lib';
+
import { PingPongResponseType } from '../types';
const decodeTime = (data: PingPongResponseType) => {
@@ -26,11 +22,12 @@ const decodeTime = (data: PingPongResponseType) => {
export const useGetTimeToPong = () => {
const network = networkSelector(getState());
- const { address } = getAccount();
+ const address = getAccount()?.address;
const getTimeToPong = async () => {
try {
- const args = new AddressValue(new Address(address)).valueOf().hex();
+ const args = new Address(address).toHex();
+
const { data } = await axios.post(
`${network.apiAddress}/vm-values/query`,
{
diff --git a/src/pages/Dashboard/widgets/SignMessage/SignMessage.tsx b/src/pages/Dashboard/widgets/SignMessage/SignMessage.tsx
index d222957..d790248 100644
--- a/src/pages/Dashboard/widgets/SignMessage/SignMessage.tsx
+++ b/src/pages/Dashboard/widgets/SignMessage/SignMessage.tsx
@@ -1,23 +1,35 @@
-import {
- faFileSignature,
- faBroom,
- faArrowsRotate
-} from '@fortawesome/free-solid-svg-icons';
-import { Address, Message } from '@multiversx/sdk-core';
+import { faPaste } from '@fortawesome/free-solid-svg-icons';
+// import { MvxButton } from '@multiversx/sdk-dapp-ui/react';
+
import Fa from 'solid-fa';
import { createSignal } from 'solid-js';
-import { Button } from 'components/Button';
-import { OutputContainer } from 'components/OutputContainer/OutputContainer';
-import { getAccount, getAccountProvider } from 'lib';
+import { OutputContainer } from 'components';
+import { Address, getAccount, getAccountProvider, Message } from 'lib';
+import { ItemsIdentifiersEnum } from 'pages/Dashboard/dashboard.types';
+
import { SignFailure, SignSuccess } from './components';
+// prettier-ignore
+const styles = {
+ signMessageContainer: 'sign-message-container flex flex-col gap-6',
+ signMessage: 'sign-message flex flex-col gap-2',
+ signMessageLabel: 'sign-message-label text-secondary transition-all duration-200 ease-out text-sm font-normal',
+ signMessageText: 'sign-message-text text-secondary transition-all duration-200 ease-out resize-none w-full h-32 rounded-lg focus:outline-none',
+ signMessagePasteButtonContainer: 'sign-message-paste-button-container w-full flex justify-end',
+ signMessagePasteButton: 'sign-message-paste-button text-tertiary text-sm font-semibold flex items-center bg-btn-tertiary rounded-md cursor-pointer px-1 transition-all duration-200 ease-out',
+ signMessagePasteButtonText: 'sign-message-paste-button-text p-1',
+ signMessageButton: 'sign-message-button flex gap-2 items-start',
+ signButtonContent: 'sign-button-content text-sm font-normal'
+} satisfies Record;
+
export const SignMessage = () => {
const [message, setMessage] = createSignal('');
const [signedMessage, setSignedMessage] = createSignal(null);
const [state, setState] = createSignal<'pending' | 'success' | 'error'>(
'pending'
);
- const [signatrue, setSignatrue] = createSignal('');
+
+ const [signature, setSignatrue] = createSignal('');
const address = getAccount()?.address;
const provider = getAccountProvider();
@@ -27,6 +39,7 @@ export const SignMessage = () => {
address: new Address(address),
data: Buffer.from(message())
});
+
const signedMessageResult = await provider.signMessage(messageToSign);
if (!signedMessageResult?.signature) {
@@ -51,55 +64,91 @@ export const SignMessage = () => {
setState('pending');
};
+ const handlePasteClick = async () => {
+ const messageToSign = await navigator.clipboard.readText();
+
+ setMessage(messageToSign);
+ };
+
return (
-
-
+
+
+
Message
+
+ {!['success', 'error'].includes(state()) && (
+
+
+
+
{['success', 'error'].includes(state()) ? (
-
- <>
-
- {state() === 'error' ? 'Try again' : 'Clear'}
- >
-
+ <>>
) : (
-
- <>
-
- Sign
- >
-
- )}
-
-
- {!['success', 'error'].includes(state()) && (
-
+ //
Sign
+ //
+ )}
+
);
};
diff --git a/src/pages/Dashboard/widgets/SignMessage/components/SignFailure.tsx b/src/pages/Dashboard/widgets/SignMessage/components/SignFailure.tsx
index c029dc0..e4bd9a9 100644
--- a/src/pages/Dashboard/widgets/SignMessage/components/SignFailure.tsx
+++ b/src/pages/Dashboard/widgets/SignMessage/components/SignFailure.tsx
@@ -1,10 +1,19 @@
-export const SignFailure = ({ errorMessage }: { errorMessage?: string }) => {
- return (
-
-
Message could not be signed
-
- Reason: {errorMessage ?? '-'}
-
-
- );
-};
+// prettier-ignore
+const styles = {
+ signFailureContainer: 'sign-failure-container flex flex-col',
+ signFailureErrorMessage: 'sign-failure-error-message flex gap-1'
+} satisfies Record
;
+
+interface SignFailurePropsType {
+ errorMessage?: string;
+}
+
+export const SignFailure = ({ errorMessage }: SignFailurePropsType) => (
+
+
Message could not be signed
+
+
+ Reason: {errorMessage ?? '-'}
+
+
+);
diff --git a/src/pages/Dashboard/widgets/SignMessage/components/SignSuccess.tsx b/src/pages/Dashboard/widgets/SignMessage/components/SignSuccess.tsx
index aa411d0..a499c39 100644
--- a/src/pages/Dashboard/widgets/SignMessage/components/SignSuccess.tsx
+++ b/src/pages/Dashboard/widgets/SignMessage/components/SignSuccess.tsx
@@ -1,11 +1,26 @@
-import { Label } from 'components/Label';
+import { Label } from 'components';
import { Message } from 'lib';
+
import { decodeMessage } from '../helpers';
-export const SignSuccess = (props: {
+// prettier-ignore
+const styles = {
+ signSuccessContainer: 'sign-success-container flex flex-col gap-6',
+ signSuccess: 'sign-success flex flex-col w-[calc(100%-50px)]',
+ signatureContainer: 'signature-container flex flex-row w-full gap-2',
+ signatureText: 'signature-text w-full resize-none outline-none bg-transparent',
+ encodedMessageContainer: 'encoded-message-container flex flex-row w-full gap-2',
+ decodedMessageContainer: 'decoded-message-container flex flex-row w-full gap-2',
+ decodedMessageText: 'resize-none outline-none text-green-700 bg-transparent'
+} satisfies Record;
+
+interface VerifyMessagePropsType {
signedMessage: Message | null;
signature: string;
-}) => {
+ address: string;
+}
+
+export const SignSuccess = (props: VerifyMessagePropsType) => {
if (props.signedMessage == null) {
return null;
}
@@ -16,29 +31,33 @@ export const SignSuccess = (props: {
});
return (
-
-
-
+
+
+
Signature:
+
+ {/* */}
-
+
Encoded message:
+
{encodedMessage}
-