Minto NFT Marketplace is a marketplace where you can buy, sell, bid, and create NFTs. Here you can find category wised NFTs with their seller and creators. Besides, you can connect with your crypto wallet and start creating your own assets and can sell them!
This project is Build with React, a JavaScript frameworks for building user interfaces. Besides, Material UI was used as a UI component library. These are the main tools that are used in this project and the other tools and libraries are listed below:
- Axios - For data fetching.
- React Router V6 - For in app routing.
- i18Next - For internationalization.
- i18Next Browser Language Detector - For detecting the browsers default language.
- i18Next Http Backend - Backend layer plugin for i18Next
- React Icons - Icon component library.
- React Slick - For slideshow on Homepage.
- Slick Carousel - Core dependance library for React Slick.
- Anime.js - For animating the SVGs.
- React Spring - For cards animation on Homepage.
- React Use Gesture - Dependancy for using gesture in React Spring.
- Date FNS - For managing date-time in countdown board.
Here is the list of all dependancies -
"dependencies": {
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@mui/lab": "^5.0.0-alpha.62",
"@mui/material": "^5.2.4",
"@mui/styled-engine-sc": "^5.1.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@testing-library/user-event": "^13.2.1",
"animejs": "^3.2.1",
"axios": "^0.24.0",
"date-fns": "^2.27.0",
"i18next": "^21.6.5",
"i18next-browser-languagedetector": "^6.1.2",
"i18next-http-backend": "^1.3.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-i18next": "^11.15.3",
"react-icons": "^4.3.1",
"react-lazy-load-image-component": "^1.5.1",
"react-router-dom": "6",
"react-scripts": "5.0.0",
"react-slick": "^0.28.1",
"react-spring": "^9.4.2",
"react-use-gesture": "^9.1.3",
"slick-carousel": "^1.8.1",
"styled-components": "^5.3.3",
"web-vitals": "^2.1.0"
},
- Responsive Design
- Dark / Light Theme
- Internationalization with four languages.
- English.
- Hindi.
- Tamil.
- Bengali.
- Crypto Wallet Connect.
- Live Auction Count Down.
It is a complete React Project, so we assume that you have the latest version of Node.js pre-installed. The Node Package Manager or NPM came with the Node.js bundle so you don't need to install it separately. However, if you want to use Yarn package manager which is our recommendation, simply run:
npm install --global yarn
Then check the version with:
yarn --version
To get started change directory to the project folder by running:
cd minto-nft-marketplace
Then you have to install all the dependacies that came with the project. We recommend to use Yarn, as there exist a .lock
file.
TL;DR
- it will make your life easier. 😎
yarn # or npm install
After installing all required dependancies, now we are good to start our project by running:
yarn start # or npm start
That's it. Now you can view the entire project on your local server.
So, the basic usage of the application is really simple. We divided this section with Folder Structure, Components, Pages, Hooks, Utitlities, and Assets. Besides, the initial index.html
and other publicly seen assets can be found in /public
folder.
The App.js
file is the root of all the Pages and Components that will described below. Here we used the Custom Theme Provider, the states of theme switching and the AnimatedLoader
Here is the full view
import { useState } from "react";
import { useEffect } from "react";
// Material UI
import { ThemeProvider } from "@emotion/react";
import { Box, useMediaQuery } from "@mui/material";
// React Router
import { BrowserRouter, Route, Routes } from "react-router-dom";
// Modules
import ArtCardDetails from "./components/ArtCardDetails/ArtCardDetails";
import Layout from "./components/Layout/Layout";
import useCustomTheme from "./hooks/useCustomTheme";
import Auction from "./Pages/Auction/Auction";
import Explore from "./Pages/Explore/Explore";
import Favourites from "./Pages/Favourites/Favourites";
import TrendingSellers from "./Pages/TrendingSellers/TrendingSellers";
import Home from "./Pages/Home/Home";
import SellersDetails from "./Pages/SellerDetails/SellersDetails";
import Footer from "./components/Footer/Footer";
import UserProfile from "./Pages/UserProfile/UserProfile";
import TrendingCreators from "./Pages/TrendingCreators/TrendingCreators";
import ProfileInterface from "./components/ProfileInterface/ProfileInterface";
import KYCInterface from "./components/KYCInterface/KYCInterface";
import LanguageInterface from "./components/LanguageInterface/LanguageInterface";
import ThemeInterface from "./components/ThemeInterface/ThemeInterface";
import EditProfile from "./components/ProfileInterface/EditProfile";
import KYCPending from "./components/KYCInterface/KYCPending";
import KYCApproved from "./components/KYCInterface/KYCApproved";
import TermsAndCondition from "./Pages/Terms&Condition/TermsAndCondition";
import FAQ from "./Pages/FAQ/FAQ";
import PrivacyPolicy from "./Pages/PrivacyPolicy/PrivacyPolicy";
import ContactUs from "./Pages/ContactUs/ContactUs";
import CreateAssets from "./Pages/CreateAssets/CreateAssets";
import DummyUserProfile from "./Pages/DummyUser/DummyUserProfile";
import AuctionCardDetails from "./components/AuctionCardDetails/AuctionCardDetails";
import CreatorsDetails from "./Pages/CreatorsDetails/CreatorsDetails";
import AnimatedLoader from "./Utils/AnimatedLoader/AnimatedLoader";
import "./App.css";
function App() {
const [darkMode, setDarkMode] = useState(true);
const { customTheme } = useCustomTheme(darkMode);
const [showApp, setShowApp] = useState(false);
useEffect(() => {
const appLoader = setTimeout(() => setShowApp(true), 2000);
return () => clearTimeout(appLoader);
}, []);
const isMobile = useMediaQuery("(max-width:600px)");
const handleDarkThemeSwitch = () => {
localStorage.setItem("theme", "dark");
const theme = localStorage.getItem("theme");
if (theme === "dark") {
localStorage.setItem("theme", "dark");
setDarkMode(true);
}
};
const handleLightThemeSwitch = () => {
localStorage.setItem("theme", "light");
const theme = localStorage.getItem("theme");
if (theme === "light") {
localStorage.setItem("theme", "light");
setDarkMode(false);
}
};
useEffect(() => {
const theme = localStorage.getItem("theme");
if (theme === "dark") {
localStorage.setItem("theme", "dark");
setDarkMode(true);
} else if (theme === "light") {
localStorage.setItem("theme", "light");
setDarkMode(false);
}
}, [customTheme]);
return (
<ThemeProvider theme={customTheme}>
<div
style={{
backgroundColor: `${darkMode ? "#040404" : "#ffffff"}`,
height: "100vh",
}}
>
{showApp ? (
<Box bgcolor={darkMode ? "#040404" : "#ffffff"}>
<BrowserRouter>
<Layout darkMode={darkMode}>
<Routes>
<Route path="/" element={<Home darkMode={darkMode} />} />
<Route path="/home" element={<Home darkMode={darkMode} />} />
<Route
path="/explore"
element={<Explore darkMode={darkMode} />}
/>
<Route
path="/auction"
element={<Auction darkMode={darkMode} />}
/>
<Route
path="/explore/:id"
element={<ArtCardDetails darkMode={darkMode} />}
/>
<Route
path="/auction/:id"
element={<AuctionCardDetails darkMode={darkMode} />}
/>
<Route
path="/favourites"
element={<Favourites darkMode={darkMode} />}
/>
<Route
path="/trending-sellers"
element={<TrendingSellers darkMode={darkMode} />}
/>
<Route
path="/trending-sellers/:id"
element={<SellersDetails darkMode={darkMode} />}
/>
<Route
path="/user/dummy"
element={<DummyUserProfile darkMode={darkMode} />}
/>
<Route
path="/create-asset"
element={<CreateAssets darkMode={darkMode} />}
/>
<Route
path="/profile"
element={<UserProfile darkMode={darkMode} />}
>
<Route
path="user-profile"
element={<ProfileInterface darkMode={darkMode} />}
/>
<Route
path="edit-profile"
element={<EditProfile darkMode={darkMode} />}
/>
<Route
path="kyc"
element={<KYCInterface darkMode={darkMode} />}
/>
<Route
path="kyc-pending"
element={<KYCPending darkMode={darkMode} />}
/>
<Route
path="kyc-approved"
element={<KYCApproved darkMode={darkMode} />}
/>
<Route
path="language"
element={<LanguageInterface darkMode={darkMode} />}
/>
<Route
path="theme"
element={
<ThemeInterface
darkMode={darkMode}
setDarkMode={setDarkMode}
handleDarkThemeSwitch={handleDarkThemeSwitch}
handleLightThemeSwitch={handleLightThemeSwitch}
/>
}
/>
</Route>
<Route
path="/trending-creators"
element={<TrendingCreators darkMode={darkMode} />}
/>
<Route
path="/trending-creators/:id"
element={<CreatorsDetails darkMode={darkMode} />}
/>
<Route
path="/privacy-policy"
element={<PrivacyPolicy darkMode={darkMode} />}
/>
<Route
path="/terms-and-condition"
element={<TermsAndCondition darkMode={darkMode} />}
/>
<Route
path="/frequently-asked-questions"
element={<FAQ darkMode={darkMode} />}
/>
<Route
path="/contact-us"
element={<ContactUs darkMode={darkMode} />}
/>
</Routes>
{!isMobile && <Footer darkMode={darkMode} />}
</Layout>
</BrowserRouter>
</Box>
) : (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
}}
>
<AnimatedLoader />
</div>
)}
</div>
</ThemeProvider>
);
}
export default App;
The project has approximately 26
reusable components, 8
sub-components and 15
Pages. Besides, all the main hooks that were used in this project can be found on /src/hooks
folder and the utilities are situated in the /src/Utils
folder.
Here is the full src
folders and files tree view.
| App.css
| App.js
| index.css
| index.js
| reportWebVitals.js
|
+---assets
| | accepteeImg.png
| | backdropMobile.svg
| | BackgroundWrinkles1.svg
| | BackgroundWrinkles2.svg
| | backgroundWrinklesLight.svg
| | bidderImg.png
| | contact-us-writing.svg
| | darkUIPrev.svg
| | exploreBackDropCircle.svg
| | feamaleUser.png
| | femaleUser.svg
| | heroVectorLineDark.svg
| | heroVectorLineLight.svg
| | heroVectorMainDark.svg
| | heroVectorSecondaryOneDark.svg
| | heroVectorSecondaryTwoDark.svg
| | kycApprovedImg.svg
| | kycPendingImg.svg
| | lightUIPrev.svg
| | mainLogo.svg
| | mainLogoLight.svg
| | polygonInner.svg
| | polygonInnerMobile.svg
| | polygonOuter.svg
| | polygonOuterMobile.svg
| | sideFooterLogoDark.svg
| | sideFooterLogoLight.svg
| | sideNavigationShadow.svg
| | sideNavRef.png
| | StatHexaInner.svg
| | StatHexaOuter.svg
| | userProfileAvatar.png
| |
| +---Icons
| +---darkUIIcons
| | metaMaskIcon.svg
| | minuteDotIcon.svg
| |
| +---lightUIIcons
| metaMaskIcon.svg
|
+---components
| +---ArtCard
| | ArtCard.js
| | ArtCard.module.css
| |
| +---ArtCardContainer
| | AllArtCards.js
| | ArtArtCards.js
| | ArtCardContainer.js
| | MemesArtCards.js
| | MusicArtCards.js
| | PosterArtCards.js
| | SignatureArtCards.js
| |
| +---ArtCardDetails
| | ArtCardDetails.js
| | ArtCardDetails.module.css
| | biddingData.js
| | SingleArtWork.js
| | SingleArtWork.module.css
| | TabPanel.js
| |
| +---AssetPropertiesModal
| | AssetProperModal.js
| | AssetProperModal.module.css
| |
| +---AuctionCard
| | AuctionCard.js
| | AuctionCard.module.css
| | AuctionCardPrev.js
| | AuctionCardPrev.module.css
| |
| +---AuctionCardDetails
| | AuctionCardDetails.js
| | AuctionCardDetails.module.css
| | biddingData.js
| | SingleAuctionCard.js
| | SingleAuctionCard.module.css
| | TabPanel.js
| |
| +---BackDrop
| | BackDrop.js
| | Backdrop.module.css
| |
| +---ConnectWalletPopUp
| | ConnectWalletPopUp.js
| | PopUp.module.css
| |
| +---CountDownBoard
| | CountDownBoard.js
| |
| +---CreatorCard
| | CreatorCard.js
| |
| +---FavouriteCard
| | FavoriteCardPrev.js
| | FavouriteCard.css
| | FavouriteCard.js
| |
| +---FilterTab
| | FilterTab.js
| |
| +---Footer
| | Footer.js
| |
| +---KYCInterface
| | KYCApproved.js
| | KYCInterface.js
| | KYCPending.js
| |
| +---LanguageInterface
| | LanguageInterface.js
| |
| +---Layout
| | Layout.js
| |
| +---LiveAuctions
| | LiveAuctions.js
| |
| +---Navigation
| | MobileNavigation.js
| | Navigation.js
| |
| +---PastAuctions
| | PastAuctions.js
| |
| +---ProfileInterface
| | EditProfile.css
| | EditProfile.js
| | ProfileInterface.js
| |
| +---ProfileSideBar
| | ProfileSideBar.js
| |
| +---SellerDetailsCard
| | SellerDetailsCard.js
| |
| +---SellersCard
| | SellersCard.js
| | SellersCardPrev.js
| |
| +---SideBar
| | SideBar.js
| | SideDrawer.js
| |
| +---Skeletons
| | ArtCardFB.js
| | ArtCardHome.js
| | AuctionCardFB.js
| | AuctionCardHome.js
| | TrendingSellersFB.js
| | TrendingSellersHome.js
| |
| +---ThemeInterface
| ThemeInterface.css
| ThemeInterface.js
|
+---hooks
| useCountDown.js
| useCustomTheme.js
| useQuery.js
|
+---Pages
| +---Auction
| | Auction.js
| |
| +---ContactUs
| | ContactUs.js
| |
| +---CreateAssets
| | CreateAssets.js
| |
| +---CreatorsDetails
| | CreatorsDetails.js
| |
| +---DummyUser
| | DummyUserProfile.js
| |
| +---Explore
| | Explore.js
| |
| +---FAQ
| | FAQ.js
| |
| +---Favourites
| | AuctionBookmark.js
| | Favourites.js
| | NftBookmarks.js
| |
| +---Home
| | CardDeck.js
| | cardStyle.module.css
| | HeroBanner.js
| | heroBannerStyles.module.css
| | Home.js
| | LiveAuctionContainer.js
| | MobileCardDeck.js
| | TrendingNFTContainer.js
| | TrendingSellersContainer.js
| |
| +---PrivacyPolicy
| | PrivacyPolicy.js
| |
| +---SellerDetails
| | SellersDetails.js
| |
| +---Terms&Condition
| | TermsAndCondition.js
| |
| +---TrendingCreators
| | TrendingCreators.js
| |
| +---TrendingSellers
| | TrendingSellers.js
| |
| +---UserProfile
| UserProfile.js
|
+---Utils
+---AnimatedLoader
| AnimatedLoader.js
|
+---GradientButtons
| GradientButtons.js
|
+---StatsComponent
StatsComponent.js
statsComponent.module.css
As mentioned before the project has 26
components. Here is a briefing of all the components.
-
- First one is the
ArtCard.module.css
component folder. It has it own separateCSS
modules and all the styles excep the dynamic ones can be found on thatArtCard.module.css
file.
- First one is the
-
- This components holds all the
ArtCard
components. There are 7 differnt container for them.
- This components holds all the
-
- Every ArtCard from the explore section has a descriptive details about it and this component mainly holds the
SingleArtWork
component. Have a look at the functionality:
const ArtCardDetails = ({ darkMode }) => { const { id } = useParams(); // Read from the url const [artWorks, setArtWorks] = useState([]); useEffect(() => { axios.get("/artWorkData.json").then((res) => { setArtWorks(res.data); }); }, [id]); //TODO: After adding API endpoints, we do not need to filter // Filtering artwork by IDs const filteredArtWork = artWorks.filter((artWork) => artWork.id === id); return ( <Container className={styles.artCardDetailsContainer}> <Typography variant="h3" color="secondary.main"> {filteredArtWork.map((fa) => ( <SingleArtWork darkMode={darkMode} key={fa.id} fa={fa} /> ))} </Typography> </Container> ); };
- Every ArtCard from the explore section has a descriptive details about it and this component mainly holds the
-
AssetPropertiesModal Component
- This modal component will pop-up when you need to create your own assets in
/src/Pages/CreateAssets
Page. All the styles of the component except the dynamic styles can be found on itsmodule.css
folder.
- This modal component will pop-up when you need to create your own assets in
-
- Similar to ArtCard component, this cards are used in
/src/Pages/Auction
Page for showing auction items. Along with this component there is also a sub-component named AuctionCardPrev.js which is used in/src/Pages/Home
page for showing the Live Auctions.
- Similar to ArtCard component, this cards are used in
-
- This component is also very similar to the ArtCardDetails component, but it only holds the details for the auction items. It mainly holds the
SingleAuctionCard
sub-component.
- This component is also very similar to the ArtCardDetails component, but it only holds the details for the auction items. It mainly holds the
-
- It only holds the
backdrop
effect found in web view.
- It only holds the
-
- This modal will show if any user try to connect the crypto wallets.
-
- This component is used to show the
Auction
countdown on the Trending NFTs and the Auction Cards. It heavily depends on theuseCountDown Hook
for performing its functionality. We will talk more about it later on the Hooks section.
- This component is used to show the
-
- This component is basically the cards that were used to show the
Trending Creators
on the/src/Pages/TrendingCreators
page.
- This component is basically the cards that were used to show the
-
- This is the component card that were used to show the cards on
/src/Pages/Favourites
page. The styling are separated on its ownCSS
file. Besides, it has another sub-component called FavouriteCardPrev for showing the Trending NFTs on the/src/Pages/Home
page.
- This is the component card that were used to show the cards on
-
- This component was used to show the top tabs in
/src/Pages/Explore
page. This component has also a separate Menu item for filtering and sorting the items present in the Explore page.
- This component was used to show the top tabs in
-
- The Footer component is used to show the Footer. Although, it will not appear on small devices.
-
- This component was used in the settings of
/src/Pages/UserProfile
page for registering as a seller on the marketplace. It has2
other sub-component named KYCApproved and KYCPending which were used to show the the pending and approved status of that seller.
- This component was used in the settings of
-
- This component was used to switch between the Languages that we primarily support. This mainly manipulates and triggers the
i18Next
library to change the language of the entire application. Also, it gets the language changes data that stored in the browser'sCookie
storage and set it as the default value of theradio
buttons, so it doesn't break the change. Follow the below code snippet:
This function is used to get the cookie value.
const changeLang = () => { const localeLang = document.cookie; return localeLang.slice(8); };
After that it sets as the default value of the
radio
, like this:<RadioGroup aria-label="language" defaultValue="{changeLang}" name="language-button-group" ></RadioGroup>
Basically, the
changeLanguage(lang)
method fromi18next
triggered the changes. We will talk more about it in the Customization section. For now you can see something like this:<Radio onClick={() => i18next.changeLanguage("en")} <!-- "en" is changeable > />
- This component was used to switch between the Languages that we primarily support. This mainly manipulates and triggers the
-
- This component basically holds the main structure of the of the page. It holds the Navigation and SideBar, and it takes
children
props to show all other components that were used underneath. The whole [Layout] Component is used in/src/App.js
file.
- This component basically holds the main structure of the of the page. It holds the Navigation and SideBar, and it takes
-
- It was mainly used as a container for holding the AuctionCard component/s which are
live
- It was mainly used as a container for holding the AuctionCard component/s which are
-
- Similar to LiveAuction Component, used to show the AuctioCard which are
past
.
- Similar to LiveAuction Component, used to show the AuctioCard which are
-
- This component can be divided into two sub-component which are
Navigation.js
for the web view andMobileNavigation.js
for smaller devices. Although, theMobileNavigation
component is rely on theNavigation
component which is also the parent or container components that holds theMobileNavigation
. Besides, this component has some smaller components likeMenu
item for showing a drop-down menu on theConnect Wallet
button. The wallet connection happens here. Also, it holds the ConnectWalletPopUp Component.
- This component can be divided into two sub-component which are
-
- This component is used to show the user profile information altogether. It has another sub-component which is called EditProfile for editing the user profile information.
-
- This mainly act like a navigation panel situated on
/src/Pages/UserProfile
Setting page.
- This mainly act like a navigation panel situated on
-
- This reusable component is used mainly showing the tab panel content. It used in CreatorsDetails, SellersDetails, and DummyUserProfile
-
- This component is similar to the CreatorCard. However, it was used to show the
Trending Sellers
on the/src/Pages/TrendingSellers
page. It also has a sub-component named SellerCardPrev which showed on the/src/Pages/Home
page.
- This component is similar to the CreatorCard. However, it was used to show the
-
- This component has two sub-component. The main SideBar is used to show the sidebar all over the place of the application, and the SideDrawer is used to show a drawer like navigation for the smaller devices.
-
- This folders contains all the skeletons screen for ArtCard (both for
Home
andExplore
page), AuctionCard(both forHome
andAuction
page), TrendingSeller, TrendingCreator.
- This folders contains all the skeletons screen for ArtCard (both for
-
- This component in used to switch between the
light
anddark
theme. This also works like the Radio button in LanguageInterface by invoking a function like this:
const whatTheme = () => { const theme = localStorage.getItem("theme"); if (theme === "dark") { return "darkUI"; } else if (theme === "light") { return "lightUI"; } };
- This component in used to switch between the
As we mentioned there are 15
pages in this project. Find the brief description below:
Main Pages
-
This the main landing page of this marketplace application. It has
6
sub-components which are- CardDeck - For showing the draggable cards in the HeroBanner section.
- HeroBanner - Mainly a container where the banner and draggable CardDeck contain.
- LiveAuctionContainer - It mainly holds the AuctionCardPrev component.
- MobileCardDeck - It is the mobile version of above mentioned CardDeck.
- TrendingNFTContainer - It is the container that holds the FavouriteCardPrev component.
- TrendingSellersContainer - For showing the SellerCardPrev components.
-
- The Auction Page is a separate
route
and a container for holding all the AuctionCard Component.
- The Auction Page is a separate
-
- The Explore Page is a separate
route
mainly show all the ArtCard components.
- The Explore Page is a separate
-
- This is the user settings page with a separate
route
named/profile
. It mainly holds the ProfileSideBar components and theoutlets
.
- This is the user settings page with a separate
-
- Mainly a separate page that holds the all the information about a seller. This is a dynamic page with
route
and holds the SellerDetailsCard component.
- Mainly a separate page that holds the all the information about a seller. This is a dynamic page with
-
- Similar to SellerDetails and show the CreatorDetailsCard.
-
- A separate
route
only for the user, where a user can view his/her NFTs.
- A separate
-
Favourite Page - All the favourite cards of a user showed in this page. It mainly holds the FavouriteCard component.
-
TrendingSeller Page - It is a container for showing all the SellerCard components.
-
TrendingCreator Page - Similar to TrendingSeller Page. It is the container for showing all the CreatorCard components.
Other Pages
- CreateAsset Page - For creating NFT assets.
- ContactUs Page - A static page for contacting with the authority.
- FAQ Page
- PrivacyPolicy Page
- Terms&Condition Page
This project has 3
custom hooks. They are:
This hook has one purpose only, that is show the desired countdown on the AuctionCard item. It takes an argument with the parameter of futureDate
which can be passed during the use of this hook. The code is below:
export const useCountDown = (futureDate) => {
const [now, setNow] = useState(new Date());
useEffect(() => {
const interval = setInterval(() => {
setNow(new Date());
}, 1000);
return () => {
clearInterval(interval);
};
}, [futureDate]);
const isTimeUp = isBefore(futureDate, now);
if (isTimeUp) {
return { days: 0, hours: 0, minutes: 0, seconds: 0, isTimeUp };
}
let { days, hours, minutes, seconds } = intervalToDuration({
start: now,
end: futureDate,
});
return { days, hours, minutes, seconds, isTimeUp };
};
It is using the date-fns
library for handling the date-time.
This is the main theme hook for the entire project. We were used a custom theme color palette with custom FontFamily
other than Roboto that comes with the Material UI
by default.
It was used in the main App.js
file with a ThemeProvider
context from the @emotion-react
library.
const useCustomTheme = (darkMode) => {
const customTheme = createTheme(
darkMode
? {
palette: {
primary: {
main: "#121212",
},
secondary: {
main: "#ffffff",
},
accent: {
main: "#171C26",
},
pink: {
main: "#E552FF",
},
blue: {
main: "#01D4FA",
},
black: {
main: "#040404",
},
},
typography: {
fontFamily: "'Poppins', sans-serif",
fontWeightRegular: 400,
fontWeightMedium: 500,
fontWeightBold: 700,
},
}
: {
palette: {
background: {
default: "#ffffff",
},
primary: {
main: "#ffffff",
},
secondary: {
main: "#121212",
},
accent: {
main: "#ffffff",
},
pink: {
main: "#E552FF",
},
blue: {
main: "#01D4FA",
},
black: {
main: "#FFFFFF",
},
},
typography: {
fontFamily: "'Poppins', sans-serif",
fontWeightRegular: 400,
fontWeightMedium: 500,
fontWeightBold: 700,
},
}
);
return { customTheme };
};
This hook is relatively a utility hook for reading and manipulating the query parameters from the URL using the useMemo
hook from React and the useLocation
hook from React Router DOM.
const useQuery = () => {
const { search } = useLocation();
return useMemo(() => new URLSearchParams(search), [search]);
};
There are three different utility components which are AnimatedLoader, GradientButtons, and StatsComponent.
This component was used to show the animated loader for the entire application. It will show on the first entry on this app and if the user refreshes it. Mainly, this component is built by using the Anime.js library. This JavaScript library is a handy way to build simple animations with the SVGs.
The code:
const AnimatedLoader = () => {
useEffect(() => {
var pathEls = document.querySelectorAll("path");
for (var i = 0; i < pathEls.length; i++) {
var pathEl = pathEls[i];
var offset = anime.setDashoffset(pathEl);
pathEl.setAttribute("stroke-dashoffset", offset);
anime({
targets: pathEl,
strokeDashoffset: [offset, 0],
duration: anime.random(1000, 1500),
delay: function (el, i) {
return i * 250;
},
loop: true,
direction: "alternate",
easing: "easeInOutSine",
autoplay: true,
});
}
}, []);
return (
<svg>
<path/>
// All paths...
</svg>
)
Here it mainly targets all the svg path that with correspondent with the pathEl
variable and loops through it. The main functionality in done within the anime()
function. It takes an object of instructions like targets
duration
loop
direction
easing
delay
. The duration
and the delay
can also be used as function. Here we use the delay
dynamically by invoking a function.
function (el, i) {
return i * 250;
}
This is basically a styled component function that contains two different gradient buttons
. Mainly we used the styled components with the power of Material UI
by invoking the styled
from it.
This utility component was used to build the statistics shown on the Home page. [Anime.js] library is also used here. Mainly this component takes totalNFT
totalSeller
totalSold
as props. However, if there is no data passed through these props it will invoke its default values which are 80
30
50
to show the numbers in the stats.
There used two different function wrapped up with the useEffect
hook from React. These two different function takes inner path and outer path as the path variable and loops through it.
Take a look:
// Outer paths
useEffect(() => {
const outerPathEls = document.querySelectorAll(".outerPath");
// var pathEls = document.getElementsByClassName("path");
for (var i = 0; i < outerPathEls.length; i++) {
let pathEl = outerPathEls[i];
let offset = anime.setDashoffset(pathEl);
pathEl.setAttribute("stroke-dashoffset", offset);
anime({
targets: pathEl,
strokeDashoffset: [offset, 0],
duration: anime.random(1000, 1000),
delay: function (el, i) {
return i * 250;
},
loop: false,
direction: "alternate",
easing: "easeInOutSine",
autoplay: true,
});
}
}, []);
// Inner Paths
useEffect(() => {
const innerPathEls = document.querySelectorAll(".innerPath");
for (var i = 0; i < innerPathEls.length; i++) {
let pathEl = innerPathEls[i];
let offset = anime.setDashoffset(pathEl);
let x = 0.5;
let y = -0.5;
pathEl.setAttribute("stroke-dashoffset", offset);
anime({
targets: pathEl,
strokeDashoffset: [offset, 0],
duration: anime.random(1000, 3000),
delay: function (el, i) {
return i * 150;
},
loop: false,
direction: "alternate",
translateX: function () {
return x * 3;
},
translateY: function () {
return y * 3;
},
easing: "easeInOutSine",
autoplay: true,
});
}
}, []);
The Asset folder contains all the static image assets and icons that were used in this application.
The customization will cover the following topics
if you want to customize the look and feel of the application, go to the /src/hooks/useCustomTheme.js
folder. There are two different palette for dark
and light
UI. Have a look:
palette: {
primary: {
main: "#121212",
},
secondary: {
main: "#ffffff",
},
accent: {
main: "#171C26",
},
pink: {
main: "#E552FF",
},
blue: {
main: "#01D4FA",
},
black: {
main: "#040404",
},
},
This palette is for the dark UI.
- The
primary
color here is#121212
which mainly used for the background and the cards. - The
secondary
color here is#ffffff
which mainly used for the texts. - The
accent
color was used for thePapers
andCard
background color. - The
pink
andblue
were used in buttons and highlighter. - The
black
was used as another shade of theprimary
color.
Similarly, for the light UI, take a look at this palette.
palette: {
background: {
default: "#ffffff",
},
primary: {
main: "#ffffff",
},
secondary: {
main: "#121212",
},
accent: {
main: "#ffffff",
},
pink: {
main: "#E552FF",
},
blue: {
main: "#01D4FA",
},
black: {
main: "#FFFFFF",
},
},
All the above code is same as for the dark UI, here we just alter the color combination.
This application used the i18next
library for managing all the language related work that took place within the app. The language customization folder can be found in /public/locales
directory.
Go to /public/locales
directory and create new folder with its correspondent ISO 639-1 Code
. Let's say we are adding the Bengali
language. For this:
- Create a folder in this
/public/locales
directory namedbn
and create ajson
file named astranslation.json
. - Then go to
/public/locales/en/translation.json
and copy all the data into our newly createdtranslation.json
file located in/public/locales/bn
- Then re-add the correspondent keywords like this:
{ "NAV_HOME": "হোম", "NAV_EXPLORE": "খুঁজুন", "NAV_AUCTION": "নিলাম", "NAV_CREATE_ASSET": "অ্যাসেট তৈরি করুন", "NAV_CONNECT_WALLET": "ওয়ালেট কানেক্ট করুন", "SETTINGS_USER_PROFILE": "ইউজার প্রোফাইল", "SETTINGS_EDIT_PROFILE": "এডিট প্রোফাইল", "SETTINGS_KYC": "কেওয়াইসি", "SETTINGS_KYC_APPROVED": "কেওয়াইসি অনুমোদিত", "SETTINGS_KYC_PENDING": "কেওয়াইসি চলছে", "SETTINGS_LANGUAGE": "ভাষা", "SETTINGS_THEME": "থিম", "LIVE_AUCTIONS": "চলতি নিলামসমূহ", "PAST_AUCTIONS": "বিগত নিলামসমূহ", "FAVOURITES": "প্রিয়গুলো", "SETTINGS": "সেটিংস", "TOTAL_NFT": "সর্বমোট এনএফটি" }
- Then go to the
/src/index.js
file and add the language'sISO 639-1 Code
in that array like this:Here we also add the thesupportedLngs: ["en", "bn", "hi", "ta"],
ISO 639-1 Code
forHindi
andTamil
. - It mainly takes this below by using the i18Next Http Backend plugin.
That's it. now you can switch and view this with
backend: { loadPath: "/locales/{{lng}}/translation.json", },
Bengali
language.
Note: Here we just demonstrated the existing language translation. If you really want to add another language make sure you add the switcher in Language Interface component
So, by far we're done with the usage and the customization, now this application is good to deploy.
To deploy the local build in your machine, run:
yarn build #or npm run build
You can also view the live website by deploying it to Netlify. For this you can simply drag your build folder on Netlify.
But, if you like CI/CD and want to done everything automatically, you may want to use vercel
.
For deploying the project with vercel
without connecting to git
, first you have to install the vercel cli
, run:
yarn global add vercel
#or
npm i -g vercel
Then open your shell cd
to the project folder and run
vercel #all right, that's it!!
It will automatically minified and build the project and give you a live url where you can preview
the project.
If you want to go to production
build, simply run:
vercel --prod
Note: If you are using the vercel cli
for the first time, we recommend you to go through the vercel cli docs at once. It just makes your life easier.