Thrifty Wallet UI kit - Documentation
Thriftywallet is a ReactJs UI kit for Crypto Wallet ( Cryptocurrency), rewards points, and FIAT Currency. It can be used for multiple purposes.
It is built with a very professional color and font theme. It has neat & clean code with good documentation thus making it easy to customize. Thriftywallet supports light and dark themes, responsive for all screen sizes. Thriftywallet is a PWA Application that can be installed in the mobile or desktop and can be used as a native app.
It has 25+ Uniques screens & widgets, Amazing animations, PWA enabled, Light & Dark Theme supported. Using this Theme, one can create a Custodial Wallet( Delegated wallet) for Cryptocurrency, noncustodial wallet for Crypto coin, Wallet for Reward points(loyalty) , wallet for FIAT currency, and closed wallet.
- Version: 1.5
- Author: Thriftysoft
- Created: 01 March, 2022
- Updated: 21st July, 2023
Thank you for purchasing our theme. If you have any questions that are beyond the scope of this help file, please feel free to email us at admin@thriftysoft.tech. You can also contact us via Skype Thanks so much!
Features
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.
Main Tools
- Axios - For data fetching.
- React Router - For in app routing
- Firebase - For handling in app authentication.
- React Confetti - For showing celebration poppers
- React Copy To Clipboard - A small yet powerfull npm package for handling all the copying and pasting.
- Material Icons - Icon component library
- React Swipeable Views - This handles all the swipeable views for the touch devices.
- React Pin Field - For handling in app pin input functionality
- React Webcam - For scanning the qr codes
- React Use - A great utility libraries full of amazing hooks
Here is the list of all dependancies
"dependencies": {
"@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0",
"@mui/icons-material": "^5.3.1",
"@mui/lab": "^5.0.0-alpha.67",
"@mui/material": "^5.3.1",
"@soywod/pin-field": "^0.2.2",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@testing-library/user-event": "^13.2.1",
"add": "^2.0.6",
"axios": "^0.25.0",
"cra-template-pwa": "^1.0.3",
"date-fns": "^2.28.0",
"firebase": "^9.6.5",
"react": "^17.0.2",
"react-confetti": "^6.0.1",
"react-copy-to-clipboard": "^5.0.4",
"react-dom": "^17.0.2",
"react-icons": "^4.3.1",
"react-pin-field": "^2.0.0-beta.3",
"react-router-dom": "^6.2.1",
"react-scripts": "5.0.0",
"react-swipeable-views": "^0.14.0",
"react-use": "^17.3.2",
"react-webcam": "^6.0.1",
"web-vitals": "^2.1.0",
"yarn": "^1.22.17"
},
Core Features
- 25 + Unique Screens & Widgets
- Clean, neat, and well-documneted source code
- Reusable Code Components
- User-Friendly Interface
- Fully Responsive, Compatible with All Screen Size
- Major Browser Compatibility
- For multiple use cases ( Crypto Wallet, loyalty point wallet & Fiat Wallet)
- Built with React Js 17.0.2
- Dark and Light Theme
- PWA Enabled
- Clean Animation
- Support skeleton Screens
- Easy to customize colors
- Easy to customize font
- Material UI & CSS Lib
- Google Font Used
- Icon library - Material Icons
Getting Started
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.
Our recommendation is to use the YARN package manager.
npm install --global yarn
Then check the version with:
yarn --version
Installation
To complete the installation please follow the steps below:
-
First, change directory to the project folder by running:
cd thrifty-wallet-react-app
-
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.
How to use
So, the basic usage of the application is really simple. We divided this section with:
The initial index.html
and other publicly seen assets like dummy data can be found in the /public
directory.
Main App.js File
The App.js
file is the root of all the Pages and Components that will described below.
All the pages and components that were used in the main App.js file are lazy imported with its correspondent loader and they were wrapped with three Contexts Provider which are
- AuthProvider - For providing all the user centric authentication information all over the app
- ColoModeContext Provider - This provider mainly used for the changing the theme mode between dark and light. We will talk more about it in theme section
- ThemeProvider - This provider is the main provider for distributing all the color and theme information in the entire app
All the pages on this app except the authentication pages are being used with a private/protected route. So, we have to create an account first in order to have the full access in the app. It should be rememberred that, as this project is nothing but a UI kit only, we used minimal authentication with firebase, so it gives the feelings of real flow of an application. Nothing more, nothing less.
Folder Structure
The project contains 15
reusable and independent components with 10
sub-components and 12
Pages. Within these pages, approximately70
other components are presented. These components are mainly dependent on their correspondent pages.
Here is the full src
folders and files tree view
│ App.css
│ App.js
│ App.test.js
│ index.css
│ index.js
│ logo.svg
│ reportWebVitals.js
│ service-worker.js
│ serviceWorkerRegistration.js
│ setupTests.js
│
├───assets
│ │ bitCoinIcon.svg
│ │ bronzeBadgeIcon.svg
│ │ buyCryptoCurrencyCardImg.svg
│ │ buyCryptoCurrencyCardImgLight.svg
│ │ CardanoVectorLogo.svg
│ │ contactImageDark.svg
│ │ copyIconDark.svg
│ │ diamondBadgeIcon.svg
│ │ dominosPizza.svg
│ │ EthereumVectorLogo.svg
│ │ facebookFlatColorIcon.svg
│ │ goldBadgeIcon.svg
│ │ googleFlatColorIcon.svg
│ │ initialLoginRegistrationImage.png
│ │ kycImage.png
│ │ LiteCoinVectorLogo.svg
│ │ mainLogo.svg
│ │ moonIcon.svg
│ │ onboardingBGImage.svg
│ │ onboardingLogo.svg
│ │ onboardingStepFiveImage.svg
│ │ onboardingStepFourImage.svg
│ │ onboardingStepOneImage.svg
│ │ onboardingStepThreeImage.svg
│ │ onboardingStepTwoImage.svg
│ │ profileAvatar.svg
│ │ progressCheckIcon.svg
│ │ progressUnCheckIcon.svg
│ │ qrCode.svg
│ │ qrIconDark.svg
│ │ qrImageScanner.svg
│ │ receiveCopyIcon.svg
│ │ receiveQRIcon.svg
│ │ reusableCardLogo.svg
│ │ sendIconMobile.svg
│ │ signUpImageDark.svg
│ │ silverBadgeIcon.svg
│ │ stepperIconEmpty.svg
│ │ stepperIconPending.svg
│ │ sunIcon.svg
│ │ tableDetailsIcon.svg
│ │ totalFundValueImage.svg
│ │ totalFundValueImageLight.svg
│ │ twitterFlatColorIcon.svg
│ │ twoFAPinBG.svg
│ │ twoFAPinBGLight.svg
│ │
│ └───authenticationImages
│ accountSetupStep.svg
│ BankStepDark.svg
│ BankStepLight.svg
│ ForgotPassDark.svg
│ ForgotPassLight.svg
│ kycStep.svg
│ kycStepLight.svg
│ loginDark.svg
│ loginLight.svg
│ otpDark.svg
│ otpLight.svg
│ ResetPassDark.svg
│ ResetPassLight.svg
│ signUpDark.svg
│ SignUpLight.svg
│
├───components
│ ├───AuthProgress
│ │ AuthProgress.js
│ │ AuthProgress.module.css
│ │
│ ├───CustomStepper
│ │ CustomStepper.js
│ │
│ ├───CustomSwitch
│ │ CustomSwitch.js
│ │
│ ├───CustomToolTip
│ │ CustomToolTip.js
│ │
│ ├───DatePickerTextField
│ │ DatePickerTextField.js
│ │
│ ├───GrowwBar
│ │ GrowwBar.js
│ │
│ ├───InstallationModal
│ │ InstallationModal.js
│ │
│ ├───Layout
│ │ CustomAppbar.js
│ │ CustomDrawer.js
│ │ Layout.js
│ │ MobileNavDrawer.js
│ │ MobileNavDrawerPermanent.js
│ │ SettingsMenu.js
│ │
│ ├───LazyImageComponent
│ │ LazyImageComponent.js
│ │
│ ├───ProgressLoader
│ │ ComponentLoader.js
│ │ CustomProgress.js
│ │ LoaderStyle.module.css
│ │ ProgressLoader.js
│ │
│ ├───Routes
│ │ CoinDetailsRoutes.js
│ │ LayoutRoutes.js
│ │
│ ├───Skeletons
│ │ ComponentSkeletons.js
│ │
│ ├───StyledTable
│ │ StyledTable.js
│ │
│ ├───TableDetailsModal
│ │ TableDetailsModal.js
│ │ TableDetailsModal.module.css
│ │
│ └───TabPanel
│ TabPanel.js
│
├───contexts
│ AuthProvider.js
│
├───Firebase
│ firebase.config.js
│ firebase.init.js
│
├───hooks
│ useAuth.js
│ useFirebase.js
│
├───Pages
│ ├───AccountSetup
│ │ │ AccountSetup.js
│ │ │ AccountSetup.module.css
│ │ │
│ │ ├───AccountSetupStep
│ │ │ AccountSetupStep.js
│ │ │ AccountSetupStep.module.css
│ │ │
│ │ ├───BankStep
│ │ │ BankStep.js
│ │ │ BankStep.module.css
│ │ │
│ │ └───KYCStep
│ │ KYCStep.js
│ │ KYCStep.module.css
│ │
│ ├───CoinDetails
│ │ │ BalanceArea.js
│ │ │ CoinDetails.js
│ │ │ CoinDetailsMobile.js
│ │ │ CoinDetailsStyle.module.css
│ │ │ CoinFilterArea.js
│ │ │ CoinTransactionTable.js
│ │ │ CoinTransactionTableMobile.js
│ │ │ QRCodeScannerModal.js
│ │ │ SendConfirmationModal.js
│ │ │
│ │ └───CoinDetailsChildrenMobile
│ │ │ CoinDetailsChildrenMobile.module.css
│ │ │ ReceiveBoxMobile.js
│ │ │ SendBoxMobile.js
│ │ │ TransactionBoxMobile.js
│ │ │
│ │ └───TableCoinDetails
│ ├───CryptoWallet
│ │ │ CryptoWalletInterface.js
│ │ │
│ │ ├───CryptoWalletTopCards
│ │ │ CryptoWalletTopCards.js
│ │ │ CryptoWalletTopCards.module.css
│ │ │ CryptoWalletTopCardsMobile.js
│ │ │
│ │ ├───DataArea
│ │ │ TableArea.js
│ │ │ TableArea.module.css
│ │ │ TableAreaMobile.js
│ │ │ TransactionArea.js
│ │ │ TransactionAreaMobile.js
│ │ │
│ │ └───FundsAndTransferArea
│ │ FundsAndTransferArea.js
│ │ FundsAndTransferArea.module.css
│ │ FundsAndTransferAreaMobile.js
│ │
│ ├───FiatWallet
│ │ │ FiatWalletInterface.js
│ │ │
│ │ ├───CurrencyBalanceCard
│ │ │ CurrencyBalanceCard.js
│ │ │ CurrencyBalanceCard.module.css
│ │ │
│ │ ├───DepositFunds
│ │ │ DepositFunds.js
│ │ │ DepositFunds.module.css
│ │ │
│ │ ├───PaymentAuthorizationModal
│ │ │ PaymentAuthorizationModal.js
│ │ │ PaymentAuthorizationModal.module.css
│ │ │
│ │ ├───TransactionDetailsArea
│ │ │ FiatTableDetailsModal.js
│ │ │ TransactionDetailsArea.js
│ │ │ TransactionDetailsArea.module.css
│ │ │ TransactionDetailsAreaMobile.js
│ │ │
│ │ ├───TransactionDrawer
│ │ │ TransactionDrawer.js
│ │ │ TransactionDrawerMobile.js
│ │ │
│ │ └───WithdrawFunds
│ │ WithdrawFunds.js
│ │
│ ├───Login
│ │ │ Login.js
│ │ │ Login.module.css
│ │ │
│ │ ├───ForgotPass
│ │ │ ForgotPass.js
│ │ │ ForgotPass.module.css
│ │ │
│ │ ├───OTPVerification
│ │ │ OTPVerification.js
│ │ │ OTPVerification.module.css
│ │ │
│ │ ├───ResetPass
│ │ │ ResetPass.js
│ │ │ ResetPass.module.css
│ │ │
│ │ └───SignInInterface
│ │ SignInInterface.js
│ │ SignInInterface.module.css
│ │
│ ├───LoyaltyWallet
│ │ │ LoyaltyWalletInterface.js
│ │ │ LoyaltyWalletInterface.module.css
│ │ │
│ │ ├───ClaimRewardModal
│ │ │ ClaimRewardModal.js
│ │ │
│ │ ├───RewardPathArea
│ │ │ RewardPathArea.js
│ │ │ RewardPathArea.module.css
│ │ │ RewardPathAreaMobile.js
│ │ │
│ │ ├───RewardTabArea
│ │ │ AvailableRewards.js
│ │ │ AvailableRewardsMobile.js
│ │ │ MyRewards.js
│ │ │ MyRewardsMobile.js
│ │ │ RewardTabArea.js
│ │ │ RewardTabArea.module.css
│ │ │ Transaction.js
│ │ │
│ │ ├───TimeLineArea
│ │ │ TimeLineArea.js
│ │ │ TimeLineArea.module.css
│ │ │
│ │ └───TopCardArea
│ │ TopCardArea.js
│ │ TopCardArea.module.css
│ │ TopCardAreaMobile.js
│ │
│ ├───Onboarding
│ │ OnboardingPage.js
│ │ OnboardingPage.module.css
│ │ StepComponent.js
│ │
│ ├───ProfilePage
│ │ │ ProfileInteface.js
│ │ │
│ │ └───Account
│ │ │ Account.js
│ │ │ Account.module.css
│ │ │
│ │ ├───BankInfo
│ │ │ │ BankInfo.js
│ │ │ │
│ │ │ └───BankInfoModal
│ │ │ AddBankModal.js
│ │ │ AddBankModalMobile.js
│ │ │ BankInfoModal.js
│ │ │ BankInfoModalMobile.js
│ │ │
│ │ ├───KYCInfo
│ │ │ │ KYCInfo.js
│ │ │ │
│ │ │ └───KYCInfoModal
│ │ │ KYCInfoModal.js
│ │ │ KYCInfoModalMobile.js
│ │ │
│ │ ├───OtherOptions
│ │ │ ChangePasswordModal.js
│ │ │ ChangePasswordModalMobile.js
│ │ │ DeleteAccountModal.js
│ │ │ DeleteAccountModalMobile.js
│ │ │ OtherOptions.js
│ │ │
│ │ └───ProfileInfo
│ │ │ ProfileInfo.js
│ │ │
│ │ └───ProfileInfoModal
│ │ ProfileInfoModal.js
│ │ ProfileInfoModalMobile.js
│ │
│ ├───Registration
│ │ │ Registration.js
│ │ │ Registration.module.css
│ │ │
│ │ ├───SignUpInterface
│ │ │ SignUpInterface.js
│ │ │ SignUpInterface.module.css
│ │ │
│ │ └───TwoFAPage
│ │ │ TwoFAPage.js
│ │ │ TwoFAPin.js
│ │ │ TwoFAPin.module.css
│ │ │ TwoFAPopUp.module.css
│ │ │
│ │ └───TwoFAPinModal
│ │ TwoFAPinModal.js
│ │ TwoFAPinModal.module.css
│ │
│ ├───StaticPages
│ │ About.js
│ │ FAQ.js
│ │ PrivacyPolicy.js
│ │ StaticPageInterface.js
│ │ StaticPagesStyles.module.css
│ │ TermsAndCondition.js
│ │
│ ├───TopUpPage
│ │ │ TopUpPage.js
│ │ │ TopUpPage.module.css
│ │ │
│ │ ├───ProviderSelect
│ │ │ ProviderSelect.js
│ │ │
│ │ ├───TopUpAuthorization
│ │ │ TopUpAuthorization.js
│ │ │
│ │ └───TopUpCard
│ │ TopUpCard.js
│ │ TopUpCardMobile.js
│ │
│ └───Wallets
│ Wallets.js
│
├───Private
│ PrivateRoute.js
│
├───Theme
│ CustomTheme.js
│
└───Utilities
LightUIButtons.js
Lets's take a short look how we organized all the things there
-
All the styles of the components and pages can be found within their dedicated folder. Let's say, you are customizing a modal. Then you should be able to see a file like
modal.module.css
is presented on that folder. -
The
Components
folder only contains the independent and reusable components -
The
Pages
folder only contains the pages and their correspondent components -
All the custom Contexts, hooks, Private Route, Theme, and Utilities are presented on the
src/Contexts
,src/hooks
,src/Private
,src/Theme
, andsrc/Utilities
folder.
Components
As mentioned before the project has 15
independent and components and 10
sub-components. We described the key components below.
Layout Component
The Layout component is the most important component. This component basically holds the structure of the main page. It has 5 other sub-component which were used to construct the whole Layout Component.
<>
<Box sx={{ display: "flex" }}>
<CssBaseline />
<React.Suspense
fallback={
<Skeleton
variant="rectangular"
sx={{
background: `${
theme.palette.mode === "dark" ? "#111" : "#fff"
}`,
}}
width={"100%"}
height={80}
/>
}
>
<CustomAppbar
handleClickMenu={handleClickMenu}
handleDrawerToggle={handleDrawerToggle}
/>
</React.Suspense>
<Box
component="nav"
sx={{ width: { sm: drawerWidth }, flexShrink: { sm: 0 } }}
>
{/* Drawer for mobile */}
<SwipeableDrawer
anchor="left"
variant="temporary"
open={mobileOpen}
onClose={handleDrawerToggle}
onOpen={handleDrawerToggle}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
sx={{
display: { xs: "block", sm: "none" },
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: drawerWidth,
backgroundColor: "background.default",
border: "none",
},
}}
>
<React.Suspense fallback={<ComponentLoader / >}>
<CustomDrawer handleDrawerToggle={handleDrawerToggle} />
< /React.Suspense>
</SwipeableDrawer>
{/* Drawer for larger device */}
<Drawer
variant="permanent"
sx={{
display: { xs: "none", sm: "block" },
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: drawerWidth,
backgroundColor: `${
theme.palette.mode === "dark" ? "#1b1b1b" : "#ffffff"
}`,
border: "none",
},
}}
open
>
<React.Suspense fallback={<ComponentLoader />}>
<CustomDrawer />
</React.Suspense>
</Drawer>
</Box>
{/* Children */}
<Box
component="main"
sx={{
flexGrow: 1,
p: `${!isMobile ? 3 : 0}`,
width: { sm: `calc(100% - ${drawerWidth}px)` },
}}
>
<Toolbar />
{children}
</Box >
</Box >
<Box>
<React.Suspense
fallback={
<Skeleton
animation="wave"
variant="circular"
width={20}
height={20}
sx={{
backgroundColor: `${
theme.palette.mode === "dark" ? "#111" : "#f5f5f5"
}`,
}}
/>
}
>
<SettingsMenu
open={openMenu}
anchorEl={anchorElMenu}
handleClose={handleCloseMenu}
/>
</React.Suspense>
</Box>
</>
The child components are
- CustomAppbar which is the main appbar used throughout the application
- CustomDrawer is basically the navigation menu. This drawer is persistent in nature for the larger devices, means it is not closable. For the relatively smaller devices, this drawer becomes temporary, means it closable and swipeable
- MobileNavDrawer this mainly an instance of the modal for the mobile devices
- MobileNavDrawerpermanent is the drawer for in depth routing for the mobile devices
- SettigsMenu is the menu which can be accessable by clicking the Gear icon
Skeletons Component
This components contains all the reusable skeleton screens for the components that are presented in the application.
Some Skeleton screens are beingh described below
Card Image Skeleton
const CardImageSkeleton = () => {
return (
<Skeleton animation="wave" variant="rectangular" width={210} height={175} />
);
};
Table Skeleton
const TableSkeleton = () => {
return (
<React.Fragment>
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((fm) => (
<Skeleton
key={fm}
variant="text"
animation="wave"
width={"100%"}
height={80}
/>
))}
</React.Fragment>
);
};
Grid Card Skeleton
const GridCardSkeleton = ({
columns,
spacing,
gap = 0,
rowGap,
columnGap,
md,
xs = 1,
sm,
xl,
}) => {
return (
<Grid
container
mt={2}
columns={columns}
columnGap={columnGap}
spacing={spacing}
gap={gap}
rowGap={rowGap}
>
{[1, 2, 3, 4, 5, 6].map((index) => (
<Grid item key={index} md={md} xl={xl} sm={sm} xs={12}>
<Skeleton animation="wave" width={"100%"} height={250} />
</Grid>
))}
</Grid>
);
};
ProgressLoader Component
This holds all the loader components that are used in the application. It contains 3 loader which is
- ComponentLoader
- CustomProgress
- ProgressLoader
Component loader
<Box className={styles.loader} height={"100%"}>
<CircularProgress color="primary" />
</Box>
Custom Progress
export const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
height: 10,
borderRadius: 5,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.mode === "dark" ? "#777777" : "#c4c4c4",
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 5,
backgroundColor: "#58BD7D",
},
}));
export const BorderLinearProgressMobile = styled(LinearProgress)(
({ theme }) => ({
height: 5,
borderRadius: 5,
[`&.${linearProgressClasses.colorPrimary}`]: {
backgroundColor: theme.palette.mode === "dark" ? "#777777" : "#c4c4c4",
},
[`& .${linearProgressClasses.bar}`]: {
borderRadius: 5,
backgroundColor: "#58BD7D",
},
})
);
Progress Loader
<Box
bgcolor={theme.palette.background.default}
className={styles.loader}
height={"100vh"}
>
<CircularProgress color="primary" />
</Box>
Other Component
All other components are auxiliary in nature and very handy. Below we just described the gist
- Auth Progress - This loader component is liable for showing the loading phase when the user authenticating
- Custom Stepper - This component is used in
account-setup page
- Custom Switch - This is the theme switcher component
- CustomToolTip - This tooltip is customized and being used to show the tooltip.
- Date Picker Text Field - It is used to show the date field of the date input component
- Groww Bar - This small component is liable for showing the
grow
type animations in the auth pages. It only available for mobile devices - Installation Modal - This Snackbar type modal is used to tell the user that the application is installable as a PWA
- Lazy Image Component - This component only returns the
img
element. All the images throughout this application is used this component to dynamically load the images. It optimized for the performance - Routes - this actually is not a component rather it the storage for holding the
Layout
routes mainly andCoinDetailsRoute
. - StyledTable - This table component is kinda like an auxiliary one and is used to show all the data in the application
- Table Details Modal - This component is only available for mobile devices and used to show the
Pages
As we mentioned there are 12 pages in this project with 70 different dependent component with each page. Find the brief description below
We can also divide the pages into some core pages like
- Authentication Pages
- Account Setup Pages
- Wallet Pages
- Account Pages
- Static Pages
Authentication Pages
Authentication pages are the pages where the user land first
The Authentication Page can be divided into two main category which are Login and Registration
The Login page includes
- SignInInterface - It is mainly the sign in box
- ForgotPass - The Forgot password box
- ResetPass - Password Reset Box
- OTPVerification - It shows the OTP verification box
The Registration page includes
- SignUpInterface - It is mainly the sign up box
- TwoFAPage - It holds three sub-component regarding the two factor setup
Account Setup Page
The account setup page holds three component named
- Account Setup Step - Holds the card for setup user account initially
- KYC Step - Holds the card for setup Know Your Customer Form
- Bank Step - Used for setup bank details
Wallet Page
The wallet page can be considered as the main landing page of the app. It holds three different wallets, which we will describe shortly.
The wallets page is mainly a common place that holds the wallets. The three wallets are:
- Crypto Wallet
- Fiat Wallet
- Loyalty Wallet
Crypto Wallet
<Box>
<Box px={3}>
<Suspense fallback={ }>
{!isMobile ? (
<CryptoWalletTopCards />
) : (
<CryptoWalletTopCardsMobile />
)}
</Suspense>
</Box>
<Box px={3}>
<Suspense fallback={ }>
{!isMobile ? (
<FundsAndTransferArea />
) : (
<FundsAndTransferAreaMobile />
)}
</Suspense>
</Box>
</Box>
As we can see it holds two main components which are CryptoWalletTopCards
and FundsAndTransferArea
. You may notice there are dedicated component only for mobile device. It is not repeatation rather it is organized. The Crypto Wallet Top Cards holds the top level cards and the Funds And Transfer Area holds the transaction, coin details, and transaction details
Fiat Wallet
{/* Currency Card */}
<Box>
<Box px={3}>
<Suspense fallback={<CardSkeleton />}>
<CurrencyBalanceCard
deposit={handleDepositDrawer}
withdraw={handleWithdrawDrawer}
/>
</Suspense>
</Box>
{/* Transaction Area */}
<Box px={3} mt={5}>
<Suspense fallback={<TableSkeleton />}>
{!isMobile ? (
<TransactionDetailsArea />
) : (
<TransactionDetailsAreaMobile />
)}
</Suspense>
</Box>
The Fiat Wallet Interface basically holds two main child component which are CurrencyBalanceCard
and TransactionArea
. The Currency Balance Card holds two drawer type child component which are withdraw
and deposit
And the Transaction Details Area holds the details transaction informaition of the fiat wallet
Loyalty Wallet
<Box
sx={{ overflowX: "hidden" }}
px={!isMobile ? 3 : 0}
className={styles.mainBox}
>
{showConfetti && (
<Confetti
opacity={0.8}
gravity={0.1}
onConfettiComplete={() => setShowConfetti(false)}
tweenDuration={100}
numberOfPieces={250}
recycle={false}
width={width - 50}
height={height}
/>
)}
<Box className={styles.topCardArea}>
<Suspense fallback={<CardSkeleton />}>
{!isMobile ? (
<TopCardArea handleConfetti={handleConfetti} />
) : (
<TopCardAreaMobile handleConfetti={handleConfetti} />
)}
</Suspense>
</Box>
{!isMobile && (
<Box className={styles.rewardInfoArea}>
<Grid
container
columns={{ xs: 1, sm: 1, md: 12 }}
spacing={{ xs: 4, sm: 2, md: 4 }}
>
<Grid item xs={12} sm={12} md={4}>
<Box className={styles.rewardPathArea}>
<Suspense fallback={<ComponentSkeleton />}>
<RewardPathArea />
</Suspense>
</Box>
</Grid>
<Grid item xs={12} sm={12} md={8}>
<Box className={styles.rewardTabArea}>
<Suspense fallback={<ComponentSkeleton />}>
<RewardTabArea />
</Suspense>
</Box>
</Grid>
</Grid>
</Box>
)}
{isMobile && (
<Box>
<Box className={styles.rewardTabArea}>
<Suspense fallback={<ComponentSkeleton />}>
<RewardTabArea />
</Suspense>
</Box>
</Box>
)}
</Box>
Here you can see the Confetti
component. This component is used to show celebration type poppers when the user click on the reward button for claiming rewards presented in the TopCardArea
.
Mainly the component is consists of three main child component which are Top Card Area, Reward Path Area, Reward Tab Area
The Top Card Area is the place where the user can see their claimed reward and can claim the reward clicking on the Badges
The Reward Path Area is a component where the user can view the reward path that he was obtained.
The Reward Tab Area is a component where the user can view the coupon cards, claimed coupons, and the detailed transaction
Account Page
The account page although a child of the ProfileInterface
component which represent the main instance of this route and it also represent in the main App.js
file.
With the standing, the account page is the page that holds mainly four child component which are:
- Profile Info
- KYC Info
- Add Bank
- Other Options
Initial Account Page
<Box>
<Box>
<Suspense
fallback={<AccountCardSkeletons width={!isMobile ? "58%" : "100%"} />}
>
<ProfileInfo handleClickMenu={handleClickMenu} />
</Suspense>
</Box>
<Divider />
<Box>
<Suspense
fallback={<AccountCardSkeletons width={!isMobile ? "58%" : "100%"} />}
>
<BankInfo handleClickMenu={handleClickMenu} />
</Suspense>
</Box>
<Divider />
<Box>
<Suspense
fallback={<AccountCardSkeletons width={!isMobile ? "58%" : "100%"} />}
>
<KYCInfo handleClickMenu={handleClickMenu} />
</Suspense>
</Box>
<Divider />
<Box>
<Suspense
fallback={<AccountCardSkeletons width={!isMobile ? "58%" : "100%"} />}
>
<OtherOptions handleClickMenu={handleClickMenu} />
</Suspense>
</Box>
</Box>
As we can see the first child component is ProfileInfo
which holds the profile card for the user. It is responsible for changing the user information.
The next one is BankInfo
. Here a user can change the detailed banking information and can also add new bank
The third one is KYCInfo
. Here the user can re-submit his/her KYC form and can also know the status whether it is accepted or rejected.
Static Page
The static page holds the static page related to About
, FAQ
, PrivacyPolicy
, and TermsAndCondition
.
The StaticPageInterface
components holds the above mentioned pages.
The data that were displayed on those pages are coming from a separate json
file. You may be wondered why we are loading the static data from the json
files instead of writing them in the code! Well, this reduced the complexity and it is also organizable and it is easy to change or alter.
Other Pages
The other important pages worth mentioning are:
- Coin Details Page - It containes 6 different sub-components and it is being used withing the Crypto Wallet Component
- On Boarding Page - This component will show to the user only when the user is new and never created an account here before.
- Top Up Page - It is a huge separate page but accessable from the Crypto Wallet Page. It mainly the storage for all kinds of topup and transactions
Contexts
There mainly one context for now on the context folder which is called AuthProvider
. It is used to distribute the authentication data throughout the application.
Have a look
import { createContext } from "react";
import useFirebase from "../hooks/useFirebase";
export const AuthContext = createContext(null);
const AuthProvider = ({ children }) => {
const allAuthContext = useFirebase();
return (
<AuthContext.Provider value={allAuthContext}>
{children}
</AuthContext.Provider>
);
};
export default AuthProvider;
Firebase
This folder is the separate one and is fully dedicated regarding to the configurations of the application
This folder contains two file which are:
firebase.config.js
- All the app configurationsfirebase.init.js
- Initialized the project from here
Hooks
This project has 2
custom hooks. They are:
- useFirebase - All the firebase related hooks and function can be found here
-
useAuth - This hook is mainly a wrapper for storing all the functionality from the above mentioned
useFirebase
hook
useFirebase Hook
This hook has multiple functionality such as Sign In, Sign Up, Logout, showing Auth Errors. Besides, it is used to store the user information, loading state (which is kinda like a watcher, that is why you won't be logout automatically if the page reloads) and lastly showing the Authentication pin.
Here is some important functionality
Sign Up
// Sign Up
const registerUser = (email, password, navigate) => {
setIsLoading(true); //It will holds the loading state
// Registering the user
createUserWithEmailAndPassword(auth, email, password)
.then((userCreadential) => {
// setting auth error default false
setAuthError("");
// setup new user
const newUser = { email };
setUser(newUser);
navigate("/account-setup"); //navigate to account setup route
})
.catch((err) => {
console.log(err.message);
setAuthError(err.message);
})
.finally(() => setIsLoading(false)); // this will stop showing the initial loader
};
Sign In
// Sign In
const logInUser = (email, password, location, navigate) => {
// it will hold the loading state
setIsLoading(true);
setShowPin(false);
// Signing In the user
signInWithEmailAndPassword(auth, email, password)
.then((userCreadential) => {
const prevDestination = location?.state?.from || "/";
navigate(prevDestination); //It will navigate to homepage or the destination that the user came from
setAuthError("");
const showPinModal = setTimeout(() => {
setShowPin(true);
}, 5000);
return () => clearInterval(showPinModal);
})
.catch((err) => {
console.log(err.message);
setAuthError(err.message);
})
.finally(() => {
setIsLoading(false);
}); //
};
There is another important functionality which actually watch the period of subscribing and unsubscribing a user
Have a look
// Using these will hold the user even if the page is reloaded or refreshed
useEffect(() => {
const unsubscribed = onAuthStateChanged(auth, (user) => {
if (user) {
setUser(user);
} else {
setUser({});
}
setIsLoading(false);
});
return () => unsubscribed;
}, [auth]);
We can call it as the Authentication State Changing Watcher
useAuth Hook
This small hook basically act as an auxiliary hook for holding all the hooks from the useFirebase hook and return the data for the contexts
useAuth
const useAuth = () => {
const auth = useContext(AuthContext);
return auth;
};
Utilities
There is only one utility components which is a button
component for the light UI
const LightUIButtonPrimary = styled(Button)({
color: "#ffffff",
border: "none",
background: "linear-gradient(90deg, #F9E006 0%, #F8931A 100%)",
boxShadow: "none",
textTransform: "uppercase",
"&:hover": {
boxShadow: "none",
background: "linear-gradient(90deg, #F9E006 0%, #F8931A 100%)",
},
"&:active": {
boxShadow: "none",
background: "linear-gradient(90deg, #F9E006 100%, #F8931A 0%)",
},
"&:focus": {
background: "linear-gradient(90deg, #F9E006 0%, #F8931A 100%)",
},
});
Private
The Private
folder containes only the private route which is dependent on the Firebase and the useAuth
hook.
PrivateRoute
const PrivateRoute = ({ children }) => {
// hooks
const { user, isLoading } = useAuth();
const theme = useTheme();
const location = useLocation();
if (isLoading) {
return (
<Box
bgcolor={theme.palette.background.default}
sx={{
height: "100vh",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<Box>
<CircularProgress color="primary" />
</Box>
</Box>
);
}
if (user.email) {
return children;
} else {
return <Navigate to="/login/sign-in" state={{ from: location }} />
}
};
If we noticed, we can see it loads the user informations and the loading
state from the useAuth
hook and used a location hook from the React Router for using location.
Then it checks three conditions. First of all it checks if the page is loading or not. It the isLoading
becomes true then it shows an initial loader.
Note: It will always check the loading state
Notwithstanding, this component will check another condition, which is whether the email of the user is existing in the useAuth
hook or not. Then it simply execute one command at a time. If the user exists, then shows the children
which is the children props that we taking and showed it. If the user is not existing on the AuthenticationContext
then it simply redirect the user from his/her current location to the authentication page.
Theme
The theme contains all the theme related data and a toggler
for switching between the dark and light theme
Only theme
const theme = useMemo(
() =>
createTheme({
palette: {
mode,
...(mode === "light"
? {
// palette values for light mode
primary: {
main: "#F9E006",
},
secondary: {
main: "#111111",
},
accent: {
main: "#6C63FF",
},
orange: {
main: "#FF5722",
},
error: {
main: "#FF003D",
dark: "#FF003F",
},
success: {
main: "#8DD070",
dark: "#58BD7D",
},
background: {
default: "#fdfdfd",
paper: "#fbfbfb",
surface: "#ffffff",
card: "#f8f8f8",
primary: "#F9E006",
},
text: {
primary: "#111111",
secondary: "#434547",
tertiary: "#F9E006",
success: "#58BD7D",
orange: "#FF9100",
},
common: {
black: "#111111",
white: "#ffffff",
},
}
: {
// palette values for dark mode
primary: {
main: "#F9E006",
},
secondary: {
main: "#FFFFFF",
},
accent: {
main: "#6C63FF",
},
orange: {
main: "#FF5722",
},
error: {
main: "#FF003D",
dark: "#FF003F",
},
success: {
main: "#8DD070",
dark: "#58BD7D",
},
background: {
default: "#111111",
paper: "#252628",
surface: "#2B2B2B",
card: "#1B1B1B",
primary: "#F9E006",
},
text: {
primary: "#ffffff",
secondary: "#C4C4C4",
tertiary: "#F9E006",
success: "#58BD7D",
orange: "#FF9100",
},
common: {
black: "#111111",
white: "#ffffff",
},
}),
},
typography: {
fontFamily: "'Poppins', sans-serif",
fontWeightLight: 300,
fontWeightRegular: 400,
fontWeightMedium: 500,
fontWeightBold: 700,
},
}),
[mode]
);
Here we mainly use the theme with the useMemo
hook for optimizing the performance. Then we call createTheme
function from the Material UI and send the mode
as a state. So, that we can easily manipulate the theme color based on the manipulation of the mode
state.
Now, let's take a look at the toggler function
const colorMode = useMemo(
() => ({
toggleColorMode: () => {
setMode((prevMode) => (prevMode === "light" ? "dark" : "light"));
},
}),
[]
);
Here, we also use the useMemo
hook from the React. This memoized function simply alter the previous mode of the theme and change it accordingly.
Note: You can change the theme from anywhere you like and you can also get this from the ColoModeContext
which is situated in the main App file.
By now, you maybe wondering that how we invoked this function. For this you have to go to the the main App.js
file where you can find a small one liner context on top of the App
function.
// Color Context
export const ColorModeContext = createContext({ toggleColorMode: () => {} });
It is basically create the toggler context which we later invoked within the App
function like this
const { theme, colorMode } = CustomTheme();
// First destructuring the colorMode from our CustomTheme function
// Now we're setting the provider as a wrapper for all the pages and components
<ColorModeContext.Provider value={colorMode}>
// ... our awesome pages and components
</ColoModeContext.Provider>
By using this contexts we are good to use the toggler switch from anywhere, like this
const colorMode = useContext(ColoModeContext);
return (
<div>
<CustomSwitch
defaultChecked
onChange={colorMode.toggleColorMode}
/>
</div>
)
This switch is now responsible for switching the color theme in all over the place.
Always try to use the theme from the useTheme
hook from the Material UI . Otherwise the switching and theme changing will not work as expected.
Progressive Web App
The PWA is enabled for this application. Here what you should know:
In our application we are using service-workers
for enabling the feature. You can also disable it by navigating to the index.js
file where you will find something like this
serviceWorkerRegistration.register();
// Just remove the register() method to unregister()
//for disable the pwa
For this PWA we are using two main functions which are responsible for this feature
service-worker
serviceWorkerRegistration
For this project as it is nothing but a UI kit we were used some boilerplate code. But for production grade we do not encourage to use the codes.
But you can generate one by running this below command
yarn add cra-template-pwa
It will generate all the starter code that you need to configure a pwa!
You have to remember that, you need to configure the manifest.json
file according to your need in order to run the PWA.
Assets
The Asset folder contains all the static image assets and icons that were used in this application.
Customization
The customization will cover the following topics
Theme Customization
if you want to customize the look and feel of the application, go to the /src/CustomTheme.js
folder. There are two different palette for dark
and light
UI.
Have a look:
{
// palette values for dark mode
primary: {
main: "#F9E006",
},
secondary: {
main: "#FFFFFF",
},
accent: {
main: "#6C63FF",
},
orange: {
main: "#FF5722",
},
error: {
main: "#FF003D",
dark: "#FF003F",
},
success: {
main: "#8DD070",
dark: "#58BD7D",
},
background: {
default: "#111111",
paper: "#252628",
surface: "#2B2B2B",
card: "#1B1B1B",
primary: "#F9E006",
},
text: {
primary: "#ffffff",
secondary: "#C4C4C4",
tertiary: "#F9E006",
success: "#58BD7D",
orange: "#FF9100",
},
common: {
black: "#111111",
white: "#ffffff",
},
}
This palette is for the Dark
UI.
Let's break this!
-
The primary color is
#F9E006
which is mainly used inTitles
,Buttons
and in theLoaders
-
The
secondary
color here is#FFFFFF
which mainly used in the icons, some title text which are not primary. -
The accent color
is only used in the Reward Path Area component
- The color mentioned in
orage
is mainly used to show some accented color other than the whole accent color -
The
Error
andSuccess
color is used to show errors and successes in the application. -
Next the
background
object is used to tell the Material UI to alter it's default background colors.- The
default
is the default background used throughout the application - The
paper
is the default background for all the cards heavily used in auth pages. - The
surface
was not heavily used on the cards component rather it was used to show some shades for thepaper
- The
card
is used mainly on the wallets pages
- The
-
The
text
object is mainly used to show texts -
The
common
object holds black and white value which is useful where we just want to use black and white color ranther than hardcoded on the code.
Similarly, for the light UI, take a look at this palette.
{
// palette values for light mode
primary: {
main: "#F9E006",
},
secondary: {
main: "#111111",
},
accent: {
main: "#6C63FF",
},
orange: {
main: "#FF5722",
},
error: {
main: "#FF003D",
dark: "#FF003F",
},
success: {
main: "#8DD070",
dark: "#58BD7D",
},
background: {
default: "#fdfdfd",
paper: "#fbfbfb",
surface: "#ffffff",
card: "#f8f8f8",
primary: "#F9E006",
},
text: {
primary: "#111111",
secondary: "#434547",
tertiary: "#F9E006",
success: "#58BD7D",
orange: "#FF9100",
},
common: {
black: "#111111",
white: "#ffffff",
},
}
Here we just alter the colors for the light UI
For the typography you can refer this
typography: {
fontFamily: "'Poppins', sans-serif",
fontWeightLight: 300,
fontWeightRegular: 400,
fontWeightMedium: 500,
fontWeightBold: 700,
},
Authentication Customization
This application is using the firebase authentication and you can find all the firebase related configurations in src/Firebase/firebase.config.js
.
Keep in mind that, you have to add your own firebase configurations as the value we are using is coming from the .env.local
file.
Have a look
const firebaseConfig = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_FIREBASE_APP_ID,
measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID,
};
Let's add a new configurations!
For adding firebase in your project you have to follow these below steps:
- Go to theFirebase and login or sign up with your google account
- Then, go to the Console and click on Add Project.
- Then name your project like this
-
Then click on continue and now you can see something like this. Disable the Google Analytics for now and click to
Continue
. This will take you to the project dashboard. - Now, click on the web icon where the mouse pointer is.
-
After finished loading you'll see a public facing name. Enter any name you like and click on
Register
-
Here under the
firebaseConfig
function you'll see the configurations that are needed for your project. Copy them replace the Configuration function in/src/Firebase/firebase.config.js
with your copied code. - Now, click on continue to console.
As we are used sign in and sign up functionality from firebase so it will be great if you also enable the sign in methods. You can find them on the left side menu in Project Dashboard.
Now click on the Add New Provider and select native provider which is Email/Password
We are recommending to use environmental
variables to store the values of the configurations. Just like we did.
Deployment
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 command line and want to done everything automatically, you may want to use vercel
cli tool.
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
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.
Changelog
See what's new added, changed, fixed, improved or updated in the latest versions.
Version 1.0 (01 March, 2022)
Initial Release
Version 1.1 (19 March, 2022)
- Major UI Fixes
- PWA Bug Fixes
- Responsive Issue Fixes
Version 1.2 (07 July, 2022)
- App Title, Description & Logo Easily Configurable
- All Routes Moved to Separated File NavigationRoutes.js
- Fixed Typo Issues
- Fixed PWA Issues
Version 1.3 (13 Oct, 2022)
- Added pointer for verify button in Deposit popup ( FIAT Wallet)
- Date picker issue in FIAT Wallet
- Autocomplete Issue in date picker in FIAT Wallet
Version 1.4 (04 Feb, 2023)
- Showing Message - Incase the user has not setup the Firebase
- 2FA Login Screen UI Fix
Version 1.5 (21 Jul, 2023)
- Removed unwanted logs from the console
- 2FA model fixed in account section
- Updated MUI deprecated element: datepicker & listitem, button
- Logo redirection to home page : Added,
- Removed Yarn old dependency
For any query & customization, reach out us at admin@thriftysoft.tech