├── .env.sample ├── .eslintrc.js ├── .gitignore ├── Layout ├── Footer │ ├── FooterAccount │ │ └── index.js │ ├── FooterContact │ │ └── index.js │ ├── FooterMenu │ │ └── index.js │ ├── FooterMenuItem │ │ └── index.js │ ├── FooterTerm │ │ └── index.js │ └── index.js ├── TopAppBar │ ├── ConnectWallet │ │ └── index.js │ ├── NavBarMenu │ │ └── index.js │ ├── NavDropMenu │ │ └── index.js │ ├── ThemeButton │ │ └── index.js │ └── index.js └── index.js ├── README.md ├── actions ├── auth.js ├── loading.js ├── popUp.js └── types.js ├── components ├── Icons │ ├── DefaultImageIcon │ │ └── index.js │ ├── DiscordIcon │ │ └── index.js │ ├── GreyEyeCloseIcon │ │ └── index.js │ ├── GreyEyeIcon │ │ └── index.js │ ├── MaximizeIcon │ │ └── index.js │ ├── TelegramIcon │ │ └── index.js │ └── TwitterIcon │ │ └── index.js ├── Logo │ └── index.js ├── MagicDialog │ └── index.js ├── MagicLoading │ ├── LoadingSpinner │ │ └── index.js │ └── index.js └── UI │ ├── Buttons │ ├── ButtonLink │ │ └── index.js │ ├── ContainedButton │ │ └── index.js │ ├── LinkButton │ │ └── index.js │ └── OutlinedButton │ │ └── index.js │ ├── MagicCheckbox │ └── index.js │ ├── MagicMultiSelect │ └── index.js │ ├── MagicSelect │ └── index.js │ └── TextFields │ ├── AccountTextField │ └── index.js │ ├── MagicTextField │ └── index.js │ └── TextMaskCustom │ └── index.js ├── config └── index.js ├── containers ├── Auth │ ├── Shared │ │ └── AuthWrapper │ │ │ └── index.js │ ├── SignIn │ │ └── index.js │ └── SignUp │ │ └── index.js ├── CreateNFT │ ├── CreateForm │ │ └── index.js │ ├── PreviewCard │ │ └── index.js │ ├── UploadMedia │ │ └── index.js │ └── index.js ├── FAQ │ └── index.js ├── Home │ ├── CreatorAndCollector │ │ └── index.js │ ├── HomeHeader │ │ └── index.js │ ├── HomeJourney │ │ └── index.js │ ├── UserFeedback │ │ ├── FeedbackCard │ │ │ └── index.js │ │ └── index.js │ └── index.js ├── Marketplace │ └── index.js ├── MyAccount │ ├── EditAccount │ │ └── index.js │ ├── MyBalance │ │ └── index.js │ └── index.js ├── MyNFTs │ ├── MyAskOrders │ │ ├── OrderItem │ │ │ └── index.js │ │ └── index.js │ ├── MyAssets │ │ ├── AssetItem │ │ │ └── index.js │ │ └── index.js │ ├── MyBidOrders │ │ ├── BidItem │ │ │ └── index.js │ │ └── index.js │ ├── Shared │ │ ├── TabPanel │ │ │ └── index.js │ │ └── VerticalTabs │ │ │ └── index.js │ └── index.js ├── NFTDetail │ ├── AssetBids │ │ └── index.js │ ├── HistoricalPrices │ │ └── index.js │ ├── NFTInformation │ │ ├── InformationContent │ │ │ └── index.js │ │ └── index.js │ ├── SellerNFTs │ │ └── index.js │ └── index.js ├── PrivacyPolicy │ └── index.js ├── TermsOfService │ └── index.js └── TransactionHistory │ ├── TransactionItem │ └── index.js │ └── index.js ├── contexts └── ui-context.js ├── jsconfig.json ├── next.config.js ├── package.json ├── pages ├── _app.js ├── _document.js ├── auth │ ├── sign-in.js │ └── sign-up.js ├── create-nft.js ├── faq.js ├── index.js ├── marketplace.js ├── my-account.js ├── my-nfts.js ├── nft-details │ └── [goods].js ├── privacy-policy.js ├── terms-of-service.js └── transaction-history.js ├── parts ├── BidNFTDialog │ └── index.js ├── CancelNFTOrderDialog │ └── index.js ├── DeleteNFTDialog │ └── index.js ├── HeaderMeta │ └── index.js ├── ImageWall │ └── index.js ├── MagicConfirmDialog │ └── index.js ├── NFTCarousel │ ├── NFTCarouselItem │ │ └── index.js │ └── index.js ├── NFTList │ ├── NFTCard │ │ └── index.js │ └── index.js ├── NoData │ └── index.js ├── ProductContent │ └── index.js ├── PurchaseNFTDialog │ └── index.js ├── SearchInput │ └── index.js ├── SellAssetDialog │ └── index.js ├── SendAssetDialog │ └── index.js └── Table │ └── TableContainer │ └── index.js ├── public ├── android-chrome-144x144.png ├── android-chrome-192x192.png ├── android-chrome-256x256.png ├── android-chrome-36x36.png ├── android-chrome-384x384.png ├── android-chrome-48x48.png ├── android-chrome-512x512.png ├── android-chrome-72x72.png ├── android-chrome-96x96.png ├── apple-touch-icon-114x114-precomposed.png ├── apple-touch-icon-114x114.png ├── apple-touch-icon-120x120-precomposed.png ├── apple-touch-icon-120x120.png ├── apple-touch-icon-144x144-precomposed.png ├── apple-touch-icon-144x144.png ├── apple-touch-icon-152x152-precomposed.png ├── apple-touch-icon-152x152.png ├── apple-touch-icon-180x180-precomposed.png ├── apple-touch-icon-180x180.png ├── apple-touch-icon-57x57-precomposed.png ├── apple-touch-icon-57x57.png ├── apple-touch-icon-60x60-precomposed.png ├── apple-touch-icon-60x60.png ├── apple-touch-icon-72x72-precomposed.png ├── apple-touch-icon-72x72.png ├── apple-touch-icon-76x76-precomposed.png ├── apple-touch-icon-76x76.png ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── assets │ ├── fonts │ │ ├── CRC-BOLD.woff │ │ └── CRC-LIGHT.woff │ ├── images │ │ ├── background │ │ │ ├── auth.jpg │ │ │ ├── footer.jpg │ │ │ ├── header.jpg │ │ │ └── home-header.jpg │ │ ├── banner.png │ │ ├── black-logo.png │ │ ├── home-logo.png │ │ ├── icon │ │ │ └── image-placeholder.jpg │ │ └── white-logo.png │ ├── js │ │ └── europa-jupiter.js │ └── lotties │ │ └── green-progress.json ├── browserconfig.xml ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── mstile-144x144.png ├── mstile-150x150.png ├── mstile-310x150.png ├── mstile-310x310.png ├── safari-pinned-tab.svg └── site.webmanifest ├── reducers ├── authReducer.js ├── index.js ├── loadingReducer.js └── popUpReducer.js ├── services ├── api-cloudinary.js ├── api-jupiter.js ├── axios.js └── europa.js ├── store.js ├── styles ├── global.js ├── theme.js └── use-styles.js ├── utils ├── constants │ ├── common.js │ ├── contact.js │ ├── file-types.js │ ├── footer-menu.js │ ├── image-paths.js │ ├── links.js │ ├── messages.js │ ├── order-type.js │ ├── routes.js │ ├── social.js │ ├── text-masks.js │ ├── top-bar-menu.js │ ├── validations.js │ └── words.js ├── helpers │ ├── delay.js │ ├── generatePassphrase.js │ ├── getEllipsis.js │ ├── getJSONParse.js │ ├── getTimestamp.js │ ├── scrollToTop.js │ ├── signTransaction.js │ ├── time.js │ ├── toFixedIfNecessary.js │ └── utility.js ├── hocs │ ├── InitProvider │ │ └── index.js │ ├── PopUpProvider │ │ └── index.js │ └── ThemeProvider │ │ └── index.js └── hooks │ ├── useAuth.js │ ├── useLoading.js │ ├── useMenu.js │ └── usePopUp.js └── yarn.lock /.env.sample: -------------------------------------------------------------------------------- 1 | VERSION='1.0-rc' 2 | APPNAME='Leda' 3 | 4 | NETWORK=mainnet -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TODO: Warning: React version not specified in eslint-plugin-react settings. See https://github.com/yannickcr/eslint-plugin-react#configuration 3 | * TODO: update with the standard linting settings 4 | * By great.dolphin.ls 5 | */ 6 | 7 | module.exports = { 8 | 'parser': 'babel-eslint', 9 | 'parserOptions': { 10 | 'ecmaVersion': 2018, 11 | 'sourceType': 'module' 12 | }, 13 | 'env': { 14 | 'es6': true 15 | }, 16 | 'extends': [ 17 | 'plugin:react/recommended', 18 | 'plugin:react-hooks/recommended' 19 | ], 20 | 'rules': { 21 | 'react/display-name': 0, 22 | 'no-unused-vars': 'warn', 23 | 'react/no-unknown-property': 0, 24 | 'react/prop-types': 0, 25 | 'react/react-in-jsx-scope': 0 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env 29 | .env.local 30 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | 34 | # vercel 35 | .vercel 36 | -------------------------------------------------------------------------------- /Layout/Footer/FooterAccount/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { useSelector } from 'react-redux' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | import { 6 | Grid, 7 | Typography 8 | } from '@material-ui/core' 9 | 10 | import FooterMenuItem from '../FooterMenuItem' 11 | import LINKS from 'utils/constants/links' 12 | 13 | const useStyles = makeStyles(theme => ({ 14 | root: { 15 | margin: theme.spacing(0.5, 0) 16 | }, 17 | title: { 18 | fontWeight: 'bold', 19 | color: theme.palette.background.default, 20 | marginBottom: theme.spacing(1) 21 | } 22 | })); 23 | 24 | const FooterAccount = () => { 25 | const classes = useStyles(); 26 | const { accountRS = '' } = useSelector(state => state.auth); 27 | 28 | return ( 29 |
30 | 34 | ACCOUNT 35 | 36 | 37 | {accountRS 38 | ? ( 39 | <> 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | ) : ( 54 | <> 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | ) 63 | } 64 | 65 |
66 | ); 67 | }; 68 | 69 | export default memo(FooterAccount); -------------------------------------------------------------------------------- /Layout/Footer/FooterContact/index.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import { makeStyles } from '@material-ui/core/styles' 3 | 4 | import Logo from 'components/Logo' 5 | import TelegramIcon from 'components/Icons/TelegramIcon' 6 | import TwitterIcon from 'components/Icons/TwitterIcon' 7 | import DiscordIcon from 'components/Icons/DiscordIcon' 8 | import LinkButton from 'components/UI/Buttons/LinkButton' 9 | import { SUPPORT_EMAIL } from 'utils/constants/contact' 10 | 11 | const useStyles = makeStyles((theme) => ({ 12 | root: { 13 | display: 'flex', 14 | flexDirection: 'column', 15 | marginBottom: theme.spacing(2), 16 | }, 17 | email: { 18 | fontSize: 18, 19 | fontWeight: 600, 20 | marginTop: theme.spacing(1.5), 21 | color: theme.palette.background.default, 22 | textDecoration: 'unset', 23 | }, 24 | socialContainer: { 25 | margin: theme.spacing(2.5, 0) 26 | }, 27 | socialIcon: { 28 | marginRight: theme.spacing(2) 29 | } 30 | })); 31 | 32 | const FooterContact = () => { 33 | const classes = useStyles(); 34 | 35 | return ( 36 |
37 | 38 | 43 | {SUPPORT_EMAIL} 44 | 45 |
46 | 47 | 48 | 49 |
50 |
51 | ); 52 | }; 53 | 54 | export default memo(FooterContact); 55 | -------------------------------------------------------------------------------- /Layout/Footer/FooterMenu/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import { 5 | Grid, 6 | Typography 7 | } from '@material-ui/core' 8 | 9 | import FooterMenuItem from '../FooterMenuItem' 10 | import FOOTER_MENU from 'utils/constants/footer-menu' 11 | 12 | const useStyles = makeStyles(theme => ({ 13 | root: { 14 | margin: theme.spacing(0.5, 0) 15 | }, 16 | title: { 17 | fontWeight: 'bold', 18 | color: theme.palette.background.default, 19 | marginBottom: theme.spacing(1) 20 | } 21 | })); 22 | 23 | const FooterMenu = () => { 24 | const classes = useStyles(); 25 | 26 | return ( 27 |
28 | 32 | MENU 33 | 34 | 35 | { 36 | FOOTER_MENU.map((menuItem) => ( 37 | 38 | 39 | 40 | )) 41 | } 42 | 43 |
44 | ); 45 | }; 46 | 47 | export default memo(FooterMenu); 48 | -------------------------------------------------------------------------------- /Layout/Footer/FooterMenuItem/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useCallback } from 'react' 3 | import Typography from '@material-ui/core/Typography' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | 6 | import useMenu from 'utils/hooks/useMenu' 7 | 8 | const useStyles = makeStyles(theme => ({ 9 | item: { 10 | cursor: 'pointer', 11 | color: theme.palette.background.default, 12 | margin: theme.spacing(0.5, 0), 13 | '&:hover': { 14 | textDecoration: 'underline' 15 | } 16 | } 17 | })); 18 | 19 | const FooterMenuItem = ({ 20 | menu 21 | }) => { 22 | const classes = useStyles(); 23 | const { onMenuHandler } = useMenu(); 24 | 25 | const onNavHandler = useCallback(() => { 26 | onMenuHandler(menu) 27 | }, [menu, onMenuHandler]); 28 | 29 | return ( 30 | 35 | {menu.TITLE} 36 | 37 | ); 38 | }; 39 | 40 | export default memo(FooterMenuItem); 41 | -------------------------------------------------------------------------------- /Layout/Footer/FooterTerm/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import { 5 | Grid, 6 | Typography 7 | } from '@material-ui/core' 8 | 9 | import FooterMenuItem from '../FooterMenuItem' 10 | import LINKS from 'utils/constants/links' 11 | 12 | const useStyles = makeStyles(theme => ({ 13 | root: { 14 | margin: theme.spacing(0.5, 0) 15 | }, 16 | title: { 17 | fontWeight: 'bold', 18 | color: theme.palette.background.default, 19 | marginBottom: theme.spacing(1) 20 | } 21 | })); 22 | 23 | const FooterTerm = () => { 24 | const classes = useStyles(); 25 | 26 | return ( 27 |
28 | 32 | TERMS 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 | ); 44 | }; 45 | 46 | export default memo(FooterTerm); -------------------------------------------------------------------------------- /Layout/Footer/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import Grid from '@material-ui/core/Grid' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | import clsx from 'clsx' 6 | 7 | import FooterMenu from './FooterMenu' 8 | import FooterAccount from './FooterAccount' 9 | import FooterContact from './FooterContact' 10 | import FooterTerm from './FooterTerm' 11 | import { FOOTER_BACKGROUND_IMAGE_PATH } from 'utils/constants/image-paths' 12 | import { useCommonStyles } from 'styles/use-styles' 13 | 14 | const useStyles = makeStyles(theme => ({ 15 | root: { 16 | display: 'flex', 17 | flexDirection: 'row', 18 | justifyContent: 'center', 19 | width: '100%', 20 | backgroundImage: `url(${FOOTER_BACKGROUND_IMAGE_PATH})`, 21 | backgroundRepeat: 'no-repeat', 22 | backgroundSize: 'cover', 23 | backgroundPosition: 'center', 24 | }, 25 | container: { 26 | display: 'flex', 27 | flexDirection: 'row', 28 | justifyContent: 'space-between', 29 | color: theme.custom.palette.blue, 30 | paddingTop: theme.spacing(6), 31 | paddingBottom: theme.spacing(6), 32 | [theme.breakpoints.down('sm')]: { 33 | flexDirection: 'column', 34 | } 35 | } 36 | })); 37 | 38 | const Footer = () => { 39 | const classes = useStyles(); 40 | const commonClasses = useCommonStyles(); 41 | 42 | return ( 43 | 67 | ); 68 | }; 69 | 70 | export default memo(Footer); 71 | -------------------------------------------------------------------------------- /Layout/TopAppBar/ConnectWallet/index.js: -------------------------------------------------------------------------------- 1 | import { memo, useState } from 'react' 2 | import { Typography } from '@material-ui/core' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | 5 | import * as europaAPI from 'services/europa' 6 | import * as jupiterAPI from 'services/api-jupiter' 7 | import ContainedButton from 'components/UI/Buttons/ContainedButton' 8 | import useAuth from 'utils/hooks/useAuth' 9 | import usePopUp from 'utils/hooks/usePopUp' 10 | import MESSAGES from 'utils/constants/messages' 11 | 12 | const useStyles = makeStyles((theme) => ({ 13 | account: { 14 | fontWeight: 'bold', 15 | cursor: 'pointer', 16 | padding: theme.spacing(0, 1), 17 | color: theme.custom.palette.green, 18 | border: `2px dashed ${theme.custom.palette.green}`, 19 | }, 20 | connect: { 21 | margin: theme.spacing(0, 1) 22 | } 23 | })); 24 | 25 | const ConnectWallet = () => { 26 | const classes = useStyles() 27 | const { accountRS, logOutHandler, setLoginToken } = useAuth(); 28 | const { setPopUp } = usePopUp(); 29 | 30 | const [loading, setLoading] = useState(false); 31 | 32 | const walletHandler = async () => { 33 | if (!!accountRS) { 34 | logOutHandler() 35 | return 36 | } 37 | 38 | setLoading(true); 39 | try { 40 | const accountRS = await europaAPI.connectWallet() 41 | const response = await jupiterAPI.getAccountByAccountID(accountRS); 42 | if (!response?.accountRS) { 43 | setPopUp({ text: MESSAGES.CONNECT_WALLET_ERROR }) 44 | setLoading(false); 45 | return; 46 | } 47 | 48 | setLoginToken({ 49 | accountRS: response.accountRS, 50 | user: response, 51 | isWallet: true 52 | }); 53 | } catch (error) { 54 | console.log(error) 55 | setPopUp({ text: MESSAGES.CONNECT_WALLET_ERROR }) 56 | } 57 | setLoading(false); 58 | } 59 | 60 | return ( 61 | !!accountRS 62 | ? ( 63 | 68 | {accountRS || ''} 69 | 70 | ) : ( 71 | 76 | Connect 77 | 78 | ) 79 | ); 80 | }; 81 | 82 | export default memo(ConnectWallet); 83 | -------------------------------------------------------------------------------- /Layout/TopAppBar/NavBarMenu/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useCallback } from 'react' 3 | import { useRouter } from 'next/router' 4 | import { 5 | Button, 6 | Hidden 7 | } from '@material-ui/core' 8 | import { makeStyles } from '@material-ui/core/styles' 9 | import clsx from 'clsx'; 10 | 11 | import ContainedButton from 'components/UI/Buttons/ContainedButton' 12 | import OutlinedButton from 'components/UI/Buttons/OutlinedButton' 13 | import useMenu from 'utils/hooks/useMenu' 14 | import LINKS from 'utils/constants/links' 15 | 16 | const useStyles = makeStyles((theme) => ({ 17 | root: { 18 | display: 'flex', 19 | flexDirection: 'row', 20 | alignItems: 'center' 21 | }, 22 | item: { 23 | fontSize: 15, 24 | fontWeight: 'bold', 25 | color: theme.palette.text.primary 26 | }, 27 | login: { 28 | margin: theme.spacing(0, 1) 29 | }, 30 | selected: { 31 | color: theme.palette.primary.main 32 | } 33 | })); 34 | 35 | const NavBarMenu = () => { 36 | const classes = useStyles(); 37 | const router = useRouter() 38 | const { PROFILE_MENU_LINKS, onMenuHandler } = useMenu(); 39 | 40 | const onNavHandler = useCallback((item) => () => { 41 | onMenuHandler(item) 42 | }, [onMenuHandler]) 43 | 44 | return ( 45 | 46 |
47 | { 48 | PROFILE_MENU_LINKS.map((item, index) => { 49 | if (item.HREF === LINKS.SIGN_UP.HREF) { 50 | return ( 51 | 56 | {item.TITLE} 57 | 58 | ) 59 | } 60 | 61 | if (item.HREF === LINKS.SIGN_IN.HREF || item.HREF === LINKS.SIGN_OUT.HREF) { 62 | return ( 63 | 68 | {item.TITLE} 69 | 70 | ) 71 | } 72 | 73 | return ( 74 | 81 | ) 82 | }) 83 | } 84 |
85 |
86 | ); 87 | }; 88 | 89 | export default memo(NavBarMenu); -------------------------------------------------------------------------------- /Layout/TopAppBar/NavDropMenu/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useCallback, useState } from 'react' 3 | import { 4 | Menu, 5 | MenuItem, 6 | Hidden, 7 | IconButton 8 | } from '@material-ui/core' 9 | import { makeStyles } from '@material-ui/core/styles' 10 | import MenuIcon from '@material-ui/icons/Menu' 11 | 12 | import useMenu from 'utils/hooks/useMenu' 13 | 14 | const useStyles = makeStyles((theme) => ({ 15 | paper: { 16 | minWidth: 120, 17 | marginTop: theme.spacing(2), 18 | backgroundColor: theme.palette.background.default, 19 | border: `1px solid ${theme.custom.palette.border}`, 20 | padding: theme.spacing(1) 21 | }, 22 | menu: { 23 | width: 30, 24 | height: 30, 25 | color: theme.palette.text.primary, 26 | }, 27 | item: { 28 | borderRadius: 4, 29 | color: theme.palette.text.primary, 30 | } 31 | })); 32 | 33 | const NavDropMenu = () => { 34 | const classes = useStyles(); 35 | 36 | const { PROFILE_MENU_LINKS, onMenuHandler } = useMenu(); 37 | const [anchorEl, setAnchorEl] = useState(null); 38 | 39 | const handleClick = useCallback(event => { 40 | setAnchorEl(event.currentTarget); 41 | }, [setAnchorEl]); 42 | 43 | const handleClose = useCallback(() => { 44 | setAnchorEl(null); 45 | }, [setAnchorEl]); 46 | 47 | const itemHandler = useCallback((item) => () => { 48 | onMenuHandler(item) 49 | }, [onMenuHandler]); 50 | 51 | return ( 52 | <> 53 | 54 | 59 | 60 | 61 | 81 |
82 | { 83 | PROFILE_MENU_LINKS.map((item, index) => ( 84 | 89 | {item.TITLE} 90 | 91 | )) 92 | } 93 |
94 |
95 |
96 | 97 | ); 98 | }; 99 | 100 | export default memo(NavDropMenu); -------------------------------------------------------------------------------- /Layout/TopAppBar/ThemeButton/index.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import { Switch } from '@material-ui/core'; 3 | import { makeStyles } from '@material-ui/core/styles'; 4 | 5 | import { useDarkMode } from 'contexts/ui-context'; 6 | 7 | const useStyles = makeStyles((theme) => ({ 8 | root: { 9 | width: 40, 10 | height: 22, 11 | padding: 0, 12 | margin: theme.spacing(1), 13 | }, 14 | switchBase: { 15 | padding: 2, 16 | '&$checked': { 17 | transform: 'translateX(18px)', 18 | color: theme.custom.palette.white, 19 | '& + $track': { 20 | backgroundColor: theme.custom.palette.green, 21 | opacity: 1, 22 | border: 'none', 23 | }, 24 | }, 25 | '&$focusVisible $thumb': { 26 | color: theme.custom.palette.green, 27 | border: `6px solid ${theme.custom.palette.border}`, 28 | }, 29 | }, 30 | thumb: { 31 | width: 17, 32 | height: 17, 33 | }, 34 | track: { 35 | borderRadius: 20, 36 | backgroundColor: theme.palette.background.default, 37 | opacity: 1, 38 | transition: theme.transitions.create(['background-color', 'border']), 39 | '&:before, &:after': { 40 | content: '""', 41 | position: 'absolute', 42 | top: '50%', 43 | transform: 'translateY(-50%)', 44 | width: 16, 45 | height: 16, 46 | }, 47 | '&:before': { 48 | background: `url('data:image/svg+xml;utf8,') center center no-repeat`, 51 | left: 20, 52 | }, 53 | '&:after': { 54 | background: `url('data:image/svg+xml;utf8,') center center no-repeat`, 55 | right: 20, 56 | }, 57 | }, 58 | checked: {}, 59 | focusVisible: {}, 60 | })); 61 | 62 | const ThemeButton = () => { 63 | const classes = useStyles(); 64 | const { darkMode, setDarkMode } = useDarkMode(); 65 | 66 | const themeHandler = () => { 67 | setDarkMode(prev => !prev) 68 | }; 69 | 70 | return ( 71 | 84 | ); 85 | }; 86 | 87 | export default memo(ThemeButton); 88 | -------------------------------------------------------------------------------- /Layout/TopAppBar/index.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import { AppBar, Toolbar } from '@material-ui/core' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import clsx from 'clsx' 5 | 6 | import Logo from 'components/Logo' 7 | import NavBarMenu from './NavBarMenu' 8 | import NavDropMenu from './NavDropMenu' 9 | import ConnectWallet from './ConnectWallet' 10 | // import ThemeButton from './ThemeButton' 11 | import { useCommonStyles } from 'styles/use-styles' 12 | 13 | const useStyles = makeStyles((theme) => ({ 14 | appBar: { 15 | display: 'flex', 16 | flexDirection: 'row', 17 | justifyContent: 'center', 18 | boxShadow: 'none', 19 | width: '100%', 20 | height: theme.custom.layout.topAppBarHeight, 21 | backgroundColor: theme.palette.background.default 22 | }, 23 | toolBar: { 24 | display: 'flex', 25 | justifyContent: 'space-between', 26 | }, 27 | container: { 28 | display: 'flex', 29 | alignItems: 'center', 30 | } 31 | })); 32 | 33 | const TopAppBar = () => { 34 | const classes = useStyles(); 35 | const commonClasses = useCommonStyles(); 36 | 37 | return ( 38 | 42 | 43 | 44 |
45 | 46 | 47 | 48 | {/* */} 49 |
50 |
51 |
52 | ); 53 | }; 54 | 55 | export default memo(TopAppBar); 56 | -------------------------------------------------------------------------------- /Layout/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { useSelector } from 'react-redux' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | 6 | import MagicLoading from 'components/MagicLoading' 7 | import TopAppBar from './TopAppBar' 8 | import Footer from './Footer' 9 | 10 | const useStyles = makeStyles(() => ({ 11 | root: { 12 | display: 'flex', 13 | flexDirection: 'column', 14 | minHeight: '100vh', 15 | position: 'relative' 16 | }, 17 | container: { 18 | flex: '1 0 auto' 19 | }, 20 | })); 21 | 22 | const Layout = ({ 23 | isFooter = true, 24 | children 25 | }) => { 26 | const classes = useStyles(); 27 | const { loadingStatus } = useSelector(state => state.loading); 28 | 29 | return ( 30 |
31 | {loadingStatus && } 32 | 33 |
34 | {children} 35 |
36 | {isFooter &&
} 37 |
38 | ); 39 | }; 40 | 41 | export default memo(Layout); 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

Leda - Jupiter NFT Market 🎣

3 | 4 |

Allows you to sell and buy Jupiter NFT Token

5 |
6 | 7 | --- 8 | 9 | ## Summary 10 | 11 | This project is the frontend project for Leda and a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 12 | 13 | ### Local http://localhost:3000 14 | ### Development https://jupiter-frontend.vercel.app/ 15 | 16 | --- 17 | 18 | ## Installation 19 | 20 | ```bash 21 | yarn install 22 | yarn dev 23 | yarn build 24 | yarn start 25 | yarn deploy:vercel 26 | ``` 27 | --- 28 | ## Development stacks 29 | 30 | - React.js(NEXT.js) + Material-UI 31 | --- 32 | 33 | ## Pages 34 | 35 | ### Home Page 36 | 37 | - /home 38 | ### Marketplace Page 39 | 40 | - /marketplace 41 | 42 | ### Create NFT Page 43 | 44 | - /create-collect 45 | --- 46 | ## Contributors ✨ 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |

Great Dolphin

📖

Steven Grove

🤔

Steven House

💬
57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /actions/auth.js: -------------------------------------------------------------------------------- 1 | 2 | import Router from 'next/router' 3 | 4 | import LINKS from 'utils/constants/links' 5 | import * as TYPES from './types' 6 | 7 | const setUserToken = ({ accountRS, user, isWallet = false }) => dispatch => { 8 | dispatch(setAccountRS(accountRS)); 9 | dispatch(setCurrentUser(user)); 10 | dispatch(setIsWallet(isWallet)); 11 | Router.push(LINKS.MARKETPLACE.HREF); 12 | }; 13 | 14 | const setAccountRS = accountRS => { 15 | localStorage.setItem('accountRS', accountRS); 16 | return { 17 | type: TYPES.SET_ACCOUNT_RS, 18 | payload: accountRS 19 | }; 20 | }; 21 | 22 | const setCurrentUser = currentUser => { 23 | localStorage.setItem('currentUser', JSON.stringify(currentUser)); 24 | return { 25 | type: TYPES.SET_CURRENT_USER, 26 | payload: currentUser 27 | }; 28 | }; 29 | 30 | const setIsWallet = isWallet => { 31 | localStorage.setItem('isWallet', isWallet); 32 | return { 33 | type: TYPES.SET_IS_WALLET, 34 | payload: isWallet 35 | }; 36 | }; 37 | 38 | const logoutUser = () => dispatch => { 39 | localStorage.clear(); 40 | dispatch(setAccountRS('')); 41 | dispatch(setCurrentUser({})); 42 | dispatch(setIsWallet(false)); 43 | Router.push(LINKS.HOME.HREF); 44 | }; 45 | 46 | export { 47 | setUserToken, 48 | setAccountRS, 49 | setCurrentUser, 50 | setIsWallet, 51 | logoutUser 52 | } -------------------------------------------------------------------------------- /actions/loading.js: -------------------------------------------------------------------------------- 1 | 2 | import * as TYPES from './types' 3 | 4 | const setLoadingStatus = loadingStatus => dispatch => { 5 | dispatch({ 6 | type: TYPES.SET_LOADING_STATUS, 7 | payload: loadingStatus 8 | }); 9 | }; 10 | 11 | export default setLoadingStatus; -------------------------------------------------------------------------------- /actions/popUp.js: -------------------------------------------------------------------------------- 1 | import * as TYPES from './types' 2 | 3 | const setPopUpInfo = (data) => (dispatch) => { 4 | dispatch({ 5 | type: TYPES.SET_POP_UP_INFO, 6 | payload: data 7 | }) 8 | } 9 | 10 | const openPopUp = () => (dispatch) => { 11 | dispatch({ type: TYPES.OPEN_POP_UP }) 12 | } 13 | 14 | const closePopUp = () => (dispatch) => { 15 | dispatch({ type: TYPES.CLOSE_POP_UP }) 16 | } 17 | 18 | export { 19 | setPopUpInfo, 20 | openPopUp, 21 | closePopUp 22 | } 23 | -------------------------------------------------------------------------------- /actions/types.js: -------------------------------------------------------------------------------- 1 | 2 | const SET_LOADING_STATUS = 'SET_LOADING_STATUS' 3 | const SET_CURRENT_USER = 'SET_CURRENT_USER' 4 | const SET_ACCOUNT_RS = 'SET_ACCOUNT_RS' 5 | const SET_IS_WALLET = 'SET_IS_WALLET' 6 | 7 | // PopUp 8 | const SET_POP_UP_INFO = 'SET_POP_UP_INFO' 9 | const OPEN_POP_UP = 'OPEN_POP_UP' 10 | const CLOSE_POP_UP = 'CLOSE_POP_UP' 11 | 12 | export { 13 | SET_LOADING_STATUS, 14 | SET_CURRENT_USER, 15 | SET_ACCOUNT_RS, 16 | SET_IS_WALLET, 17 | SET_POP_UP_INFO, 18 | OPEN_POP_UP, 19 | CLOSE_POP_UP 20 | }; 21 | -------------------------------------------------------------------------------- /components/Icons/DefaultImageIcon/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import SvgIcon from '@material-ui/core/SvgIcon' 5 | import clsx from 'clsx' 6 | 7 | const useStyles = makeStyles(() => ({ 8 | root: { 9 | width: 131, 10 | height: 65 11 | } 12 | })); 13 | 14 | const DefaultImageIcon = ({ 15 | className, 16 | viewBox, 17 | ...rest 18 | }) => { 19 | const classes = useStyles(); 20 | 21 | return ( 22 | 23 | 24 | 25 | 29 | 32 | 33 | 34 | 35 | ) 36 | } 37 | 38 | export default memo(DefaultImageIcon); 39 | -------------------------------------------------------------------------------- /components/Icons/DiscordIcon/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import Link from 'next/link' 4 | import SvgIcon from '@material-ui/core/SvgIcon' 5 | import { makeStyles } from '@material-ui/core/styles' 6 | import clsx from 'clsx' 7 | 8 | import SOCIALS from 'utils/constants/social' 9 | 10 | const useStyles = makeStyles(() => ({ 11 | root: { 12 | width: 24, 13 | height: 24 14 | } 15 | })); 16 | 17 | const DiscordIcon = ({ 18 | className, 19 | viewBox, 20 | ...rest 21 | }) => { 22 | const classes = useStyles(); 23 | 24 | return ( 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ) 33 | } 34 | 35 | export default memo(DiscordIcon); -------------------------------------------------------------------------------- /components/Icons/GreyEyeCloseIcon/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import SvgIcon from '@material-ui/core/SvgIcon' 5 | import clsx from 'clsx' 6 | 7 | const useStyles = makeStyles(theme => ({ 8 | root: { 9 | width: theme.spacing(3) 10 | } 11 | })); 12 | 13 | const GreyEyeCloseIcon = ({ 14 | className, 15 | viewBox, 16 | ...rest 17 | }) => { 18 | const classes = useStyles(); 19 | 20 | return ( 21 | 22 | 23 | 32 | 33 | 34 | ) 35 | } 36 | 37 | export default memo(GreyEyeCloseIcon); 38 | -------------------------------------------------------------------------------- /components/Icons/GreyEyeIcon/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import SvgIcon from '@material-ui/core/SvgIcon' 5 | import clsx from 'clsx' 6 | 7 | const useStyles = makeStyles(theme => ({ 8 | root: { 9 | width: theme.spacing(3), 10 | } 11 | })); 12 | 13 | const GreyEyeIcon = ({ 14 | className, 15 | viewBox, 16 | ...rest 17 | }) => { 18 | const classes = useStyles(); 19 | 20 | return ( 21 | 22 | 23 | 24 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | ) 36 | } 37 | 38 | export default memo(GreyEyeIcon); 39 | -------------------------------------------------------------------------------- /components/Icons/MaximizeIcon/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import { 5 | IconButton, 6 | SvgIcon 7 | } from '@material-ui/core' 8 | import clsx from 'clsx' 9 | 10 | const useStyles = makeStyles(theme => ({ 11 | root: { 12 | width: 45, 13 | height: 45, 14 | border: `1px solid ${theme.palette.text.secondary}`, 15 | }, 16 | svg: { 17 | width: theme.spacing(2.5), 18 | } 19 | })); 20 | 21 | const MaximizeIcon = ({ 22 | className, 23 | viewBox, 24 | ...rest 25 | }) => { 26 | const classes = useStyles(); 27 | 28 | return ( 29 | 35 | 36 | 37 | 38 | 39 | ) 40 | } 41 | 42 | export default memo(MaximizeIcon); 43 | -------------------------------------------------------------------------------- /components/Icons/TelegramIcon/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import Link from 'next/link' 4 | import SvgIcon from '@material-ui/core/SvgIcon' 5 | import { makeStyles } from '@material-ui/core/styles' 6 | import clsx from 'clsx' 7 | 8 | import SOCIALS from 'utils/constants/social' 9 | 10 | const useStyles = makeStyles(() => ({ 11 | root: { 12 | width: 24, 13 | height: 24 14 | } 15 | })); 16 | 17 | const TelegramIcon = ({ 18 | className, 19 | viewBox, 20 | ...rest 21 | }) => { 22 | const classes = useStyles(); 23 | 24 | return ( 25 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ) 34 | } 35 | 36 | export default memo(TelegramIcon); 37 | -------------------------------------------------------------------------------- /components/Icons/TwitterIcon/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import Link from 'next/link' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | import SvgIcon from '@material-ui/core/SvgIcon' 6 | import clsx from 'clsx' 7 | 8 | import SOCIALS from 'utils/constants/social' 9 | 10 | const useStyles = makeStyles(() => ({ 11 | root: { 12 | width: 24, 13 | height: 24 14 | } 15 | })); 16 | 17 | const TwitterIcon = ({ 18 | className, 19 | viewBox, 20 | ...rest 21 | }) => { 22 | const classes = useStyles(); 23 | 24 | return ( 25 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ) 34 | } 35 | 36 | export default memo(TwitterIcon); -------------------------------------------------------------------------------- /components/Logo/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import Link from 'next/link' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | 6 | import LINKS from 'utils/constants/links' 7 | import { 8 | WHITE_LOGO_IMAGE_PATH, 9 | BLACK_LOGO_IMAGE_PATH 10 | } from 'utils/constants/image-paths' 11 | 12 | const useStyles = makeStyles(() => ({ 13 | picture: { 14 | display: 'flex', 15 | }, 16 | img: { 17 | width: 120, 18 | height: 40, 19 | objectFit: 'contain' 20 | }, 21 | })); 22 | 23 | const Logo = ({ 24 | isWhite = false, 25 | className, 26 | ...rest 27 | }) => { 28 | const classes = useStyles(); 29 | 30 | const LOGO_IMAGE_PATH = isWhite ? WHITE_LOGO_IMAGE_PATH : BLACK_LOGO_IMAGE_PATH; 31 | 32 | return ( 33 | 34 | 35 | 36 | 37 | logo 41 | 42 | 43 | 44 | ) 45 | } 46 | 47 | export default memo(Logo); 48 | -------------------------------------------------------------------------------- /components/MagicDialog/index.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import { makeStyles } from '@material-ui/core/styles' 3 | import { 4 | IconButton, 5 | Typography, 6 | Dialog, 7 | DialogTitle, 8 | DialogContent, 9 | DialogActions 10 | } from '@material-ui/core' 11 | import CloseIcon from '@material-ui/icons/Close' 12 | 13 | import ContainedButton from 'components/UI/Buttons/ContainedButton' 14 | 15 | const useStyles = makeStyles((theme) => ({ 16 | paper: { 17 | minWidth: 700, 18 | borderRadius: 10, 19 | backgroundColor: theme.palette.background.default, 20 | [theme.breakpoints.down('sm')]: { 21 | minWidth: 'unset' 22 | }, 23 | }, 24 | dialogTitle: { 25 | display: 'flex', 26 | alignItems: 'center', 27 | justifyContent: 'center', 28 | height: 68, 29 | lineHeight: 'initial', 30 | padding: theme.spacing(0, 6), 31 | background: `linear-gradient(90deg, ${theme.custom.palette.darkGreen}, ${theme.custom.palette.black})` 32 | }, 33 | title: { 34 | fontWeight: 'bold', 35 | textTransform: 'uppercase', 36 | color: theme.custom.palette.white 37 | }, 38 | closeIcon: { 39 | position: 'absolute', 40 | top: theme.spacing(1.5), 41 | right: theme.spacing(2), 42 | color: theme.custom.palette.white 43 | }, 44 | dialogContent: { 45 | width: '100%', 46 | minWidth: 600, 47 | minHeight: 130, 48 | padding: theme.spacing(4, 2), 49 | [theme.breakpoints.down('sm')]: { 50 | minWidth: 520 51 | }, 52 | [theme.breakpoints.down('xs')]: { 53 | minWidth: 'unset', 54 | padding: theme.spacing(2, 1) 55 | } 56 | }, 57 | dialogActions: { 58 | display: 'flex', 59 | justifyContent: 'center', 60 | padding: theme.spacing(0, 2, 3), 61 | [theme.breakpoints.down('xs')]: { 62 | padding: theme.spacing(0, 1.5, 1), 63 | flexDirection: 'column' 64 | } 65 | }, 66 | button: { 67 | margin: theme.spacing(0, 1), 68 | [theme.breakpoints.down('xs')]: { 69 | margin: theme.spacing(1, 0), 70 | } 71 | } 72 | })); 73 | 74 | const MagicDialog = ({ 75 | open, 76 | title, 77 | cancelLabel, 78 | confirmLabel, 79 | onCancel, 80 | onConfirm, 81 | onClose, 82 | confirmDisable = false, 83 | children 84 | }) => { 85 | const classes = useStyles(); 86 | 87 | return ( 88 | 96 | 102 | 106 | {title} 107 | 108 | 114 | 115 | 116 | 117 | 118 | {children} 119 | 120 | { 121 | (!!cancelLabel || !!confirmLabel) && 122 | 126 | { 127 | !!cancelLabel && 128 | 133 | {cancelLabel} 134 | 135 | } 136 | { 137 | !!confirmLabel && 138 | 143 | {confirmLabel} 144 | 145 | } 146 | 147 | } 148 | 149 | ); 150 | } 151 | 152 | export default memo(MagicDialog) -------------------------------------------------------------------------------- /components/MagicLoading/LoadingSpinner/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import ReactLottie from 'react-lottie' 4 | 5 | const defaultOptions = { 6 | loop: true, 7 | autoplay: true, 8 | animationData: require('public/assets/lotties/green-progress.json'), 9 | rendererSettings: { 10 | preserveAspectRatio: 'xMidYMid slice' 11 | }, 12 | }; 13 | 14 | const LoadingSpinner = ({ loading, size = 100 || size, ...rest }) => { 15 | if (!loading) 16 | return null; 17 | return ( 18 | 25 | ); 26 | }; 27 | 28 | export default memo(LoadingSpinner); -------------------------------------------------------------------------------- /components/MagicLoading/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | 5 | import LoadingSpinner from './LoadingSpinner' 6 | 7 | const useStyles = makeStyles(() => ({ 8 | root: props => ({ 9 | position: 'absolute', 10 | zIndex: 5, 11 | display: 'flex', 12 | justifyContent: 'center', 13 | alignItems: 'center', 14 | width: '100%', 15 | height: props.height ? props.height : '100%', 16 | }) 17 | })); 18 | 19 | const MagicLoading = ({ 20 | loading, 21 | height, 22 | size = 100 23 | }) => { 24 | const classes = useStyles({ height }); 25 | 26 | return ( 27 |
28 | 31 |
32 | ); 33 | }; 34 | 35 | export default memo(MagicLoading); -------------------------------------------------------------------------------- /components/UI/Buttons/ButtonLink/index.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { memo } from 'react' 3 | import Link from 'next/link' 4 | 5 | const ButtonLink = React.forwardRef(({ 6 | className, 7 | href, 8 | hrefAs, 9 | children, 10 | prefetch 11 | }, ref) => ( 12 | 17 | 18 | {children} 19 | 20 | 21 | )); 22 | 23 | export default memo(ButtonLink); 24 | -------------------------------------------------------------------------------- /components/UI/Buttons/ContainedButton/index.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import Button from '@material-ui/core/Button' 5 | import clsx from 'clsx' 6 | 7 | import ButtonLink from 'components/UI/Buttons/ButtonLink' 8 | 9 | const useStyles = makeStyles(theme => ({ 10 | root: { 11 | fontSize: 15, 12 | fontWeight: 'bold', 13 | boxShadow: 'none', 14 | padding: theme.spacing(0.25, 1.5, 0), 15 | borderRadius: 2, 16 | border: `1px solid ${theme.palette.primary.main}`, 17 | '&:hover': { 18 | color: theme.palette.primary.main 19 | } 20 | }, 21 | icon: { 22 | display: 'flex', 23 | marginRight: theme.spacing(1.5) 24 | }, 25 | disabled: { 26 | opacity: 0.6, 27 | color: `${theme.palette.primary.main} !important`, 28 | } 29 | })); 30 | 31 | const ContainedButton = React.forwardRef(({ 32 | className, 33 | classes: propClasses = {}, 34 | color = 'primary', 35 | href, 36 | loading, 37 | disabled, 38 | icon, 39 | children, 40 | ...rest 41 | }, ref) => { 42 | const classes = useStyles(); 43 | 44 | return ( 45 | 67 | ); 68 | }); 69 | 70 | export default memo(ContainedButton); 71 | -------------------------------------------------------------------------------- /components/UI/Buttons/LinkButton/index.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { memo } from 'react' 3 | import Link from 'next/link' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | import Typography from '@material-ui/core/Typography' 6 | import clsx from 'clsx' 7 | 8 | const useStyles = makeStyles(theme => ({ 9 | root: { 10 | cursor: 'pointer', 11 | fontSize: 14, 12 | fontWeight: 600, 13 | textDecoration: 'unset', 14 | color: theme.palette.primary.main, 15 | '&:hover': { 16 | textDecoration: 'underline', 17 | } 18 | } 19 | })); 20 | 21 | const LinkButton = ({ 22 | className, 23 | href, 24 | as, 25 | target = '', 26 | onClick = () => { }, 27 | children 28 | }) => { 29 | const classes = useStyles(); 30 | 31 | return href 32 | ? ( 33 | 37 | 41 | {children} 42 | 43 | 44 | ) : ( 45 | 49 | {children} 50 | 51 | ) 52 | }; 53 | 54 | export default memo(LinkButton); -------------------------------------------------------------------------------- /components/UI/Buttons/OutlinedButton/index.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import Button from '@material-ui/core/Button' 5 | import clsx from 'clsx' 6 | 7 | import ButtonLink from 'components/UI/Buttons/ButtonLink' 8 | 9 | const useStyles = makeStyles(theme => ({ 10 | root: { 11 | fontSize: 15, 12 | fontWeight: 'bold', 13 | padding: theme.spacing(0.25, 1.5, 0), 14 | borderRadius: 2, 15 | border: `1px solid ${theme.palette.primary.main}` 16 | }, 17 | icon: { 18 | display: 'flex', 19 | marginRight: theme.spacing(1.5) 20 | }, 21 | disabled: { 22 | opacity: 0.6, 23 | color: `${theme.palette.primary.main} !important`, 24 | border: `2px solid ${theme.palette.primary.main} !important`, 25 | } 26 | })); 27 | 28 | const OutlinedButton = React.forwardRef(({ 29 | className, 30 | classes: propClasses = {}, 31 | color = 'primary', 32 | href, 33 | loading, 34 | disabled, 35 | icon, 36 | children, 37 | ...rest 38 | }, ref) => { 39 | const classes = useStyles(); 40 | 41 | return ( 42 | 64 | ); 65 | }); 66 | 67 | export default memo(OutlinedButton); 68 | -------------------------------------------------------------------------------- /components/UI/MagicCheckbox/index.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import Checkbox from '@material-ui/core/Checkbox' 5 | import clsx from 'clsx' 6 | 7 | const useStyles = makeStyles(theme => ({ 8 | checkbox: { 9 | padding: 0, 10 | marginRight: theme.spacing(0.75), 11 | color: theme.custom.palette.green, 12 | '&$checked': { 13 | color: theme.custom.palette.green, 14 | }, 15 | } 16 | })); 17 | 18 | const MagicCheckbox = React.forwardRef(({ 19 | className, 20 | ...rest 21 | }, ref) => { 22 | const classes = useStyles(); 23 | 24 | return ( 25 | 34 | ); 35 | }); 36 | 37 | export default memo(MagicCheckbox); 38 | -------------------------------------------------------------------------------- /components/UI/MagicMultiSelect/index.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import { 5 | Select, 6 | MenuItem 7 | } from '@material-ui/core' 8 | 9 | import MagicTextField from 'components/UI/TextFields/MagicTextField' 10 | 11 | const useStyles = makeStyles(theme => ({ 12 | menuPaper: { 13 | backgroundColor: theme.palette.background.primary 14 | }, 15 | icon: { 16 | borderRadius: 6, 17 | marginRight: theme.spacing(1), 18 | color: theme.palette.text.primary 19 | }, 20 | placeholder: { 21 | color: theme.custom.palette.lightBlack, 22 | } 23 | })); 24 | 25 | const MagicMultiSelect = React.forwardRef(({ 26 | items = [], 27 | placeholder, 28 | label, 29 | ...rest 30 | }, ref) => { 31 | 32 | const classes = useStyles(); 33 | 34 | return ( 35 | 74 | ); 75 | }); 76 | 77 | export default memo(MagicMultiSelect); -------------------------------------------------------------------------------- /components/UI/MagicSelect/index.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import { 5 | Select, 6 | MenuItem 7 | } from '@material-ui/core' 8 | 9 | import MagicTextField from 'components/UI/TextFields/MagicTextField' 10 | 11 | const useStyles = makeStyles(theme => ({ 12 | menuPaper: { 13 | backgroundColor: theme.palette.background.primary 14 | }, 15 | icon: { 16 | borderRadius: 6, 17 | marginRight: theme.spacing(1), 18 | color: theme.palette.text.primary 19 | }, 20 | placeholder: { 21 | color: theme.custom.palette.lightBlack, 22 | } 23 | })); 24 | 25 | const MagicSelect = React.forwardRef(({ 26 | items, 27 | placeholder, 28 | label, 29 | ...rest 30 | }, ref) => { 31 | 32 | const classes = useStyles(); 33 | 34 | return ( 35 | 72 | ); 73 | }); 74 | 75 | export default memo(MagicSelect); -------------------------------------------------------------------------------- /components/UI/TextFields/AccountTextField/index.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { memo, useCallback, useMemo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import { 5 | Typography, 6 | TextField 7 | } from '@material-ui/core' 8 | import clsx from 'clsx' 9 | 10 | import TextMaskCustom from 'components/UI/TextFields/TextMaskCustom' 11 | import { isEmpty } from 'utils/helpers/utility' 12 | 13 | const useStyles = makeStyles(theme => ({ 14 | root: { 15 | width: '100%' 16 | }, 17 | textField: { 18 | width: '100%', 19 | 20 | }, 21 | inputRoot: { 22 | '&:hover:before': { 23 | border: '0 !important' 24 | }, 25 | '&::after': { 26 | border: '0 !important' 27 | }, 28 | '&::before': { 29 | border: '0 !important' 30 | } 31 | }, 32 | input: { 33 | width: '100%', 34 | fontSize: 18, 35 | padding: theme.spacing(1.5), 36 | border: `1px solid ${theme.custom.palette.border}`, 37 | borderRadius: 2, 38 | backgroundColor: theme.palette.background.primary, 39 | color: theme.custom.palette.lightBlack 40 | }, 41 | errorInput: { 42 | border: `1px solid ${theme.palette.danger.main}` 43 | }, 44 | labelContainer: { 45 | display: 'flex', 46 | justifyContent: 'space-between', 47 | alignItems: 'center', 48 | padding: theme.spacing(0, 1.5, 1) 49 | }, 50 | optional: { 51 | fontStyle: 'italic' 52 | }, 53 | error: { 54 | padding: theme.spacing(1, 1.5, 0) 55 | }, 56 | })); 57 | 58 | const AccountTextField = React.forwardRef(({ 59 | error, 60 | mask, 61 | label, 62 | isOption = false, 63 | placeholder, 64 | className, 65 | onChange, 66 | endAdornment, 67 | ...rest 68 | }, ref) => { 69 | 70 | const classes = useStyles(); 71 | 72 | const inputComponent = useMemo(() => { 73 | if (!isEmpty(mask)) { 74 | return { 75 | inputComponent: TextMaskCustom 76 | } 77 | } 78 | }, [mask]); 79 | 80 | const inputHandler = useCallback((e) => { 81 | onChange(e.target.value.toUpperCase()) 82 | }, [onChange]) 83 | 84 | return ( 85 |
86 | { 87 | !!label && 88 |
89 | 90 | {label} 91 | 92 | 93 | { 94 | isOption && 95 | 100 | (Optional) 101 | 102 | } 103 |
104 | } 105 | 106 | 126 | 127 | { 128 | !!error && 129 | 134 | {error} 135 | 136 | } 137 |
138 | ); 139 | }); 140 | 141 | export default memo(AccountTextField); -------------------------------------------------------------------------------- /components/UI/TextFields/MagicTextField/index.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { memo, useCallback, useState } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import { 5 | Typography, 6 | OutlinedInput 7 | } from '@material-ui/core' 8 | import clsx from 'clsx' 9 | 10 | import GreyEyeIcon from 'components/Icons/GreyEyeIcon' 11 | import GreyEyeCloseIcon from 'components/Icons/GreyEyeCloseIcon' 12 | 13 | const useStyles = makeStyles(theme => ({ 14 | root: { 15 | width: '100%' 16 | }, 17 | textField: { 18 | width: '100%', 19 | border: `1px solid ${theme.custom.palette.border}`, 20 | borderRadius: 2, 21 | backgroundColor: theme.palette.background.default, 22 | }, 23 | input: { 24 | fontSize: 16, 25 | fontFamily: 'roboto, sans-serif', 26 | lineHeight: 'normal', 27 | padding: theme.spacing(1), 28 | color: theme.custom.palette.lightBlack, 29 | '&:focus': { 30 | backgroundColor: 'unset' 31 | }, 32 | '&::placeholder': { 33 | lineHeight: 'normal', 34 | color: theme.palette.text.secondary 35 | }, 36 | '&:-ms-input-placeholder': { 37 | lineHeight: 'normal', 38 | color: theme.palette.text.secondary 39 | }, 40 | '&::-ms-input-placeholder': { 41 | lineHeight: 'normal', 42 | color: theme.palette.text.secondary 43 | } 44 | }, 45 | multiline: { 46 | padding: 0 47 | }, 48 | notchedOutline: { 49 | border: 'none' 50 | }, 51 | errorInput: { 52 | border: `1px solid ${theme.palette.danger.main}` 53 | }, 54 | labelContainer: { 55 | display: 'flex', 56 | justifyContent: 'space-between', 57 | alignItems: 'center', 58 | padding: theme.spacing(0, 1, 0.5) 59 | }, 60 | optional: { 61 | fontStyle: 'italic' 62 | }, 63 | error: { 64 | padding: theme.spacing(1, 1, 0) 65 | }, 66 | eyeIcon: { 67 | cursor: 'pointer' 68 | } 69 | })); 70 | 71 | const MagicTextField = React.forwardRef(({ 72 | label, 73 | type = 'text', 74 | error, 75 | isOption = false, 76 | className, 77 | ...rest 78 | }, ref) => { 79 | 80 | const classes = useStyles(); 81 | const [showPassword, setShowPassword] = useState(false); 82 | 83 | const eyeIconHandler = useCallback(() => { 84 | setShowPassword(prev => !prev) 85 | }, [setShowPassword]) 86 | 87 | return ( 88 |
89 | { 90 | !!label && 91 |
92 | 93 | {label} 94 | 95 | 96 | { 97 | isOption && 98 | 103 | (Optional) 104 | 105 | } 106 |
107 | } 108 | 109 | 122 | ) : ( 123 | 127 | ) 128 | ) 129 | } 130 | className={clsx( 131 | 'form-control form-control-lg', 132 | classes.textField 133 | )} 134 | classes={{ 135 | input: classes.input, 136 | multiline: classes.multiline, 137 | error: classes.errorInput, 138 | notchedOutline: classes.notchedOutline 139 | }} 140 | {...rest} 141 | /> 142 | { 143 | !!error && 144 | 149 | {error} 150 | 151 | } 152 |
153 | ); 154 | }); 155 | 156 | export default memo(MagicTextField); -------------------------------------------------------------------------------- /components/UI/TextFields/TextMaskCustom/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import MaskedInput from 'react-text-mask' 4 | 5 | const TextMaskCustom = ({ 6 | inputRef, 7 | ...rest 8 | }) => { 9 | return ( 10 | { 13 | inputRef(ref ? ref.inputElement : null); 14 | }} 15 | guide={false} 16 | placeholderChar={'\u005F'} 17 | showMask 18 | /> 19 | ) 20 | }; 21 | 22 | export default memo(TextMaskCustom); -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | const IS_MAINNET = process.env.NETWORK === 'mainnet' 2 | 3 | const PROXY_URL = process.env.NODE_ENV === 'production' 4 | ? 'https://leda.gojupiter.tech/' 5 | : 'http://localhost:8000' 6 | 7 | const JUPITER_URL = 'https://nodes.jup.io' 8 | 9 | const STATS_URL = 'https://stats.jup.io' 10 | 11 | const ERC_CHAIN_ID = IS_MAINNET ? 1 : 4; 12 | 13 | export { 14 | PROXY_URL, 15 | JUPITER_URL, 16 | STATS_URL, 17 | ERC_CHAIN_ID 18 | } -------------------------------------------------------------------------------- /containers/Auth/Shared/AuthWrapper/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import Paper from '@material-ui/core/Paper' 4 | import Typography from '@material-ui/core/Typography' 5 | import { makeStyles } from '@material-ui/core/styles' 6 | 7 | import Logo from 'components/Logo' 8 | import { AUTH_BACKGROUND_IMAGE_PATH } from 'utils/constants/image-paths' 9 | 10 | const useStyles = makeStyles((theme) => ({ 11 | root: { 12 | display: 'flex', 13 | alignItems: 'center', 14 | justifyContent: 'center', 15 | minHeight: `calc(100vh - ${theme.custom.layout.topAppBarHeight}px)`, 16 | backgroundImage: `url(${AUTH_BACKGROUND_IMAGE_PATH})`, 17 | backgroundRepeat: 'no-repeat', 18 | backgroundSize: 'cover', 19 | backgroundPosition: 'center', 20 | }, 21 | container: { 22 | display: 'flex', 23 | flexDirection: 'column', 24 | alignItems: 'center', 25 | width: '100%', 26 | maxWidth: 544, 27 | borderRadius: 2, 28 | margin: theme.spacing(3), 29 | padding: theme.spacing(4, 8), 30 | [theme.breakpoints.down('xs')]: { 31 | padding: theme.spacing(4, 2) 32 | } 33 | }, 34 | logo: { 35 | marginBottom: theme.spacing(2) 36 | }, 37 | welcome: { 38 | fontWeight: 'bold', 39 | marginBottom: theme.spacing(4) 40 | } 41 | })); 42 | 43 | const authPageStyles = makeStyles(theme => ({ 44 | form: { 45 | display: 'flex', 46 | flexDirection: 'column', 47 | alignItems: 'center', 48 | width: '100%' 49 | }, 50 | input: { 51 | marginBottom: theme.spacing(2.5) 52 | }, 53 | button: { 54 | margin: theme.spacing(2.5) 55 | } 56 | })); 57 | 58 | const AuthWrapper = ({ 59 | children 60 | }) => { 61 | const classes = useStyles() 62 | 63 | return ( 64 |
65 | 66 | 67 | 73 | Welcome to Leda! 74 | 75 | {children} 76 | 77 |
78 | ) 79 | } 80 | 81 | export { authPageStyles }; 82 | export default memo(AuthWrapper); -------------------------------------------------------------------------------- /containers/Auth/SignIn/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useCallback } from 'react' 3 | import { useDispatch } from 'react-redux' 4 | import { useRouter } from 'next/router' 5 | import Typography from '@material-ui/core/Typography' 6 | import { makeStyles } from '@material-ui/core/styles' 7 | import { useForm, Controller } from 'react-hook-form' 8 | import { yupResolver } from '@hookform/resolvers/yup' 9 | import * as yup from 'yup' 10 | 11 | import * as jupiterAPI from 'services/api-jupiter' 12 | import { setUserToken } from 'actions/auth' 13 | import ContainedButton from 'components/UI/Buttons/ContainedButton' 14 | import LinkButton from 'components/UI/Buttons/LinkButton' 15 | import AccountTextField from 'components/UI/TextFields/AccountTextField' 16 | import AuthWrapper, { authPageStyles } from '../Shared/AuthWrapper' 17 | import useLoading from 'utils/hooks/useLoading' 18 | import usePopUp from 'utils/hooks/usePopUp' 19 | import LINKS from 'utils/constants/links' 20 | import { ACCOUNT_VALID } from 'utils/constants/validations' 21 | import MESSAGES from 'utils/constants/messages' 22 | import TEXT_MASKS from 'utils/constants/text-masks' 23 | 24 | const useStyles = makeStyles((theme) => ({ 25 | footer: { 26 | display: 'flex' 27 | }, 28 | signup: { 29 | paddingLeft: theme.spacing(1) 30 | } 31 | })); 32 | 33 | const schema = yup.object().shape({ 34 | account: ACCOUNT_VALID 35 | }); 36 | 37 | const SignIn = () => { 38 | const dispatch = useDispatch(); 39 | const classes = useStyles(); 40 | const authClasses = authPageStyles(); 41 | const router = useRouter(); 42 | const { setPopUp } = usePopUp(); 43 | const { changeLoadingStatus } = useLoading(); 44 | 45 | const { control, handleSubmit, errors } = useForm({ 46 | resolver: yupResolver(schema) 47 | }); 48 | 49 | const onSubmit = useCallback(async (data) => { 50 | changeLoadingStatus(true) 51 | try { 52 | const response = await jupiterAPI.getAccountByAccountID(data.account); 53 | if (!response?.accountRS) { 54 | setPopUp({ text: MESSAGES.AUTH_ERROR }) 55 | changeLoadingStatus(false); 56 | return; 57 | } 58 | 59 | dispatch(setUserToken({ 60 | accountRS: response.accountRS, 61 | user: response 62 | })); 63 | setPopUp({ text: MESSAGES.SIGN_IN_SUCCESS }) 64 | router.push(LINKS.MARKETPLACE.HREF) 65 | } catch (error) { 66 | console.log(error) 67 | } 68 | changeLoadingStatus(false) 69 | }, [router, dispatch, setPopUp, changeLoadingStatus]); 70 | 71 | return ( 72 | 73 |
78 | } 80 | name='account' 81 | label='JUP Address' 82 | placeholder='JUP-____-____-____-_____' 83 | mask={TEXT_MASKS.ACCOUNT} 84 | error={errors.account?.message} 85 | className={authClasses.input} 86 | control={control} 87 | defaultValue='' 88 | /> 89 | 93 | Log In 94 | 95 | 100 | {'Don\'t have an account?'} 101 | 105 | Create New Account 106 | 107 | 108 | 109 |
110 | ) 111 | } 112 | 113 | export default memo(SignIn) -------------------------------------------------------------------------------- /containers/CreateNFT/PreviewCard/index.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import { makeStyles } from '@material-ui/core/styles' 3 | 4 | import DefaultImageIcon from 'components/Icons/DefaultImageIcon' 5 | import { FILE_TYPES } from 'utils/constants/file-types' 6 | import { IMAGE_PLACEHOLDER_IMAGE_PATH } from 'utils/constants/image-paths' 7 | 8 | const useStyles = makeStyles((theme) => ({ 9 | root: { 10 | height: 220, 11 | padding: theme.spacing(1), 12 | borderRadius: 2, 13 | border: `1px solid ${theme.custom.palette.border}`, 14 | }, 15 | defaultImage: { 16 | display: 'flex', 17 | flexDirection: 'column', 18 | justifyContent: 'center', 19 | alignItems: 'center', 20 | height: '100%', 21 | backgroundColor: theme.custom.palette.grey 22 | }, 23 | image: { 24 | height: '100%', 25 | width: '100%', 26 | objectFit: 'contain' 27 | }, 28 | })); 29 | 30 | const PreviewCard = ({ 31 | type, 32 | fileBuffer 33 | }) => { 34 | const classes = useStyles(); 35 | 36 | return ( 37 |
38 | {!!fileBuffer 39 | ? ( 40 | type === FILE_TYPES.IMAGE.VALUE 41 | ? ( 42 | image 47 | ) : ( 48 | 51 | ) 52 | ) : ( 53 |
54 | 55 |
56 | ) 57 | } 58 |
59 | ); 60 | } 61 | 62 | export default memo(PreviewCard) -------------------------------------------------------------------------------- /containers/CreateNFT/UploadMedia/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useCallback } from 'react' 3 | import { useDropzone } from 'react-dropzone' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | import { Typography } from '@material-ui/core' 6 | 7 | import ContainedButton from 'components/UI/Buttons/ContainedButton' 8 | import { MAX_UPLOAD_SIZE } from 'utils/constants/common' 9 | import usePopUp from 'utils/hooks/usePopUp' 10 | import MESSAGES from 'utils/constants/messages' 11 | 12 | const useStyles = makeStyles((theme) => ({ 13 | root: { 14 | height: '100%', 15 | padding: theme.spacing(1), 16 | borderRadius: 2, 17 | border: `1px solid ${theme.custom.palette.border}`, 18 | }, 19 | container: { 20 | display: 'flex', 21 | flexDirection: 'column', 22 | justifyContent: 'space-evenly', 23 | alignItems: 'center', 24 | height: '100%', 25 | padding: theme.spacing(3), 26 | backgroundColor: theme.custom.palette.grey 27 | }, 28 | })); 29 | 30 | const UploadMedia = ({ 31 | type, 32 | setFileBuffer 33 | }) => { 34 | const classes = useStyles(); 35 | const { setPopUp } = usePopUp(); 36 | 37 | const onDrop = useCallback(async (acceptedFiles) => { 38 | if (!Array.isArray(acceptedFiles) || acceptedFiles.length <= 0) { 39 | setPopUp({ text: MESSAGES.MAX_UPLOAD_ERROR }) 40 | return; 41 | } 42 | 43 | const file = acceptedFiles[0]; 44 | const reader = new FileReader(); 45 | reader.addEventListener('load', () => { 46 | setFileBuffer(reader.result); 47 | }); 48 | reader.readAsDataURL(file); 49 | }, [setPopUp, setFileBuffer]); 50 | 51 | const { getRootProps, getInputProps } = useDropzone({ 52 | onDrop, 53 | accept: type.ACCEPT, 54 | maxSize: MAX_UPLOAD_SIZE 55 | }) 56 | 57 | return ( 58 |
59 |
60 | 61 | 66 | {type.PLACEHOLDER} 67 | 68 | 69 | Choose file 70 | 71 |
72 |
73 | ); 74 | } 75 | 76 | export default memo(UploadMedia) -------------------------------------------------------------------------------- /containers/CreateNFT/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import { Typography } from '@material-ui/core' 5 | 6 | import ImageWall from 'parts/ImageWall' 7 | import CreateForm from './CreateForm' 8 | 9 | const useStyles = makeStyles((theme) => ({ 10 | root: { 11 | display: 'flex', 12 | flexDirection: 'column', 13 | alignItems: 'center', 14 | width: '100%', 15 | backgroundColor: theme.palette.background.default 16 | }, 17 | container: { 18 | display: 'flex', 19 | flexDirection: 'column', 20 | alignItems: 'center', 21 | width: '100%', 22 | maxWidth: 610, 23 | padding: theme.spacing(0, 2, 10), 24 | }, 25 | header: { 26 | fontSize: 18, 27 | marginBottom: theme.spacing(3) 28 | }, 29 | })); 30 | 31 | const CreateNFT = () => { 32 | const classes = useStyles(); 33 | 34 | return ( 35 |
36 | 37 |
38 | 43 | Please select your file type, upload your file and add a title for your NFT. 44 |
45 | If you{"'"}d like to attach additional info you can enter this in the 46 | description box (sub-title, your name, inspiration, whether it is part of a 47 | series or a limited edition number, etc). 48 |
49 | 50 |
51 |
52 | ) 53 | } 54 | 55 | export default memo(CreateNFT) -------------------------------------------------------------------------------- /containers/Home/CreatorAndCollector/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import { 5 | Grid, 6 | Typography 7 | } from '@material-ui/core' 8 | import CodeIcon from '@material-ui/icons/Code' 9 | import BoltIcon from '@material-ui/icons/OfflineBolt' 10 | import HeadPhoneIcon from '@material-ui/icons/Headset' 11 | import CheckIcon from '@material-ui/icons/Check' 12 | import BarIcon from '@material-ui/icons/ViewWeek' 13 | import UnLockIcon from '@material-ui/icons/LockOpen' 14 | import ShowChartIcon from '@material-ui/icons/ShowChart' 15 | import HomeIcon from '@material-ui/icons/DonutSmall' 16 | 17 | const useStyles = makeStyles(theme => ({ 18 | root: { 19 | display: 'flex', 20 | justifyContent: 'center', 21 | width: '100%', 22 | margin: theme.spacing(8, 3) 23 | }, 24 | container: { 25 | display: 'flex', 26 | flexDirection: 'column', 27 | alignItems: 'center', 28 | maxWidth: 1020, 29 | width: '100%' 30 | }, 31 | title: { 32 | fontSize: 40, 33 | fontWeight: 'bold', 34 | textAlign: 'center', 35 | marginBottom: theme.spacing(8), 36 | maxWidth: 380, 37 | [theme.breakpoints.down('sm')]: { 38 | fontSize: 32, 39 | marginBottom: theme.spacing(5), 40 | }, 41 | }, 42 | list: { 43 | width: '100%', 44 | padding: theme.spacing(2) 45 | }, 46 | item: { 47 | display: 'flex', 48 | flexDirection: 'column', 49 | alignItems: 'center', 50 | justifyContent: 'center' 51 | }, 52 | iconContainer: { 53 | position: 'relative', 54 | display: 'flex', 55 | alignItems: 'center', 56 | justifyContent: 'center', 57 | margin: theme.spacing(2) 58 | }, 59 | square: { 60 | position: 'absolute', 61 | width: 60, 62 | height: 60, 63 | borderRadius: 15, 64 | opacity: 0.2, 65 | }, 66 | text: { 67 | fontSize: 32, 68 | fontWeight: 'bold', 69 | [theme.breakpoints.down('sm')]: { 70 | fontSize: 22, 71 | }, 72 | } 73 | })); 74 | 75 | const CreatorAndCollector = () => { 76 | const classes = useStyles(); 77 | 78 | return ( 79 |
80 |
81 | 82 | Categories and Tags 83 | 84 | 85 | { 86 | CREATORS.map((item, index) => ( 87 | 88 |
89 |
90 | 91 |
92 | 93 | {item.title} 94 | 95 | 96 | {item.description} 97 | 98 | 99 | )) 100 | } 101 | 102 |
103 |
104 | ); 105 | }; 106 | 107 | export default memo(CreatorAndCollector); 108 | 109 | const CREATORS = [ 110 | { 111 | title: 'No code', 112 | description: 'Required', 113 | color: '#5dc1a3', 114 | icon: CodeIcon 115 | }, 116 | { 117 | title: 'Community', 118 | description: 'Driven', 119 | color: '#7672f6', 120 | icon: BoltIcon 121 | }, 122 | { 123 | title: 'Flexible', 124 | description: 'Royalties', 125 | color: '#ec5f67', 126 | icon: HeadPhoneIcon 127 | }, 128 | { 129 | title: 'Verified', 130 | description: 'Creators', 131 | color: '#f3be42', 132 | icon: CheckIcon 133 | }, 134 | { 135 | title: 'Multiple', 136 | description: 'Minting', 137 | color: '#ef8532', 138 | icon: BarIcon 139 | }, 140 | { 141 | title: 'Unlockable', 142 | description: 'Items', 143 | color: '#4ca9ef', 144 | icon: UnLockIcon 145 | }, 146 | { 147 | title: 'Auction', 148 | description: 'Functions', 149 | color: '#bd00ff', 150 | icon: ShowChartIcon 151 | }, 152 | { 153 | title: 'Leda', 154 | description: 'Rewards', 155 | color: '#0066ff', 156 | icon: HomeIcon 157 | }, 158 | ] -------------------------------------------------------------------------------- /containers/Home/HomeHeader/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | 5 | import { 6 | HOME_LOGO_IMAGE_PATH, 7 | HOME_HEADER_BACKGROUND_IMAGE_PATH 8 | } from 'utils/constants/image-paths' 9 | 10 | const useStyles = makeStyles(theme => ({ 11 | root: { 12 | display: 'flex', 13 | justifyContent: 'center', 14 | width: '100%', 15 | padding: theme.spacing(15, 3), 16 | minHeight: 790, 17 | backgroundImage: `url(${HOME_HEADER_BACKGROUND_IMAGE_PATH})`, 18 | backgroundRepeat: 'no-repeat', 19 | backgroundSize: 'cover', 20 | backgroundPosition: 'center', 21 | [theme.breakpoints.down('sm')]: { 22 | minHeight: 'unset', 23 | }, 24 | }, 25 | container: { 26 | width: '100%', 27 | maxWidth: 760, 28 | }, 29 | picture: { 30 | display: 'flex', 31 | }, 32 | img: { 33 | width: '100%', 34 | objectFit: 'contain' 35 | }, 36 | })); 37 | 38 | const HomeHeader = () => { 39 | const classes = useStyles(); 40 | return ( 41 |
42 |
43 | 44 | 45 | logo 49 | 50 |
51 |
52 | ); 53 | }; 54 | 55 | export default memo(HomeHeader); -------------------------------------------------------------------------------- /containers/Home/HomeJourney/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import { Typography } from '@material-ui/core' 5 | 6 | const useStyles = makeStyles(theme => ({ 7 | root: { 8 | display: 'flex', 9 | justifyContent: 'center', 10 | alignItems: 'center', 11 | width: '100%', 12 | padding: theme.spacing(10, 0) 13 | }, 14 | container: { 15 | display: 'flex', 16 | flexDirection: 'column', 17 | alignItems: 'center', 18 | width: '100%', 19 | maxWidth: 1020, 20 | padding: theme.spacing(3), 21 | }, 22 | title: { 23 | fontSize: 40, 24 | fontWeight: 'bold', 25 | fontFamily: 'CRC-LIGHT', 26 | textAlign: 'center', 27 | marginBottom: theme.spacing(4), 28 | [theme.breakpoints.down('sm')]: { 29 | fontSize: 32, 30 | }, 31 | }, 32 | description: { 33 | fontSize: 18, 34 | marginBottom: theme.spacing(4), 35 | } 36 | })); 37 | 38 | const HomeJourney = () => { 39 | const classes = useStyles(); 40 | 41 | return ( 42 |
43 |
44 | 48 | WHAT IS LEDA? 49 | 50 | 51 | Leda is an NFT marketplace, that uses the Jupiter blockchain. A Non-Fungible 52 | Token (NFT) (or called Singleton Asset Token on Jupiter) is a unit of data 53 | stored on a blockchain that certifies a digital asset to be unique and 54 | therefore not interchangeable. 55 | 56 | 57 | NFTs can be used to represent items such as photos, videos, audios, in-game 58 | items, and any other type of digital file. As an artist, by tokenizing your 59 | work, you ensure that it is unique and that the ownership is provably yours. 60 | 61 | 62 | Start your NFT journey with Leda! 63 | 64 | 65 | No code knowledge required. Easily upload your content and create your NFT 66 | with a few clicks. 67 | 68 |
69 |
70 | ); 71 | }; 72 | 73 | export default memo(HomeJourney); -------------------------------------------------------------------------------- /containers/Home/UserFeedback/FeedbackCard/index.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import { 3 | Card, 4 | CardHeader, 5 | CardContent, 6 | CardActionArea, 7 | Avatar, 8 | Typography, 9 | } from '@material-ui/core' 10 | import { makeStyles } from '@material-ui/core/styles' 11 | 12 | const useStyles = makeStyles((theme) => ({ 13 | card: { 14 | margin: theme.spacing(1.5), 15 | borderRadius: 10, 16 | }, 17 | content: { 18 | height: 205 19 | }, 20 | description: { 21 | fontSize: 18 22 | }, 23 | action: { 24 | display: 'flex', 25 | flexDirection: 'column', 26 | alignItems: 'flex-start', 27 | padding: theme.spacing(2) 28 | }, 29 | name: { 30 | fontWeight: 'bold' 31 | } 32 | })); 33 | 34 | const FeedbackCard = ({ 35 | item 36 | }) => { 37 | const classes = useStyles(); 38 | 39 | return ( 40 | 41 | 44 | } 45 | /> 46 | 47 | 52 | {item.description} 53 | 54 | 55 | 56 | 61 | {item.name} 62 | 63 | 67 | {item.subName} 68 | 69 | 70 | 71 | ); 72 | } 73 | 74 | export default memo(FeedbackCard) -------------------------------------------------------------------------------- /containers/Home/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | 5 | import HomeHeader from './HomeHeader' 6 | // import CreatorAndCollector from './CreatorAndCollector' 7 | // import UserFeedback from './UserFeedback' 8 | import NFTCarousel from 'parts/NFTCarousel' 9 | import HomeJourney from './HomeJourney' 10 | 11 | const useStyles = makeStyles((theme) => ({ 12 | root: { 13 | display: 'flex', 14 | flexDirection: 'column', 15 | alignItems: 'center', 16 | width: '100%', 17 | backgroundColor: theme.palette.background.default, 18 | marginBottom: theme.spacing(15) 19 | } 20 | })); 21 | 22 | const Home = () => { 23 | const classes = useStyles(); 24 | 25 | return ( 26 |
27 | 28 | 29 | {/* */} 30 | {/* */} 31 | 32 |
33 | ) 34 | } 35 | 36 | export default memo(Home) -------------------------------------------------------------------------------- /containers/Marketplace/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useState, useEffect, useCallback } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import Router, { useRouter } from 'next/router' 5 | 6 | import * as jupiterAPI from 'services/api-jupiter' 7 | import ImageWall from 'parts/ImageWall' 8 | import SearchInput from 'parts/SearchInput' 9 | import NFTList from 'parts/NFTList' 10 | import LINKS from 'utils/constants/links' 11 | 12 | const useStyles = makeStyles((theme) => ({ 13 | root: { 14 | display: 'flex', 15 | flexDirection: 'column', 16 | alignItems: 'center' 17 | }, 18 | container: { 19 | width: '100%', 20 | maxWidth: theme.custom.layout.maxMarketPlaceWidth, 21 | padding: theme.spacing(2), 22 | }, 23 | filterContainer: { 24 | display: 'flex', 25 | flexDirection: 'column', 26 | alignItems: 'flex-end', 27 | marginBottom: theme.spacing(2) 28 | }, 29 | })); 30 | 31 | const PAGE_COUNT = 8; 32 | 33 | const Marketplace = () => { 34 | const classes = useStyles(); 35 | const router = useRouter(); 36 | 37 | const [goods, setGoods] = useState([]); 38 | const [first, setFirst] = useState(0); 39 | const [isLast, setIsLast] = useState(false); 40 | const [search, setSearch] = useState(router.query.search); 41 | 42 | useEffect(() => { 43 | getAllOpenAskOrders(); 44 | // eslint-disable-next-line react-hooks/exhaustive-deps 45 | }, [search]); 46 | 47 | const getAllOpenAskOrders = useCallback(async () => { 48 | try { 49 | if (!isLast) { 50 | const params = { 51 | first, 52 | last: first + PAGE_COUNT - 1, 53 | query: search 54 | } 55 | 56 | const { openOrders = [] } = await jupiterAPI.searchAllOpenAskOrders(params); 57 | if (first === 0) { 58 | setGoods(openOrders); 59 | } else { 60 | setGoods((prev) => [...prev, ...openOrders]); 61 | } 62 | 63 | setFirst((prev) => prev + openOrders.length); 64 | setIsLast(openOrders.length < PAGE_COUNT); 65 | } 66 | } catch (error) { 67 | console.log(error) 68 | } 69 | }, [search, isLast, first, setGoods, setFirst, setIsLast]) 70 | 71 | const searchHandler = useCallback(async (value) => { 72 | if (search !== value) { 73 | setGoods([]); 74 | setFirst(0) 75 | setIsLast(false) 76 | setSearch(value); 77 | Router.replace({ 78 | pathname: LINKS.MARKETPLACE.HREF, 79 | query: { 80 | search: value 81 | } 82 | }); 83 | } 84 | }, [search, setSearch, setGoods, setFirst, setIsLast]) 85 | 86 | return ( 87 |
88 | 89 |
90 |
91 | 92 |
93 | 98 |
99 |
100 | ) 101 | } 102 | 103 | export default memo(Marketplace); -------------------------------------------------------------------------------- /containers/MyAccount/MyBalance/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { useSelector } from 'react-redux' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | import { Typography } from '@material-ui/core' 6 | 7 | import { NQT_WEIGHT } from 'utils/constants/common' 8 | 9 | const useStyles = makeStyles((theme) => ({ 10 | root: { 11 | display: 'flex', 12 | flexDirection: 'column', 13 | alignItems: 'center' 14 | }, 15 | title: { 16 | fontSize: 24, 17 | marginBottom: theme.spacing(1), 18 | [theme.breakpoints.down('sm')]: { 19 | fontSize: 20, 20 | }, 21 | }, 22 | balance: { 23 | fontSize: 30, 24 | color: theme.palette.primary.main, 25 | marginBottom: theme.spacing(5), 26 | [theme.breakpoints.down('sm')]: { 27 | fontSize: 24, 28 | }, 29 | }, 30 | })); 31 | 32 | const MyBalance = () => { 33 | const classes = useStyles(); 34 | const { currentUser, accountRS } = useSelector(state => state.auth); 35 | 36 | return ( 37 |
38 | 42 | JUP ADDRESS 43 | 44 | 49 | {accountRS} 50 | 51 | 55 | BALANCE 56 | 57 | 62 | {parseInt(currentUser?.balanceNQT || 0, 10) / NQT_WEIGHT} JUP 63 | 64 |
65 | ) 66 | } 67 | 68 | 69 | export default memo(MyBalance) -------------------------------------------------------------------------------- /containers/MyAccount/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import clsx from 'clsx' 5 | 6 | import ImageWall from 'parts/ImageWall' 7 | import MyBalance from './MyBalance' 8 | import EditAccount from './EditAccount' 9 | import { useCommonStyles } from 'styles/use-styles' 10 | 11 | const useStyles = makeStyles((theme) => ({ 12 | root: { 13 | display: 'flex', 14 | flexDirection: 'column', 15 | alignItems: 'center' 16 | }, 17 | container: { 18 | display: 'flex', 19 | flexDirection: 'column', 20 | alignItems: 'center', 21 | maxWidth: theme.custom.layout.maxDesktopWidth, 22 | margin: theme.spacing(7, 0, 13), 23 | [theme.breakpoints.down('xs')]: { 24 | margin: theme.spacing(2.5, 0) 25 | } 26 | }, 27 | })); 28 | 29 | const MyAccount = () => { 30 | const classes = useStyles(); 31 | const commonClasses = useCommonStyles(); 32 | 33 | return ( 34 |
35 | 36 |
37 | 38 | 39 |
40 |
41 | ) 42 | } 43 | 44 | export default memo(MyAccount) -------------------------------------------------------------------------------- /containers/MyNFTs/MyAskOrders/OrderItem/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useMemo } from 'react' 3 | import { Typography } from '@material-ui/core' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | 6 | import ContainedButton from 'components/UI/Buttons/ContainedButton' 7 | import ProductContent from 'parts/ProductContent' 8 | import getJSONParse from 'utils/helpers/getJSONParse' 9 | import { NQT_WEIGHT } from 'utils/constants/common' 10 | 11 | const useStyles = makeStyles((theme) => ({ 12 | itemContainer: { 13 | display: 'flex', 14 | width: '100%', 15 | marginBottom: theme.spacing(3), 16 | [theme.breakpoints.down('xs')]: { 17 | flexDirection: 'column' 18 | }, 19 | }, 20 | imageContainer: { 21 | display: 'flex', 22 | justifyContent: 'center', 23 | cursor: 'pointer', 24 | padding: theme.spacing(1), 25 | borderRadius: 2, 26 | border: `1px solid ${theme.custom.palette.border}`, 27 | backgroundColor: theme.palette.background.default, 28 | marginRight: theme.spacing(2), 29 | [theme.breakpoints.down('xs')]: { 30 | marginBottom: theme.spacing(1), 31 | }, 32 | }, 33 | image: { 34 | width: 90, 35 | height: 90, 36 | objectFit: 'cover', 37 | }, 38 | content: { 39 | display: 'flex', 40 | flexDirection: 'column', 41 | justifyContent: 'space-between', 42 | alignItems: 'flex-start', 43 | }, 44 | name: { 45 | fontSize: 16, 46 | fontWeight: 'bold', 47 | marginBottom: theme.spacing(1) 48 | }, 49 | delete: { 50 | backgroundColor: theme.custom.palette.red, 51 | }, 52 | rowContainer: { 53 | display: 'flex', 54 | alignItems: 'center', 55 | marginBottom: theme.spacing(1) 56 | }, 57 | label: { 58 | fontSize: 14, 59 | fontWeight: 'bold', 60 | textTransform: 'uppercase', 61 | width: 60 62 | }, 63 | value: { 64 | fontWeight: 'bold', 65 | fontSize: 14, 66 | } 67 | })); 68 | 69 | const OrderItem = ({ 70 | item, 71 | onDetail, 72 | onDelete 73 | }) => { 74 | const classes = useStyles(); 75 | 76 | const info = useMemo(() => getJSONParse(item?.message), [item]); 77 | 78 | return ( 79 |
80 |
onDetail(item)}> 81 | 85 |
86 |
87 |
88 | 92 | {item.description} 93 | 94 |
95 | 99 | PRICE 100 | 101 | 105 | {item?.priceNQT / NQT_WEIGHT || 0} JUP 106 | 107 |
108 |
109 | onDelete(item)} 112 | > 113 | Cancel 114 | 115 |
116 |
117 | ) 118 | } 119 | 120 | export default memo(OrderItem) -------------------------------------------------------------------------------- /containers/MyNFTs/MyAskOrders/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useState, useEffect, useCallback } from 'react' 3 | import { useRouter } from 'next/router' 4 | import { useSelector } from 'react-redux' 5 | import { makeStyles } from '@material-ui/core/styles' 6 | import { IconButton } from '@material-ui/core' 7 | import ExpandMoreIcon from '@material-ui/icons/ExpandMore' 8 | 9 | import * as jupiterAPI from 'services/api-jupiter' 10 | import NoData from 'parts/NoData' 11 | import CancelNFTOrderDialog from 'parts/CancelNFTOrderDialog' 12 | import TabPanel from '../Shared/TabPanel' 13 | import OrderItem from './OrderItem' 14 | import { isEmpty } from 'utils/helpers/utility' 15 | import usePopUp from 'utils/hooks/usePopUp' 16 | import MESSAGES from 'utils/constants/messages' 17 | import LINKS from 'utils/constants/links' 18 | 19 | const useStyles = makeStyles(() => ({ 20 | container: { 21 | display: 'flex', 22 | flexDirection: 'column', 23 | alignItems: 'center', 24 | }, 25 | moreIcon: { 26 | fontSize: 50 27 | } 28 | })); 29 | 30 | const PAGE_COUNT = 5; 31 | const MyAskOrders = ({ 32 | index, 33 | value 34 | }) => { 35 | const classes = useStyles(); 36 | const { setPopUp } = usePopUp(); 37 | const router = useRouter(); 38 | 39 | const { currentUser } = useSelector(state => state.auth); 40 | const [assets, setAssets] = useState([]) 41 | const [first, setFirst] = useState(0); 42 | const [isLast, setIsLast] = useState(false) 43 | const [openModal, setOpenModal] = useState(false); 44 | const [selectedItem, setSelectedItem] = useState(false); 45 | 46 | useEffect(() => { 47 | if (!isEmpty(currentUser)) { 48 | getAccountCurrentAskOrders(); 49 | } 50 | // eslint-disable-next-line react-hooks/exhaustive-deps 51 | }, [currentUser]) 52 | 53 | const getAccountCurrentAskOrders = useCallback(async () => { 54 | if (!isLast) { 55 | const params = { 56 | first, 57 | last: first + PAGE_COUNT - 1, 58 | account: currentUser.account 59 | } 60 | 61 | const response = await jupiterAPI.getAccountCurrentAskOrders(params); 62 | if (response?.errorCode) { 63 | setPopUp({ text: MESSAGES.GET_NFT_ERROR }) 64 | return; 65 | } 66 | 67 | const { askOrders = [] } = response; 68 | setAssets((prev) => [...prev, ...askOrders]); 69 | setFirst((prev) => prev + askOrders.length); 70 | setIsLast(askOrders.length < PAGE_COUNT); 71 | } 72 | }, [isLast, first, currentUser, setAssets, setFirst, setIsLast, setPopUp]) 73 | 74 | const deleteHandler = useCallback((item) => { 75 | setSelectedItem(item) 76 | setOpenModal(true) 77 | }, [setOpenModal, setSelectedItem]) 78 | 79 | const detailNFTHandler = useCallback((item) => { 80 | router.push( 81 | LINKS.NFT_DETAIL.HREF, 82 | LINKS.NFT_DETAIL.HREF.replace('[goods]', item.asset) 83 | ) 84 | }, [router]) 85 | 86 | return ( 87 | 88 | {isEmpty(assets) 89 | ? ( 90 | 91 | ) : ( 92 |
93 | {assets.map((item, index) => ( 94 | 100 | ))} 101 | { 102 | !isLast && 103 | 104 | 105 | 106 | } 107 |
108 | ) 109 | } 110 | { 111 | openModal && 112 | 117 | } 118 |
119 | ) 120 | } 121 | 122 | export default memo(MyAskOrders) -------------------------------------------------------------------------------- /containers/MyNFTs/MyAssets/AssetItem/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useMemo } from 'react' 3 | import { Grid, Typography } from '@material-ui/core' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | 6 | import ContainedButton from 'components/UI/Buttons/ContainedButton' 7 | import OutlinedButton from 'components/UI/Buttons/OutlinedButton' 8 | import ProductContent from 'parts/ProductContent' 9 | import getJSONParse from 'utils/helpers/getJSONParse' 10 | 11 | const useStyles = makeStyles((theme) => ({ 12 | itemContainer: { 13 | display: 'flex', 14 | width: '100%', 15 | marginBottom: theme.spacing(3), 16 | [theme.breakpoints.down('xs')]: { 17 | flexDirection: 'column' 18 | }, 19 | }, 20 | imageContainer: { 21 | display: 'flex', 22 | justifyContent: 'center', 23 | cursor: 'pointer', 24 | padding: theme.spacing(1), 25 | borderRadius: 2, 26 | border: `1px solid ${theme.custom.palette.border}`, 27 | backgroundColor: theme.palette.background.default, 28 | marginRight: theme.spacing(2), 29 | [theme.breakpoints.down('xs')]: { 30 | marginBottom: theme.spacing(1), 31 | }, 32 | }, 33 | image: { 34 | width: 90, 35 | height: 90, 36 | objectFit: 'cover', 37 | }, 38 | content: { 39 | display: 'flex', 40 | flexDirection: 'column', 41 | justifyContent: 'space-between', 42 | alignItems: 'flex-start', 43 | }, 44 | name: { 45 | fontSize: 16, 46 | fontWeight: 'bold', 47 | marginBottom: theme.spacing(1) 48 | }, 49 | delete: { 50 | borderColor: theme.custom.palette.red, 51 | backgroundColor: theme.custom.palette.red, 52 | }, 53 | })); 54 | 55 | const AssetItem = ({ 56 | item, 57 | onDetail, 58 | onSell, 59 | onSend, 60 | onDelete 61 | }) => { 62 | const classes = useStyles(); 63 | 64 | const info = useMemo(() => getJSONParse(item?.message), [item]); 65 | 66 | return ( 67 |
68 |
onDetail(item)}> 69 | 73 |
74 |
75 | 79 | {item.description} 80 | 81 | { 82 | item.unconfirmedQuantityQNT > 0 && 83 | 84 | 85 | onSell(item)}> 86 | Sell 87 | 88 | 89 | 90 | onSend(item)}> 91 | Transfer 92 | 93 | 94 | 95 | onDelete(item)} 98 | > 99 | Delete 100 | 101 | 102 | 103 | } 104 |
105 |
106 | ) 107 | } 108 | 109 | export default memo(AssetItem) -------------------------------------------------------------------------------- /containers/MyNFTs/MyBidOrders/BidItem/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useMemo } from 'react' 3 | import { Typography } from '@material-ui/core' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | 6 | import ContainedButton from 'components/UI/Buttons/ContainedButton' 7 | import ProductContent from 'parts/ProductContent' 8 | import getJSONParse from 'utils/helpers/getJSONParse' 9 | import { NQT_WEIGHT } from 'utils/constants/common' 10 | 11 | const useStyles = makeStyles((theme) => ({ 12 | itemContainer: { 13 | display: 'flex', 14 | width: '100%', 15 | marginBottom: theme.spacing(3), 16 | [theme.breakpoints.down('xs')]: { 17 | flexDirection: 'column' 18 | }, 19 | }, 20 | imageContainer: { 21 | display: 'flex', 22 | justifyContent: 'center', 23 | cursor: 'pointer', 24 | padding: theme.spacing(1), 25 | borderRadius: 2, 26 | border: `1px solid ${theme.custom.palette.border}`, 27 | backgroundColor: theme.palette.background.default, 28 | marginRight: theme.spacing(2), 29 | [theme.breakpoints.down('xs')]: { 30 | marginBottom: theme.spacing(1), 31 | }, 32 | }, 33 | image: { 34 | width: 90, 35 | height: 90, 36 | objectFit: 'cover', 37 | }, 38 | content: { 39 | display: 'flex', 40 | flexDirection: 'column', 41 | justifyContent: 'space-between', 42 | alignItems: 'flex-start', 43 | }, 44 | name: { 45 | fontSize: 16, 46 | fontWeight: 'bold', 47 | marginBottom: theme.spacing(1) 48 | }, 49 | delete: { 50 | backgroundColor: theme.custom.palette.red, 51 | }, 52 | rowContainer: { 53 | display: 'flex', 54 | alignItems: 'center', 55 | marginBottom: theme.spacing(1) 56 | }, 57 | label: { 58 | fontSize: 14, 59 | fontWeight: 'bold', 60 | textTransform: 'uppercase', 61 | width: 60 62 | }, 63 | value: { 64 | fontWeight: 'bold', 65 | fontSize: 14, 66 | } 67 | })); 68 | 69 | const BidItem = ({ 70 | item, 71 | onDetail, 72 | onDelete 73 | }) => { 74 | const classes = useStyles(); 75 | 76 | const info = useMemo(() => getJSONParse(item?.message), [item]); 77 | 78 | return ( 79 |
80 |
onDetail(item)}> 81 | 85 |
86 |
87 |
88 | 92 | {item.description} 93 | 94 |
95 | 99 | PRICE 100 | 101 | 105 | {item?.priceNQT / NQT_WEIGHT || 0} JUP 106 | 107 |
108 |
109 | onDelete(item)} 112 | > 113 | Cancel 114 | 115 |
116 |
117 | ) 118 | } 119 | 120 | export default memo(BidItem) -------------------------------------------------------------------------------- /containers/MyNFTs/MyBidOrders/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useState, useEffect, useCallback } from 'react' 3 | import { useRouter } from 'next/router' 4 | import { useSelector } from 'react-redux' 5 | import { makeStyles } from '@material-ui/core/styles' 6 | import { IconButton } from '@material-ui/core' 7 | import ExpandMoreIcon from '@material-ui/icons/ExpandMore' 8 | 9 | import * as jupiterAPI from 'services/api-jupiter' 10 | import NoData from 'parts/NoData' 11 | import CancelNFTOrderDialog from 'parts/CancelNFTOrderDialog' 12 | import TabPanel from '../Shared/TabPanel' 13 | import BidItem from './BidItem' 14 | import { isEmpty } from 'utils/helpers/utility' 15 | import usePopUp from 'utils/hooks/usePopUp' 16 | import MESSAGES from 'utils/constants/messages' 17 | import LINKS from 'utils/constants/links' 18 | 19 | const useStyles = makeStyles(() => ({ 20 | container: { 21 | display: 'flex', 22 | flexDirection: 'column', 23 | alignItems: 'center', 24 | }, 25 | moreIcon: { 26 | fontSize: 50 27 | } 28 | })); 29 | 30 | const PAGE_COUNT = 5; 31 | const MyBidOrders = ({ 32 | index, 33 | value 34 | }) => { 35 | const classes = useStyles(); 36 | const { setPopUp } = usePopUp(); 37 | const router = useRouter(); 38 | 39 | const { currentUser } = useSelector(state => state.auth); 40 | const [assets, setAssets] = useState([]) 41 | const [first, setFirst] = useState(0); 42 | const [isLast, setIsLast] = useState(false) 43 | const [openModal, setOpenModal] = useState(false); 44 | const [selectedItem, setSelectedItem] = useState(false); 45 | 46 | useEffect(() => { 47 | if (!isEmpty(currentUser)) { 48 | getAccountCurrentBidOrders(); 49 | } 50 | // eslint-disable-next-line react-hooks/exhaustive-deps 51 | }, [currentUser]) 52 | 53 | const getAccountCurrentBidOrders = useCallback(async () => { 54 | if (!isLast) { 55 | const params = { 56 | first, 57 | last: first + PAGE_COUNT - 1, 58 | account: currentUser.account 59 | } 60 | 61 | const response = await jupiterAPI.getAccountCurrentBidOrders(params); 62 | if (response?.errorCode) { 63 | setPopUp({ text: MESSAGES.GET_NFT_ERROR }) 64 | return; 65 | } 66 | 67 | const { bidOrders = [] } = response; 68 | setAssets((prev) => [...prev, ...bidOrders]); 69 | setFirst((prev) => prev + bidOrders.length); 70 | setIsLast(bidOrders.length < PAGE_COUNT); 71 | } 72 | }, [isLast, first, currentUser, setAssets, setFirst, setIsLast, setPopUp]) 73 | 74 | const deleteHandler = useCallback((item) => { 75 | setSelectedItem(item) 76 | setOpenModal(true) 77 | }, [setOpenModal, setSelectedItem]) 78 | 79 | const detailNFTHandler = useCallback((item) => { 80 | router.push( 81 | LINKS.NFT_DETAIL.HREF, 82 | LINKS.NFT_DETAIL.HREF.replace('[goods]', item.asset) 83 | ) 84 | }, [router]) 85 | 86 | return ( 87 | 88 | {isEmpty(assets) 89 | ? ( 90 | 91 | ) : ( 92 |
93 | {assets.map((item, index) => ( 94 | 100 | ))} 101 | { 102 | !isLast && 103 | 104 | 105 | 106 | } 107 |
108 | ) 109 | } 110 | { 111 | openModal && 112 | 117 | } 118 |
119 | ) 120 | } 121 | 122 | export default memo(MyBidOrders) -------------------------------------------------------------------------------- /containers/MyNFTs/Shared/TabPanel/index.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import { Box } from '@material-ui/core' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | 5 | const useStyles = makeStyles(() => ({ 6 | tabPanel: { 7 | width: '100%' 8 | }, 9 | })); 10 | 11 | const TabPanel = ({ 12 | children, 13 | value, 14 | index, 15 | ...rest 16 | }) => { 17 | const classes = useStyles(); 18 | 19 | return ( 20 | 34 | ) 35 | }; 36 | 37 | export default memo(TabPanel) -------------------------------------------------------------------------------- /containers/MyNFTs/Shared/VerticalTabs/index.js: -------------------------------------------------------------------------------- 1 | import { memo, useCallback } from 'react' 2 | import { makeStyles } from '@material-ui/core/styles' 3 | import { Tab, Tabs } from '@material-ui/core' 4 | 5 | function a11yProps(index) { 6 | return { 7 | id: `vertical-tab-${index}`, 8 | 'aria-controls': `vertical-tabpanel-${index}`, 9 | }; 10 | } 11 | 12 | const useStyles = makeStyles((theme) => ({ 13 | tabs: { 14 | maxWidth: 180, 15 | width: '100%', 16 | borderRight: `2px solid ${theme.palette.divider}`, 17 | [theme.breakpoints.down('xs')]: { 18 | maxWidth: 80, 19 | }, 20 | }, 21 | tab: { 22 | fontSize: 18, 23 | fontWeight: 'bold', 24 | textDecoration: 'underline', 25 | textAlign: 'right', 26 | color: theme.palette.text.primary, 27 | padding: theme.spacing(0.5, 1, 0.5, 0), 28 | minHeight: 'unset', 29 | [theme.breakpoints.down('xs')]: { 30 | fontSize: 14, 31 | }, 32 | }, 33 | selected: { 34 | color: theme.palette.primary.main, 35 | }, 36 | wrapper: { 37 | alignItems: 'flex-end' 38 | }, 39 | indicator: { 40 | backgroundColor: 'unset', 41 | } 42 | })); 43 | 44 | const VerticalTabs = ({ 45 | tabs, 46 | value, 47 | setValue 48 | }) => { 49 | const classes = useStyles(); 50 | 51 | const handleChange = useCallback((event, newValue) => { 52 | setValue(newValue); 53 | }, [setValue]); 54 | 55 | return ( 56 | 67 | { 68 | tabs.map((item, index) => ( 69 | 79 | )) 80 | } 81 | 82 | ); 83 | } 84 | 85 | export default memo(VerticalTabs) 86 | -------------------------------------------------------------------------------- /containers/MyNFTs/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useEffect, useState } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import clsx from 'clsx' 5 | import Router, { useRouter } from 'next/router' 6 | 7 | import ImageWall from 'parts/ImageWall' 8 | import { useCommonStyles } from 'styles/use-styles' 9 | import VerticalTabs from './Shared/VerticalTabs' 10 | import MyAssets from './MyAssets' 11 | import MyAskOrders from './MyAskOrders' 12 | import MyBidOrders from './MyBidOrders' 13 | import LINKS from 'utils/constants/links' 14 | 15 | const useStyles = makeStyles((theme) => ({ 16 | root: { 17 | display: 'flex', 18 | flexDirection: 'column', 19 | alignItems: 'center' 20 | }, 21 | container: { 22 | flexGrow: 1, 23 | display: 'flex', 24 | height: '100%', 25 | width: '100%', 26 | maxWidth: 720, 27 | padding: theme.spacing(0, 2), 28 | marginBottom: theme.spacing(10) 29 | }, 30 | })); 31 | 32 | const TABS = [ 33 | 'MY NFTS', 34 | 'MY SELL ORDERS', 35 | 'MY BUY ORDERS', 36 | ] 37 | 38 | const MyNFTs = () => { 39 | const classes = useStyles(); 40 | const commonClasses = useCommonStyles(); 41 | const router = useRouter(); 42 | 43 | const [selectedTab, setSelectedTab] = useState(() => { 44 | const findIndex = TABS.findIndex((tab) => tab === router.query.tab) 45 | return findIndex < 0 ? 0 : findIndex 46 | }); 47 | 48 | useEffect(() => { 49 | const tabValue = TABS[selectedTab] 50 | Router.replace({ 51 | pathname: LINKS.MY_NFTS.HREF, 52 | query: { 53 | tab: tabValue 54 | } 55 | }); 56 | }, [selectedTab]) 57 | 58 | return ( 59 |
60 | 61 |
62 | 67 | 71 | 75 | 79 |
80 |
81 | ) 82 | } 83 | 84 | export default memo(MyNFTs) -------------------------------------------------------------------------------- /containers/NFTDetail/HistoricalPrices/index.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import { makeStyles } from '@material-ui/core/styles' 3 | import { 4 | Typography, 5 | TableCell, 6 | TableRow, 7 | } from '@material-ui/core' 8 | 9 | import TableContainer from 'parts/Table/TableContainer' 10 | import { isEmpty } from 'utils/helpers/utility' 11 | import { NQT_WEIGHT } from 'utils/constants/common' 12 | 13 | const useStyles = makeStyles((theme) => ({ 14 | root: { 15 | overflowX: 'inherit', 16 | marginBottom: theme.spacing(1) 17 | }, 18 | title: { 19 | fontWeight: 'bold', 20 | textTransform: 'uppercase', 21 | }, 22 | cell: { 23 | border: 'unset', 24 | padding: theme.spacing(1, 0) 25 | }, 26 | })); 27 | 28 | const columns = [ 29 | { id: 'seller', label: 'Seller', minWidth: 150 }, 30 | { id: 'buyer', label: 'Buyer', minWidth: 150 }, 31 | { id: 'price', label: 'Price', minWidth: 60 }, 32 | ]; 33 | 34 | const HistoricalPrices = ({ 35 | trades 36 | }) => { 37 | const classes = useStyles(); 38 | 39 | return ( 40 |
41 | 42 | Historical Prices 43 | 44 | {isEmpty(trades) 45 | ? ( 46 | 47 | No Transaction Histories 48 | 49 | ) : ( 50 | 51 | {trades.map((trade, index) => ( 52 | 53 | 54 | 55 | {trade.sellerRS} 56 | 57 | 58 | 59 | 60 | {trade.buyerRS} 61 | 62 | 63 | 64 | {trade.priceNQT / NQT_WEIGHT} JUP 65 | 66 | 67 | ))} 68 | 69 | )} 70 |
71 | ) 72 | } 73 | 74 | export default memo(HistoricalPrices) -------------------------------------------------------------------------------- /containers/NFTDetail/NFTInformation/InformationContent/index.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import { makeStyles } from '@material-ui/core/styles' 3 | import { Typography } from '@material-ui/core' 4 | 5 | import { NQT_WEIGHT } from 'utils/constants/common' 6 | import { getDateFromTimestamp } from 'utils/helpers/getTimestamp' 7 | 8 | const useStyles = makeStyles((theme) => ({ 9 | name: { 10 | fontSize: 30, 11 | fontWeight: 'bold', 12 | marginBottom: theme.spacing(1) 13 | }, 14 | description: { 15 | fontSize: 16, 16 | marginBottom: theme.spacing(2) 17 | }, 18 | container: { 19 | marginBottom: theme.spacing(2) 20 | }, 21 | label: { 22 | fontSize: 16, 23 | fontWeight: 'bold' 24 | }, 25 | accountName: { 26 | fontSize: 14, 27 | }, 28 | accountDescription: { 29 | fontSize: 14, 30 | }, 31 | accountRS: { 32 | fontSize: 14, 33 | fontWeight: 'bold', 34 | }, 35 | price: { 36 | fontSize: 14, 37 | }, 38 | })); 39 | 40 | const InformationContent = ({ 41 | good, 42 | sellerAccount, 43 | creatorAccount, 44 | assetInfo 45 | }) => { 46 | const classes = useStyles(); 47 | 48 | return ( 49 | <> 50 | 55 | {good.description} 56 | 57 | 62 | {assetInfo.description} 63 | 64 | 65 |
66 | 70 | CREATOR INFO 71 | 72 | 76 | {creatorAccount.accountRS} 77 | 78 | 82 | {creatorAccount.name || 'No Name'} 83 | 84 | {creatorAccount?.description && 85 | 89 | {creatorAccount.description} 90 | 91 | } 92 | 96 | {`DATE OF CREATION: ${getDateFromTimestamp(good.timestamp)}`} 97 | 98 |
99 | 100 |
101 | 105 | SELLER INFO 106 | 107 | 111 | {sellerAccount.accountRS} 112 | 113 | 117 | {sellerAccount.name || 'No Name'} 118 | 119 | {sellerAccount?.description && 120 | 124 | {sellerAccount.description} 125 | 126 | } 127 |
128 | 129 |
130 | 134 | PRICE 135 | 136 | 140 | {!good.priceNQT 141 | ? 'No Price' 142 | : `${good.priceNQT / NQT_WEIGHT} JUP` 143 | } 144 | 145 |
146 | 147 | ) 148 | } 149 | 150 | export default memo(InformationContent) -------------------------------------------------------------------------------- /containers/NFTDetail/NFTInformation/index.js: -------------------------------------------------------------------------------- 1 | import { memo, useState, useCallback } from 'react' 2 | import { makeStyles } from '@material-ui/core/styles' 3 | import { useRouter } from 'next/router' 4 | import { useSelector } from 'react-redux' 5 | 6 | import ContainedButton from 'components/UI/Buttons/ContainedButton' 7 | import CancelNFTOrderDialog from 'parts/CancelNFTOrderDialog' 8 | import PurchaseNFTDialog from 'parts/PurchaseNFTDialog' 9 | import InformationContent from './InformationContent' 10 | import usePopUp from 'utils/hooks/usePopUp' 11 | import MESSAGES from 'utils/constants/messages' 12 | import LINKS from 'utils/constants/links' 13 | import { isEmpty } from 'utils/helpers/utility' 14 | 15 | const useStyles = makeStyles((theme) => ({ 16 | root: { 17 | marginBottom: theme.spacing(2), 18 | }, 19 | delete: { 20 | backgroundColor: theme.custom.palette.red 21 | } 22 | })); 23 | 24 | const NFTInformation = ({ 25 | isMine, 26 | good, 27 | order, 28 | sellerAccount, 29 | creatorAccount, 30 | assetInfo 31 | }) => { 32 | const classes = useStyles(); 33 | const router = useRouter(); 34 | const { setPopUp } = usePopUp(); 35 | 36 | const { accountRS } = useSelector(state => state.auth); 37 | const [openDeleteModal, setOpenDeleteModal] = useState(false); 38 | const [openPurchaseModal, setOpenPurchaseModal] = useState(false); 39 | 40 | const purchaseHandler = useCallback(() => { 41 | if (!accountRS) { 42 | setPopUp({ text: MESSAGES.AUTH_REQUIRED }) 43 | router.push(LINKS.SIGN_IN.HREF) 44 | return; 45 | } 46 | 47 | setOpenPurchaseModal(true) 48 | }, [accountRS, router, setPopUp, setOpenPurchaseModal]) 49 | 50 | return ( 51 |
52 | 58 | {!isEmpty(order) && 59 | (isMine 60 | ? ( 61 | setOpenDeleteModal(true)} 64 | > 65 | Delete 66 | 67 | ) : ( 68 | 69 | Buy Now 70 | 71 | )) 72 | } 73 | 74 | {openPurchaseModal && 75 | 80 | } 81 | {openDeleteModal && 82 | 87 | } 88 |
89 | ) 90 | } 91 | 92 | export default memo(NFTInformation) -------------------------------------------------------------------------------- /containers/NFTDetail/SellerNFTs/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useState, useCallback } from 'react' 3 | import { makeStyles } from '@material-ui/core/styles' 4 | import { Typography } from '@material-ui/core' 5 | 6 | import * as jupiterAPI from 'services/api-jupiter' 7 | import NFTList from 'parts/NFTList' 8 | 9 | const useStyles = makeStyles((theme) => ({ 10 | container: { 11 | width: '100%', 12 | maxWidth: theme.custom.layout.maxMarketPlaceWidth, 13 | padding: theme.spacing(4, 3) 14 | }, 15 | title: { 16 | fontSize: 28, 17 | } 18 | })); 19 | 20 | const PAGE_COUNT = 8; 21 | 22 | const SellerNFTs = ({ 23 | account 24 | }) => { 25 | const classes = useStyles(); 26 | 27 | const [goods, setGoods] = useState([]); 28 | const [first, setFirst] = useState(0); 29 | const [isLast, setIsLast] = useState(false); 30 | 31 | const getAccountCurrentAskOrders = useCallback(async () => { 32 | try { 33 | if (!isLast) { 34 | const params = { 35 | first, 36 | last: first + PAGE_COUNT - 1, 37 | account 38 | } 39 | 40 | const { askOrders = [] } = await jupiterAPI.getAccountCurrentAskOrders(params); 41 | 42 | setGoods((prev) => [...prev, ...askOrders]); 43 | setFirst((prev) => prev + askOrders.length); 44 | setIsLast(askOrders.length < PAGE_COUNT); 45 | } 46 | } catch (error) { 47 | console.log(error) 48 | } 49 | }, [account, isLast, first, setGoods, setFirst, setIsLast]) 50 | 51 | return ( 52 |
53 | 54 | MORE NFTS FROM THIS SELLER 55 | 56 | 61 |
62 | ) 63 | } 64 | 65 | export default memo(SellerNFTs); -------------------------------------------------------------------------------- /containers/TransactionHistory/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { memo, useEffect, useCallback, useState } from 'react' 3 | import { useRouter } from 'next/router' 4 | import { makeStyles } from '@material-ui/core/styles' 5 | import { IconButton } from '@material-ui/core' 6 | import ExpandMoreIcon from '@material-ui/icons/ExpandMore' 7 | import clsx from 'clsx' 8 | 9 | import * as jupiterAPI from 'services/api-jupiter' 10 | import ImageWall from 'parts/ImageWall' 11 | import TransactionItem from './TransactionItem' 12 | import { useCommonStyles } from 'styles/use-styles' 13 | import LINKS from 'utils/constants/links' 14 | 15 | const useStyles = makeStyles((theme) => ({ 16 | root: { 17 | display: 'flex', 18 | flexDirection: 'column', 19 | alignItems: 'center' 20 | }, 21 | container: { 22 | display: 'flex', 23 | flexDirection: 'column', 24 | height: '100%', 25 | width: '100%', 26 | maxWidth: 560, 27 | marginBottom: theme.spacing(10), 28 | }, 29 | moreIcon: { 30 | fontSize: 50 31 | } 32 | })); 33 | 34 | const PAGE_COUNT = 8; 35 | 36 | const TransactionHistory = () => { 37 | const classes = useStyles(); 38 | const router = useRouter(); 39 | const commonClasses = useCommonStyles(); 40 | 41 | const [trades, setTrades] = useState([]) 42 | const [first, setFirst] = useState(0) 43 | const [isLast, setIsLast] = useState(false) 44 | 45 | useEffect(() => { 46 | searchAllTrades(); 47 | // eslint-disable-next-line react-hooks/exhaustive-deps 48 | }, []) 49 | 50 | const searchAllTrades = useCallback(async () => { 51 | if (!isLast) { 52 | const params = { 53 | first, 54 | last: first + PAGE_COUNT - 1, 55 | } 56 | 57 | const response = await jupiterAPI.searchAllTrades(params); 58 | const { trades = [] } = response; 59 | setTrades((prev) => [...prev, ...trades]); 60 | setFirst((prev) => prev + trades.length); 61 | setIsLast(trades.length < PAGE_COUNT); 62 | } 63 | }, [isLast, first, setTrades, setFirst, setIsLast]); 64 | 65 | const detailNFTHandler = useCallback((item) => { 66 | console.log(item) 67 | router.push( 68 | LINKS.NFT_DETAIL.HREF, 69 | LINKS.NFT_DETAIL.HREF.replace('[goods]', item.asset) 70 | ) 71 | }, [router]) 72 | 73 | return ( 74 |
75 | 76 |
77 | { 78 | trades.map((item, index) => ( 79 | 84 | )) 85 | } 86 | { 87 | !isLast && 88 | 89 | 90 | 91 | } 92 |
93 |
94 | ) 95 | } 96 | 97 | export default memo(TransactionHistory) -------------------------------------------------------------------------------- /contexts/ui-context.js: -------------------------------------------------------------------------------- 1 | import { createContext, useState, useContext } from 'react' 2 | 3 | const DarkModeContext = createContext() 4 | 5 | function DarkModeProvider({ children }) { 6 | const [darkMode, setDarkMode] = useState(false) 7 | const value = { darkMode, setDarkMode }; 8 | 9 | return ( 10 | 11 | {children} 12 | 13 | ) 14 | } 15 | 16 | function useDarkMode() { 17 | const context = useContext(DarkModeContext) 18 | if (context === undefined) { 19 | throw new Error('useDarkMode must be used within a DarkModeProvider') 20 | } 21 | return context 22 | } 23 | 24 | export { DarkModeProvider, useDarkMode } -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "*": ["*", "/*"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | env: { 4 | NETWORK: process.env.NETWORK, 5 | EUROPA_ID: process.env.EUROPA_ID, 6 | }, 7 | images: { 8 | domains: [ 9 | 'res.cloudinary.com' 10 | ], 11 | }, 12 | webpack: (config, { dev }) => { 13 | if (dev) { 14 | config.module.rules.push({ 15 | test: /\.(j|t)sx?$/, 16 | exclude: /node_modules/, 17 | loader: 'eslint-loader' 18 | }); 19 | } 20 | 21 | config.module.rules.push({ 22 | test: /\.svg$/, 23 | use: ['@svgr/webpack'] 24 | }); 25 | 26 | return config; 27 | } 28 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Leda", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "deploy:vercel": "vercel --prod" 10 | }, 11 | "dependencies": { 12 | "@hookform/resolvers": "1.0.0", 13 | "@iarna/word-count": "^1.1.2", 14 | "@material-ui/core": "^4.11.2", 15 | "@material-ui/icons": "^4.11.2", 16 | "axios": "^0.21.1", 17 | "clsx": "^1.1.1", 18 | "next": "10.0.4", 19 | "react": "17.0.1", 20 | "react-alice-carousel": "^2.4.0", 21 | "react-div-100vh": "^0.5.6", 22 | "react-dom": "17.0.1", 23 | "react-dropzone": "^11.3.1", 24 | "react-hook-form": "^6.14.0", 25 | "react-infinite-scroll-component": "^6.0.0", 26 | "react-lottie": "^1.2.3", 27 | "react-redux": "^7.2.2", 28 | "react-text-mask": "^5.4.3", 29 | "redux": "^4.0.5", 30 | "redux-thunk": "^2.3.0", 31 | "yup": "^0.32.8" 32 | }, 33 | "devDependencies": { 34 | "@svgr/webpack": "^5.5.0", 35 | "babel-eslint": "^10.1.0", 36 | "eslint": "^7.17.0", 37 | "eslint-loader": "^4.0.2", 38 | "eslint-plugin-react": "^7.22.0", 39 | "eslint-plugin-react-hooks": "^4.2.0", 40 | "pm2": "^4.5.1", 41 | "redux-devtools-extension": "^2.13.8", 42 | "redux-logger": "^3.0.6" 43 | } 44 | } -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | 2 | import Head from 'next/head' 3 | import { Provider } from 'react-redux' 4 | import CssBaseline from '@material-ui/core/CssBaseline' 5 | 6 | import store from 'store' 7 | import { DarkModeProvider } from 'contexts/ui-context' 8 | import InitProvider from 'utils/hocs/InitProvider' 9 | import PopUpProvider from 'utils/hocs/PopUpProvider' 10 | import ThemeProvider from 'utils/hocs/ThemeProvider' 11 | import * as COMMON_CONSTANTS from 'utils/constants/common' 12 | 13 | function MyApp({ Component, pageProps }) { 14 | return ( 15 | <> 16 | 17 | {COMMON_CONSTANTS.TITLE} 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {/* Open Graph / Facebook */} 26 | 27 | 28 | 29 | 30 | {/* Twitter */} 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ) 48 | } 49 | 50 | MyApp.getInitialProps = async ({ Component, ctx }) => { 51 | const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {}; 52 | return { pageProps }; 53 | }; 54 | 55 | export default MyApp 56 | -------------------------------------------------------------------------------- /pages/_document.js: -------------------------------------------------------------------------------- 1 | 2 | import { Fragment } from 'react' 3 | import Document, { Html, Head, Main, NextScript } from 'next/document' 4 | import { ServerStyleSheets } from '@material-ui/core/styles' 5 | 6 | import globalStyles from 'styles/global' 7 | 8 | class MyDocument extends Document { 9 | static async getInitialProps(ctx) { 10 | const sheets = new ServerStyleSheets(); 11 | const originalRenderPage = ctx.renderPage; 12 | 13 | ctx.renderPage = () => originalRenderPage({ 14 | enhanceApp: App => props => sheets.collect() 15 | }); 16 | 17 | const initialProps = await Document.getInitialProps(ctx); 18 | 19 | return { 20 | ...initialProps, 21 | styles: [ 22 | 23 | {initialProps.styles} 24 | {sheets.getStyleElement()} 25 | 26 | ] 27 | }; 28 | } 29 | 30 | render() { 31 | return ( 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 44 | 47 | 48 | 49 |
50 | 51 | 54 |