├── .editorconfig ├── .env ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── babel.config.js ├── config-overrides.js ├── config ├── routesConfig.js └── testingSetupConfig.js ├── jest.config.js ├── jsconfig.json ├── package-lock.json ├── plopfile.js ├── public ├── favicon.ico ├── images │ ├── rashgarothplate.png │ └── wallets │ │ ├── metamask.png │ │ ├── torus.svg │ │ └── walletconnect.png ├── index.html ├── logo192.png ├── logo512.png └── manifest.json ├── src ├── App.js ├── __test__ │ ├── __snapshots__ │ │ └── app.test.js.snap │ └── app.test.js ├── components │ ├── Button │ │ └── index.js │ ├── ConnectWalletModal │ │ └── index.js │ ├── Loadable │ │ ├── __test__ │ │ │ └── Loadable.test.js │ │ └── index.js │ ├── Loader │ │ ├── __test__ │ │ │ └── Loader.test.js │ │ └── index.js │ ├── ModalWrapper │ │ └── index.js │ ├── Navbar │ │ ├── Bottom │ │ │ └── index.js │ │ ├── Drawer │ │ │ └── index.js │ │ ├── __test__ │ │ │ └── Navbar.test.js │ │ └── index.js │ ├── Seo │ │ ├── __test__ │ │ │ └── Seo.test.js │ │ └── index.js │ └── index.js ├── config.js ├── containers │ ├── Dashboard │ │ ├── index.js │ │ ├── store │ │ │ ├── actions.js │ │ │ ├── constants.js │ │ │ └── reducer.js │ │ └── views │ │ │ └── index.js │ └── Home │ │ ├── index.js │ │ ├── store │ │ ├── actions.js │ │ ├── constants.js │ │ └── reducer.js │ │ └── views │ │ └── index.js ├── hooks │ ├── useConnectWallet.js │ ├── useDebounce.js │ ├── useFormFields.js │ ├── useLayout.js │ ├── useModals.js │ └── useScript.js ├── index.js ├── providers │ ├── Metamask.js │ └── WalletConnect.js ├── reportWebVitals.js ├── routes │ ├── MainRoute.js │ └── routes.js ├── serviceWorker.js ├── store │ ├── globalReducer │ │ ├── actions.js │ │ ├── constant.js │ │ └── reducer.js │ ├── reducers.js │ └── redux.js ├── styles │ ├── base │ │ ├── font.scss │ │ └── theme.scss │ └── global │ │ ├── App.css │ │ └── index.css ├── themes │ ├── index.js │ ├── overrides.js │ ├── pallete.js │ ├── shapes.js │ └── typography.js └── utilities │ ├── core │ ├── localStorage.js │ ├── process.js │ └── string.js │ ├── network │ ├── endpoints.js │ ├── headers.js │ └── network.js │ └── web3 │ └── chains.js └── templates ├── Component.hbs ├── Page.hbs ├── Style.hbs ├── actions.hbs ├── componentTest.hbs ├── constants.hbs ├── exportPage.hbs ├── reducer.hbs └── test.hbs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | REACT_APP_PHASES=dev -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Don't check auto-generated stuff into git 2 | .cache 3 | node_modules 4 | coverage/ 5 | 6 | # Cruft 7 | .DS_Store 8 | npm-debug.log 9 | dist/ 10 | config/ -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | jest: true, 6 | }, 7 | extends: ['eslint:recommended', 'plugin:react/recommended', 'airbnb', 'plugin:prettier/recommended'], 8 | parser: '@babel/eslint-parser', 9 | parserOptions: { 10 | ecmaFeatures: { 11 | jsx: true, 12 | }, 13 | ecmaVersion: 'latest', 14 | sourceType: 'module', 15 | }, 16 | settings: { 17 | 'import/resolver': { 18 | node: { 19 | extensions: ['.js', '.jsx', '.ts', '.tsx'], 20 | moduleDirectory: ['node_modules', 'src/'], 21 | }, 22 | }, 23 | }, 24 | plugins: ['react'], 25 | rules: { 26 | 'react/react-in-jsx-scope': 0, 27 | 'no-unused-vars': 1, 28 | 'react/no-unknown-property': 0, 29 | 'no-console': 1, 30 | 'react/prop-types': 1, 31 | 'no-undef': 1, 32 | 'react/require-default-props': 0, 33 | 'no-async-promise-executor': 1, 34 | 'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }], 35 | 'prettier/prettier': [ 36 | 'error', 37 | { 38 | trailingComma: 'es5', 39 | semi: true, 40 | jsxSingleQuote: true, 41 | singleQuote: true, 42 | useTabs: false, 43 | tabWidth: 2, 44 | bracketSpacing: true, 45 | endOfLine: 'lf', 46 | tabSize: 2, 47 | }, 48 | ], 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.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 | package-lock.json 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "singleQuote": true, 4 | "jsxBracketSameLine": true, 5 | "trailingComma": "none", 6 | "printWidth": 120, 7 | "semi": true, 8 | "endOfLine": "lf" 9 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "compile-hero.disable-compile-files-on-did-save-code": false, 3 | "search.exclude": { 4 | "**/node_modules": true 5 | }, 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": true 8 | }, 9 | "eslint.validate": ["javascript"], 10 | "eslint.alwaysShowStatus": true 11 | } 12 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@babel/preset-env', { targets: { node: 'current' } }], 4 | ['@babel/preset-react', { runtime: 'automatic' }], 5 | ], 6 | }; 7 | -------------------------------------------------------------------------------- /config-overrides.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | /* eslint-disable import/no-extraneous-dependencies */ 3 | const webpack = require('webpack'); 4 | 5 | module.exports = function override(config) { 6 | const fallback = config.resolve.fallback || {}; 7 | Object.assign(fallback, { 8 | crypto: require.resolve('crypto-browserify'), 9 | stream: require.resolve('stream-browserify'), 10 | assert: require.resolve('assert'), 11 | http: require.resolve('stream-http'), 12 | https: require.resolve('https-browserify'), 13 | os: require.resolve('os-browserify'), 14 | url: require.resolve('url'), 15 | }); 16 | config.resolve.fallback = fallback; 17 | config.plugins = (config.plugins || []).concat([ 18 | new webpack.ProvidePlugin({ 19 | process: 'process/browser', 20 | Buffer: ['buffer', 'Buffer'], 21 | }), 22 | ]); 23 | return config; 24 | }; 25 | -------------------------------------------------------------------------------- /config/routesConfig.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | // basename: only at build time to set, and Don't add '/' at end off BASENAME for breadcrumbs, also Don't put only '/' use blank('') instead, 3 | // like '/default' 4 | basename: '', 5 | defaultPath: '', 6 | fontFamily: `'Roboto', sans-serif`, 7 | borderRadius: 12 8 | }; 9 | 10 | export default config; -------------------------------------------------------------------------------- /config/testingSetupConfig.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | import '@testing-library/dom'; 7 | import '@testing-library/react'; 8 | import '@testing-library/user-event'; -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property, visit: 3 | * https://jestjs.io/docs/configuration 4 | */ 5 | 6 | module.exports = { 7 | // All imported modules in your tests should be mocked automatically 8 | // automock: false, 9 | 10 | // Stop running tests after `n` failures 11 | // bail: 0, 12 | 13 | // The directory where Jest should store its cached dependency information 14 | // cacheDirectory: "/private/var/folders/08/28fmqw3n7y519n7wqgd_rcjr0000gp/T/jest_dy", 15 | 16 | // Automatically clear mock calls, instances, contexts and results before every test 17 | clearMocks: true, 18 | 19 | // Indicates whether the coverage information should be collected while executing the test 20 | collectCoverage: true, 21 | 22 | // An array of glob patterns indicating a set of files for which coverage information should be collected 23 | // collectCoverageFrom: undefined, 24 | 25 | // The directory where Jest should output its coverage files 26 | coverageDirectory: 'coverage', 27 | 28 | // An array of regexp pattern strings used to skip coverage collection 29 | coveragePathIgnorePatterns: ['/node_modules/'], 30 | 31 | // Indicates which provider should be used to instrument code for coverage 32 | coverageProvider: 'babel', 33 | 34 | // A list of reporter names that Jest uses when writing coverage reports 35 | // coverageReporters: [ 36 | // "json", 37 | // "text", 38 | // "lcov", 39 | // "clover" 40 | // ], 41 | 42 | // An object that configures minimum threshold enforcement for coverage results 43 | // coverageThreshold: undefined, 44 | 45 | // A path to a custom dependency extractor 46 | // dependencyExtractor: undefined, 47 | 48 | // Make calling deprecated APIs throw helpful error messages 49 | // errorOnDeprecated: false, 50 | 51 | // The default configuration for fake timers 52 | // fakeTimers: { 53 | // "enableGlobally": false 54 | // }, 55 | 56 | // Force coverage collection from ignored files using an array of glob patterns 57 | // forceCoverageMatch: [], 58 | 59 | // A path to a module which exports an async function that is triggered once before all test suites 60 | // globalSetup: undefined, 61 | 62 | // A path to a module which exports an async function that is triggered once after all test suites 63 | // globalTeardown: undefined, 64 | 65 | // A set of global variables that need to be available in all test environments 66 | // globals: {}, 67 | 68 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 69 | // maxWorkers: "50%", 70 | 71 | // An array of directory names to be searched recursively up from the requiring module's location 72 | moduleDirectories: ['node_modules', 'src'], 73 | 74 | // An array of file extensions your modules use 75 | moduleFileExtensions: ['js', 'mjs', 'cjs', 'jsx', 'ts', 'tsx', 'json', 'node'], 76 | 77 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module 78 | // moduleNameMapper: {}, 79 | 80 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 81 | // modulePathIgnorePatterns: [], 82 | 83 | // Activates notifications for test results 84 | // notify: false, 85 | 86 | // An enum that specifies notification mode. Requires { notify: true } 87 | // notifyMode: "failure-change", 88 | 89 | // A preset that is used as a base for Jest's configuration 90 | // preset: undefined, 91 | 92 | // Run tests from one or more projects 93 | // projects: undefined, 94 | 95 | // Use this configuration option to add custom reporters to Jest 96 | // reporters: undefined, 97 | 98 | // Automatically reset mock state before every test 99 | // resetMocks: false, 100 | 101 | // Reset the module registry before running each individual test 102 | // resetModules: false, 103 | 104 | // A path to a custom resolver 105 | // resolver: undefined, 106 | 107 | // Automatically restore mock state and implementation before every test 108 | // restoreMocks: false, 109 | 110 | // The root directory that Jest should scan for tests and modules within 111 | // rootDir: undefined, 112 | 113 | // A list of paths to directories that Jest should use to search for files in 114 | roots: ['/src'], 115 | 116 | // Allows you to use a custom runner instead of Jest's default test runner 117 | // runner: "jest-runner", 118 | 119 | // The paths to modules that run some code to configure or set up the testing environment before each test 120 | setupFiles: ['/config/testingSetupConfig.js'], 121 | 122 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 123 | // setupFilesAfterEnv: [], 124 | 125 | // The number of seconds after which a test is considered as slow and reported as such in the results. 126 | // slowTestThreshold: 5, 127 | 128 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 129 | // snapshotSerializers: [], 130 | 131 | // The test environment that will be used for testing 132 | testEnvironment: 'jsdom', 133 | 134 | // Options that will be passed to the testEnvironment 135 | // testEnvironmentOptions: {}, 136 | 137 | // Adds a location field to test results 138 | // testLocationInResults: false, 139 | 140 | // The glob patterns Jest uses to detect test files 141 | testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'], 142 | 143 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 144 | // testPathIgnorePatterns: [ 145 | // "/node_modules/" 146 | // ], 147 | 148 | // The regexp pattern or array of patterns that Jest uses to detect test files 149 | // testRegex: 'tests/.*\\.test\\.js$', 150 | 151 | // This option allows the use of a custom results processor 152 | // testResultsProcessor: undefined, 153 | 154 | // This option allows use of a custom test runner 155 | // testRunner: "jest-circus/runner", 156 | 157 | // A map from regular expressions to paths to transformers 158 | // transform: undefined, 159 | 160 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 161 | // transformIgnorePatterns: [ 162 | // "/node_modules/", 163 | // "\\.pnp\\.[^\\/]+$" 164 | // ], 165 | 166 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 167 | // unmockedModulePathPatterns: undefined, 168 | 169 | // Indicates whether each individual test should be reported during the run 170 | // verbose: undefined, 171 | 172 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 173 | // watchPathIgnorePatterns: [], 174 | 175 | // Whether to use watchman for file crawling 176 | // watchman: true, 177 | }; 178 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "src" 4 | }, 5 | "include": ["src"] 6 | } -------------------------------------------------------------------------------- /plopfile.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable func-names */ 2 | module.exports = function (plop) { 3 | // paths 4 | const path = 'src/containers/{{classname}}'; 5 | const reducerPath = 'src/containers/{{classname}}/store'; 6 | const viewsPath = 'src/containers/{{classname}}/views'; 7 | const testPath = 'src/containers/{{classname}}/__test__/{{classname}}.test.js'; 8 | const componentPath = 'src/components/{{componentname}}/index.js'; 9 | const componentTestPath = 'src/components/{{componentname}}/__test__/{{componentname}}.test.js'; 10 | // block generator 11 | plop.setGenerator('Create New Pages', { 12 | description: 'Generate class for pages', 13 | prompts: [ 14 | { 15 | type: 'input', 16 | name: 'classname', 17 | message: 'Your Class Name', 18 | }, 19 | { 20 | type: 'input', 21 | name: 'reducer', 22 | message: 'Your Reducer Name', 23 | }, 24 | ], 25 | actions: [ 26 | // import file 27 | { 28 | type: 'add', 29 | path: `${path}/index.js`, 30 | templateFile: 'templates/exportPage.hbs', 31 | }, 32 | { 33 | type: 'add', 34 | path: `${reducerPath}/actions.js`, 35 | templateFile: 'templates/actions.hbs', 36 | }, 37 | { 38 | type: 'add', 39 | path: `${reducerPath}/constants.js`, 40 | templateFile: 'templates/constants.hbs', 41 | }, 42 | { 43 | type: 'add', 44 | path: `${reducerPath}/reducer.js`, 45 | templateFile: 'templates/reducer.hbs', 46 | }, 47 | { 48 | type: 'add', 49 | path: `${viewsPath}/{{classname}}.scss`, 50 | templateFile: 'templates/Style.hbs', 51 | }, 52 | { 53 | type: 'add', 54 | path: `${viewsPath}/index.js`, 55 | templateFile: 'templates/Page.hbs', 56 | }, 57 | { 58 | type: 'add', 59 | path: `${testPath}`, 60 | templateFile: 'templates/test.hbs', 61 | }, 62 | { 63 | path: 'src/store/reducers.js', 64 | pattern: /(\/\/ REDUCERIMPORT)/g, 65 | template: `import {{reducer}} from 'containers/{{classname}}/store/reducer';\n$1`, 66 | type: 'modify', 67 | }, 68 | { 69 | path: 'src/store/reducers.js', 70 | pattern: /(\/\/ COMBINEREDUCER)/g, 71 | template: `{{reducer}},\n$1`, 72 | type: 'modify', 73 | }, 74 | ], 75 | }); 76 | plop.setGenerator('Create New Components', { 77 | description: 'Generate class for pages', 78 | prompts: [ 79 | { 80 | type: 'input', 81 | name: 'componentname', 82 | message: 'Your Component Name', 83 | }, 84 | ], 85 | actions: [ 86 | { 87 | type: 'add', 88 | path: `${componentPath}`, 89 | templateFile: 'templates/Component.hbs', 90 | }, 91 | { 92 | type: 'add', 93 | path: `${componentTestPath}`, 94 | templateFile: 'templates/componentTest.hbs', 95 | }, 96 | { 97 | path: 'src/components/index.js', 98 | pattern: /(\/\/ IMPORTCOMPONENT)/g, 99 | template: `import {{componentname}}Component from './{{componentname}}';\n$1`, 100 | type: 'modify', 101 | }, 102 | { 103 | path: 'src/components/index.js', 104 | pattern: /(\/\/ EXPORTCOMPONENT)/g, 105 | template: `{{componentname}}Component,\n$1`, 106 | type: 'modify', 107 | }, 108 | ], 109 | }); 110 | }; 111 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/public/favicon.ico -------------------------------------------------------------------------------- /public/images/rashgarothplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/public/images/rashgarothplate.png -------------------------------------------------------------------------------- /public/images/wallets/metamask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/public/images/wallets/metamask.png -------------------------------------------------------------------------------- /public/images/wallets/torus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/images/wallets/walletconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/public/images/wallets/walletconnect.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Rashboiler 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import './styles/global/App.css'; 2 | import RenderRoutes from 'routes/routes'; 3 | import { StyledEngineProvider, CssBaseline, ThemeProvider } from '@mui/material'; 4 | import { useSelector } from 'react-redux'; 5 | // eslint-disable-next-line import/no-named-as-default 6 | import theme from 'themes'; 7 | import { NavbarComponent } from 'components'; 8 | import { Web3ReactProvider } from '@web3-react/core'; 9 | import Web3 from 'web3'; 10 | 11 | function App() { 12 | const globalState = useSelector(state => state.globalReducer); 13 | const getLibrary = provider => { 14 | const library = new Web3(provider); 15 | return library; 16 | }; 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | 31 | export default App; 32 | -------------------------------------------------------------------------------- /src/__test__/__snapshots__/app.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Describe App.js Tests 1`] = ` 4 |
7 |

8 | Unit Testing! 9 |

10 |
11 | `; 12 | 13 | exports[`Describe App.js Tests 2`] = ` 14 |
17 |

18 | Unit Testing! 19 |

20 |
21 | `; 22 | 23 | exports[`Describe App.js Tests 3`] = ` 24 |
27 |

28 | Unit Testing! 29 |

30 |
31 | `; 32 | -------------------------------------------------------------------------------- /src/__test__/app.test.js: -------------------------------------------------------------------------------- 1 | import renderer from 'react-test-renderer'; 2 | import App from '../App'; 3 | 4 | it('Describe App.js Tests', () => { 5 | const component = renderer.create(); 6 | let tree = component.toJSON(); 7 | expect(tree).toMatchSnapshot(); 8 | // re-rendering 9 | tree = component.toJSON(); 10 | expect(tree).toMatchSnapshot(); 11 | // re-rendering 12 | tree = component.toJSON(); 13 | expect(tree).toMatchSnapshot(); 14 | }); 15 | -------------------------------------------------------------------------------- /src/components/Button/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/jsx-props-no-spreading */ 2 | import React from 'react'; 3 | import PropTypes from 'prop-types'; 4 | import { makeStyles } from '@mui/styles'; 5 | import { Button } from '@mui/material'; 6 | 7 | const useStyles = makeStyles(_theme => { 8 | return { 9 | root: { 10 | backgroundColor: props => props.bgColor || _theme.palette.primary[800], 11 | minHeight: _theme.shape.button.size.medium, 12 | borderRadius: _theme.shape.button.large, 13 | boxShadow: _theme.shape.button.large, 14 | }, 15 | }; 16 | }); 17 | 18 | function ButtonComponent({ variant = 'contained', title = 'Click Me!', ...other }) { 19 | const classes = useStyles(other); 20 | return 189 | ))} 190 | 191 | {/* desktop */} 192 | {!active ? ( 193 | 194 | 195 | 196 | Connect Wallet 197 | 198 | 199 | 200 | ) : ( 201 | 202 | 207 | 208 | disconnect 209 | 210 | 211 | 212 | )} 213 | {/* mobile */} 214 | 215 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | {children} 229 | 230 | 231 | 232 | 233 | 234 | ); 235 | } 236 | 237 | NavbarComponent.propTypes = { 238 | children: PropTypes.node, 239 | }; 240 | 241 | export default NavbarComponent; 242 | -------------------------------------------------------------------------------- /src/components/Seo/__test__/Seo.test.js: -------------------------------------------------------------------------------- 1 | //use your unit testing 2 | import renderer from 'react-test-renderer'; 3 | import SeoComponent from '../index'; 4 | 5 | it('Describe Seo.js Tests', () => { 6 | const component = renderer.create(); 7 | let tree = component.toJSON(); 8 | expect(tree).toMatchSnapshot(); 9 | // re-rendering 10 | tree = component.toJSON(); 11 | expect(tree).toMatchSnapshot(); 12 | // re-rendering 13 | tree = component.toJSON(); 14 | expect(tree).toMatchSnapshot(); 15 | }); 16 | -------------------------------------------------------------------------------- /src/components/Seo/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/require-default-props */ 2 | import React from 'react'; 3 | import PropTypes from 'prop-types'; 4 | import { Helmet } from 'react-helmet'; 5 | import { resolvePageUrl } from 'utilities/core/string'; 6 | import config from '../../config'; 7 | 8 | function SeoComponent({ title, description, path = '/', lang, keywords = 'Rashgaroth', contentType, imageUrl, meta }) { 9 | const metaKeywords = keywords && keywords.length > 0 ? { name: 'keywords', content: keywords.join(', ') } : []; 10 | const pageUrl = resolvePageUrl(config.siteUrl, config.basename, path); 11 | return ( 12 | 43 | ); 44 | } 45 | 46 | SeoComponent.propTypes = { 47 | title: PropTypes.string, 48 | description: PropTypes.string, 49 | path: PropTypes.string, 50 | lang: PropTypes.string, 51 | keywords: PropTypes.string, 52 | contentType: PropTypes.string, 53 | imageUrl: PropTypes.string, 54 | meta: PropTypes.string, 55 | }; 56 | 57 | export default SeoComponent; 58 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import SeoComponent from './Seo'; 3 | import NavbarComponent from './Navbar'; 4 | import LoaderComponent from './Loader'; 5 | import LoadableComponent from './Loadable'; 6 | import ModalWrapper from './ModalWrapper'; 7 | import ConnectWalletModal from './ConnectWalletModal'; 8 | // IMPORTCOMPONENT 9 | 10 | export { 11 | SeoComponent, 12 | NavbarComponent, 13 | LoaderComponent, 14 | LoadableComponent, 15 | // EXPORTCOMPONENT 16 | ModalWrapper, 17 | ConnectWalletModal, 18 | }; 19 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | // basename: only at build time to set, and Don't add '/' at end off BASENAME for breadcrumbs, also Don't put only '/' use blank('') instead, 3 | // like '/default' 4 | basename: '', 5 | siteUrl: 'http://localhost:3000', 6 | defaultPath: '', 7 | fontFamily: `'Poppins', sans-serif`, 8 | borderRadius: 12, 9 | siteTitle: 'Rashgaroth dApps Boilerplate!', 10 | }; 11 | 12 | export default config; 13 | -------------------------------------------------------------------------------- /src/containers/Dashboard/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import Dashboard from './views'; 3 | 4 | export default Dashboard; 5 | -------------------------------------------------------------------------------- /src/containers/Dashboard/store/actions.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/src/containers/Dashboard/store/actions.js -------------------------------------------------------------------------------- /src/containers/Dashboard/store/constants.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/src/containers/Dashboard/store/constants.js -------------------------------------------------------------------------------- /src/containers/Dashboard/store/reducer.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/src/containers/Dashboard/store/reducer.js -------------------------------------------------------------------------------- /src/containers/Dashboard/views/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | function Dashboard(props) { 5 | return
Dashboard Pages!!
; 6 | } 7 | 8 | Dashboard.propTypes = {}; 9 | 10 | export default Dashboard; 11 | -------------------------------------------------------------------------------- /src/containers/Home/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import Home from './views'; 3 | 4 | export default Home; 5 | -------------------------------------------------------------------------------- /src/containers/Home/store/actions.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/src/containers/Home/store/actions.js -------------------------------------------------------------------------------- /src/containers/Home/store/constants.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/src/containers/Home/store/constants.js -------------------------------------------------------------------------------- /src/containers/Home/store/reducer.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/src/containers/Home/store/reducer.js -------------------------------------------------------------------------------- /src/containers/Home/views/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | import { styled } from '@mui/styles'; 4 | import { Grid, Paper, Stack, Typography } from '@mui/material'; 5 | import { toSmallUnit, truncateWalletAddress } from 'utilities/core/string'; 6 | 7 | const Item = styled(Paper)(({ theme }) => ({ 8 | textAlign: 'center', 9 | color: theme.palette.text.primary, 10 | padding: 10, 11 | width: 200, 12 | height: 'auto', 13 | backgroundColor: '#FFF', 14 | })); 15 | 16 | function Home() { 17 | const selector = useSelector(state => state.globalReducer); 18 | const { wallet } = selector; 19 | const { account, balance } = wallet; 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | Account 27 | 28 | {account !== '' ? ( 29 | {truncateWalletAddress(account, 6)} 30 | ) : ( 31 | 32 | Please Connect Your Wallet 33 | 34 | )} 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | BNB Balance 43 | 44 | {balance !== 0 ? ( 45 | {`${toSmallUnit(balance, 18)} BNB`} 46 | ) : ( 47 | 48 | Please Connect Your Wallet 49 | 50 | )} 51 | 52 | 53 | 54 | 55 | ); 56 | } 57 | 58 | Home.propTypes = {}; 59 | 60 | export default Home; 61 | -------------------------------------------------------------------------------- /src/hooks/useConnectWallet.js: -------------------------------------------------------------------------------- 1 | import { useWeb3React } from '@web3-react/core'; 2 | import metamaskConnector from 'providers/Metamask'; 3 | import walletconnectConnector from 'providers/WalletConnect'; 4 | import { useEffect, useMemo, useState } from 'react'; 5 | import Web3 from 'web3'; 6 | 7 | export default function useConnectWallet() { 8 | const { activate, library, chainId, account, active, deactivate, setError } = useWeb3React(); 9 | const [balance, setBalance] = useState(0); 10 | const init = async () => { 11 | try { 12 | activate(metamaskConnector); 13 | } catch (error) { 14 | setError(error); 15 | } 16 | }; 17 | 18 | const getBalance = async () => { 19 | try { 20 | const web3 = new Web3(library); 21 | const accBalance = await web3.eth.getBalance(account); 22 | setBalance(accBalance); 23 | } catch (err) { 24 | console.log(err, '@error'); 25 | } 26 | }; 27 | 28 | useEffect(() => { 29 | if (account === '' || account === undefined) { 30 | init(); 31 | } 32 | }, [null]); 33 | 34 | useEffect(() => { 35 | if (account !== '' && active) { 36 | getBalance(); 37 | } 38 | }, [account]); 39 | 40 | const connectToMetamask = async () => { 41 | try { 42 | activate(metamaskConnector); 43 | } catch (error) { 44 | setError(error); 45 | } 46 | }; 47 | 48 | const connectToWalletConnect = async () => { 49 | try { 50 | activate(walletconnectConnector); 51 | } catch (error) { 52 | setError(error); 53 | } 54 | }; 55 | 56 | const disconnect = async () => { 57 | try { 58 | deactivate(); 59 | } catch (error) { 60 | setError(error); 61 | } 62 | }; 63 | 64 | if (active) { 65 | return useMemo( 66 | () => ({ 67 | library, 68 | chainId, 69 | account, 70 | active, 71 | connectToMetamask, 72 | connectToWalletConnect, 73 | disconnect, 74 | balance, 75 | }), 76 | [active, account, balance] 77 | ); 78 | } 79 | return useMemo( 80 | () => ({ 81 | library: null, 82 | chainId: 0, 83 | account: '', 84 | active: false, 85 | connectToMetamask, 86 | connectToWalletConnect, 87 | disconnect, 88 | balance, 89 | }), 90 | [active, account, balance] 91 | ); 92 | } 93 | -------------------------------------------------------------------------------- /src/hooks/useDebounce.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable default-param-last */ 2 | import { useEffect, useState } from 'react'; 3 | 4 | const useDebounce = (initialValue = '', delay) => { 5 | const [actualValue, setActualValue] = useState(initialValue); 6 | const [debounceValue, setDebounceValue] = useState(initialValue); 7 | useEffect(() => { 8 | const debounceId = setTimeout(() => setDebounceValue(actualValue), delay); 9 | return () => clearTimeout(debounceId); 10 | }, [actualValue, delay]); 11 | return [debounceValue, setActualValue]; 12 | }; 13 | 14 | export default useDebounce; 15 | -------------------------------------------------------------------------------- /src/hooks/useFormFields.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | const useFormFields = initialState => { 4 | const [fields, setValues] = useState(initialState); 5 | const reset = () => setValues(initialState); 6 | const set = val => setValues(val); 7 | 8 | return { 9 | fields, 10 | handleFieldsChange: event => { 11 | setValues({ 12 | ...fields, 13 | [event.target.id]: event.target.value, 14 | }); 15 | }, 16 | reset, 17 | set, 18 | }; 19 | }; 20 | 21 | export default useFormFields; 22 | -------------------------------------------------------------------------------- /src/hooks/useLayout.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | const useLayout = () => { 4 | const [isMobile, setIsMobile] = useState(false); 5 | const userAgent = typeof navigator === 'undefined' ? 'SSR' : navigator.userAgent; 6 | useEffect(() => { 7 | setIsMobile(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent)); 8 | }, [userAgent]); 9 | 10 | return { 11 | isMobile, 12 | }; 13 | }; 14 | 15 | export default useLayout; 16 | -------------------------------------------------------------------------------- /src/hooks/useModals.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | const useModal = () => { 4 | const [isShowing, setIsShowing] = useState(false); 5 | // for error modal 6 | const [errMsg, setErrMsg] = useState(''); 7 | const [titleMsg, setTitleMsg] = useState(''); 8 | // for loader 9 | const [isLoading, setIsLoading] = useState(false); 10 | 11 | const toggle = () => { 12 | setIsShowing(!isShowing); 13 | }; 14 | 15 | const setLoader = load => { 16 | setIsLoading(load); 17 | }; 18 | 19 | const setModalMessage = (title = 'Some Error Occured', desc = '') => { 20 | setErrMsg(desc); 21 | setTitleMsg(title); 22 | }; 23 | 24 | return { 25 | isShowing, 26 | toggle, 27 | errMsg, 28 | titleMsg, 29 | setModalMessage, 30 | isLoading, 31 | setLoader, 32 | }; 33 | }; 34 | 35 | export default useModal; 36 | -------------------------------------------------------------------------------- /src/hooks/useScript.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | 3 | // ==============================|| ELEMENT REFERENCE HOOKS ||============================== // 4 | 5 | const useScriptRef = () => { 6 | const scripted = useRef(true); 7 | 8 | useEffect( 9 | () => () => { 10 | scripted.current = false; 11 | }, 12 | [] 13 | ); 14 | 15 | return scripted; 16 | }; 17 | 18 | export default useScriptRef; 19 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './styles/global/index.css'; 4 | import { BrowserRouter } from 'react-router-dom'; 5 | import { Provider } from 'react-redux'; 6 | import { store } from 'store/redux'; 7 | import App from './App'; 8 | import reportWebVitals from './reportWebVitals'; 9 | import * as serviceWorker from './serviceWorker'; 10 | 11 | const root = ReactDOM.createRoot(document.getElementById('root')); 12 | root.render( 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | 20 | // If you want to start measuring performance in your app, pass a function 21 | // to log results (for example: reportWebVitals(console.log)) 22 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 23 | reportWebVitals(); 24 | serviceWorker.unregister(); 25 | -------------------------------------------------------------------------------- /src/providers/Metamask.js: -------------------------------------------------------------------------------- 1 | import { InjectedConnector } from '@web3-react/injected-connector'; 2 | 3 | const metamaskConnector = new InjectedConnector({ supportedChainIds: [56, 97, 1, 3, 4, 5] }); 4 | export default metamaskConnector; 5 | -------------------------------------------------------------------------------- /src/providers/WalletConnect.js: -------------------------------------------------------------------------------- 1 | import { WalletConnectConnector } from '@web3-react/walletconnect-connector'; 2 | import { WCCHAIN } from 'utilities/web3/chains'; 3 | 4 | const walletconnectConnector = new WalletConnectConnector({ 5 | qrcode: true, 6 | rpc: WCCHAIN, 7 | bridge: 'https://bridge.walletconnect.org', 8 | }); 9 | 10 | export default walletconnectConnector; 11 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/routes/MainRoute.js: -------------------------------------------------------------------------------- 1 | import Loadable from 'components/Loadable'; 2 | import { lazy } from 'react'; 3 | 4 | const DashboardPage = Loadable(lazy(() => import('containers/Dashboard'))); 5 | const HomePage = Loadable(lazy(() => import('containers/Home'))); 6 | 7 | const MAIN_ROUTES = [ 8 | { 9 | path: '/', 10 | element: , 11 | children: [], 12 | }, 13 | { 14 | path: '/dashboard', 15 | element: , 16 | children: [], 17 | }, 18 | ]; 19 | 20 | export default MAIN_ROUTES; 21 | -------------------------------------------------------------------------------- /src/routes/routes.js: -------------------------------------------------------------------------------- 1 | import { useRoutes } from 'react-router-dom'; 2 | import config from '../config'; 3 | import MAIN_ROUTES from './MainRoute'; 4 | 5 | export default function RenderRoutes() { 6 | return useRoutes([...MAIN_ROUTES], config.basename); 7 | } 8 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | // This optional code is used to register a service worker. 3 | // register() is not called by default. 4 | 5 | // This lets the app load faster on subsequent visits in production, and gives 6 | // it offline capabilities. However, it also means that developers (and users) 7 | // will only see deployed updates on subsequent visits to a page, after all the 8 | // existing tabs open on the page have been closed, since previously cached 9 | // resources are updated in the background. 10 | 11 | // To learn more about the benefits of this model and instructions on how to 12 | // opt-in, read https://bit.ly/CRA-PWA 13 | 14 | const isLocalhost = Boolean( 15 | window.location.hostname === 'localhost' || 16 | // [::1] is the IPv6 localhost address. 17 | window.location.hostname === '[::1]' || 18 | // 127.0.0.0/8 are considered localhost for IPv4. 19 | window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) 20 | ); 21 | 22 | function registerValidSW(swUrl, config) { 23 | navigator.serviceWorker 24 | .register(swUrl) 25 | .then(registration => { 26 | // eslint-disable-next-line no-param-reassign 27 | registration.onupdatefound = () => { 28 | const installingWorker = registration.installing; 29 | if (installingWorker == null) { 30 | return; 31 | } 32 | installingWorker.onstatechange = () => { 33 | if (installingWorker.state === 'installed') { 34 | if (navigator.serviceWorker.controller) { 35 | // At this point, the updated precached content has been fetched, 36 | // but the previous service worker will still serve the older 37 | // content until all client tabs are closed. 38 | console.log( 39 | 'New content is available and will be used when all tabs for this page are closed. See https://bit.ly/CRA-PWA.' 40 | ); 41 | 42 | // Execute callback 43 | if (config && config.onUpdate) { 44 | config.onUpdate(registration); 45 | } 46 | } else { 47 | // At this point, everything has been precached. 48 | // It's the perfect time to display a 49 | // "Content is cached for offline use." message. 50 | console.log('Content is cached for offline use.'); 51 | 52 | // Execute callback 53 | if (config && config.onSuccess) { 54 | config.onSuccess(registration); 55 | } 56 | } 57 | } 58 | }; 59 | }; 60 | }) 61 | .catch(error => { 62 | console.error('Error during service worker registration:', error); 63 | }); 64 | } 65 | 66 | function checkValidServiceWorker(swUrl, config) { 67 | // Check if the service worker can be found. If it can't reload the page. 68 | fetch(swUrl, { 69 | headers: { 'Service-Worker': 'script' }, 70 | }) 71 | .then(response => { 72 | // Ensure service worker exists, and that we really are getting a JS file. 73 | const contentType = response.headers.get('content-type'); 74 | if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) { 75 | // No service worker found. Probably a different app. Reload the page. 76 | navigator.serviceWorker.ready.then(registration => { 77 | registration.unregister().then(() => { 78 | window.location.reload(); 79 | }); 80 | }); 81 | } else { 82 | // Service worker found. Proceed as normal. 83 | registerValidSW(swUrl, config); 84 | } 85 | }) 86 | .catch(() => { 87 | console.log('No internet connection found. App is running in offline mode.'); 88 | }); 89 | } 90 | 91 | export function register(config) { 92 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 93 | // The URL constructor is available in all browsers that support SW. 94 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 95 | if (publicUrl.origin !== window.location.origin) { 96 | // Our service worker won't work if PUBLIC_URL is on a different origin 97 | // from what our page is served on. This might happen if a CDN is used to 98 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 99 | return; 100 | } 101 | 102 | window.addEventListener('load', () => { 103 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 104 | 105 | if (isLocalhost) { 106 | // This is running on localhost. Let's check if a service worker still exists or not. 107 | checkValidServiceWorker(swUrl, config); 108 | 109 | // Add some additional logging to localhost, pointing developers to the 110 | // service worker/PWA documentation. 111 | navigator.serviceWorker.ready.then(() => { 112 | console.log( 113 | 'This web app is being served cache-first by a service worker. To learn more, visit https://bit.ly/CRA-PWA' 114 | ); 115 | }); 116 | } else { 117 | // Is not localhost. Just register service worker 118 | registerValidSW(swUrl, config); 119 | } 120 | }); 121 | } 122 | } 123 | 124 | export function unregister() { 125 | if ('serviceWorker' in navigator) { 126 | navigator.serviceWorker.ready 127 | .then(registration => { 128 | registration.unregister(); 129 | }) 130 | .catch(error => { 131 | console.error(error.message); 132 | }); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/store/globalReducer/actions.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { SET_ACCOUNT } from './constant'; 3 | 4 | export const setWallet = (account, balance, wallet) => ({ 5 | type: SET_ACCOUNT, 6 | balance, 7 | account, 8 | wallet, 9 | }); 10 | -------------------------------------------------------------------------------- /src/store/globalReducer/constant.js: -------------------------------------------------------------------------------- 1 | // action - customization reducer 2 | export const SET_MENU = '@customization/SET_MENU'; 3 | export const MENU_TOGGLE = '@customization/MENU_TOGGLE'; 4 | export const MENU_OPEN = '@customization/MENU_OPEN'; 5 | export const SET_FONT_FAMILY = '@customization/SET_FONT_FAMILY'; 6 | export const SET_BORDER_RADIUS = '@customization/SET_BORDER_RADIUS'; 7 | export const SEARCH_VISIBILITY = '@customization/SEARCH_VISIBILITY'; 8 | export const SELECT_EVENT_VISIBILITY = '@customization/SELECT_EVENT_VISIBILITY'; 9 | export const SET_MODAL = '@customization/SET_MODAL'; 10 | 11 | // action - event reducer 12 | export const EVENT_GET_DATA = '@event/EVENT_GET_DATA'; 13 | export const EVENT_SET_ID = '@event/EVENT_SET_ID'; 14 | export const EVENT_SET_THEME = '@event/EVENT_SET_THME'; 15 | 16 | // action - global 17 | export const SET_LOADER = '@global/SET_LOADER'; 18 | export const SET_DIALOG = '@global/SET_DIALOG'; 19 | 20 | // wallet 21 | export const SET_ACCOUNT = '@global/SET_WALLET'; 22 | -------------------------------------------------------------------------------- /src/store/globalReducer/reducer.js: -------------------------------------------------------------------------------- 1 | // project imports 2 | import config from 'config'; 3 | 4 | // action - state management 5 | import * as actionTypes from './constant'; 6 | 7 | export const initialState = { 8 | isOpen: [], // for active default menu 9 | fontFamily: config.fontFamily, 10 | borderRadius: config.borderRadius, 11 | opened: true, 12 | isSearchShow: false, 13 | isSelectEventShow: true, 14 | modal: { 15 | show: false, 16 | modalType: '', 17 | title: '', 18 | }, 19 | wallet: { 20 | account: '', 21 | wallet: '', 22 | balance: '', 23 | }, 24 | }; 25 | 26 | // ==============================|| CUSTOMIZATION REDUCER ||============================== // 27 | 28 | // eslint-disable-next-line default-param-last 29 | const globalReducer = (state = initialState, action) => { 30 | let id; 31 | switch (action.type) { 32 | case actionTypes.MENU_OPEN: 33 | id = action.id; 34 | return { 35 | ...state, 36 | isOpen: [id], 37 | }; 38 | case actionTypes.SET_MENU: 39 | return { 40 | ...state, 41 | opened: action.opened, 42 | }; 43 | case actionTypes.SET_FONT_FAMILY: 44 | return { 45 | ...state, 46 | fontFamily: action.fontFamily, 47 | }; 48 | case actionTypes.SET_BORDER_RADIUS: 49 | return { 50 | ...state, 51 | borderRadius: action.borderRadius, 52 | }; 53 | case actionTypes.SEARCH_VISIBILITY: 54 | return { 55 | ...state, 56 | isSearchShow: action.isSearchShow, 57 | }; 58 | case actionTypes.SELECT_EVENT_VISIBILITY: 59 | return { 60 | ...state, 61 | isSelectEventShow: action.isSelectEventShow, 62 | }; 63 | case actionTypes.SET_MODAL: 64 | return { 65 | ...state, 66 | modal: { 67 | ...state.modal, 68 | show: action.show, 69 | modalType: action.modalType, 70 | title: action.title, 71 | }, 72 | }; 73 | case actionTypes.SET_ACCOUNT: 74 | return { 75 | ...state, 76 | wallet: { 77 | ...state.wallet, 78 | account: action.account, 79 | wallet: action.wallet, 80 | balance: action.balance, 81 | }, 82 | }; 83 | default: 84 | return state; 85 | } 86 | }; 87 | 88 | export default globalReducer; 89 | -------------------------------------------------------------------------------- /src/store/reducers.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | // Please don't remove this comment, this can be affected to your plopfile 4 | // REDUCERIMPORT 5 | import globalReducer from './globalReducer/reducer'; 6 | // ==============================|| COMBINE REDUCER ||============================== // 7 | 8 | const reducer = combineReducers({ 9 | globalReducer, 10 | // Please don't remove this comment, this can be affected to your plopfile 11 | // COMBINEREDUCER 12 | }); 13 | 14 | export default reducer; 15 | -------------------------------------------------------------------------------- /src/store/redux.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import reducer from './reducers'; 3 | 4 | // ==============================|| REDUX - MAIN STORE ||============================== // 5 | 6 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 7 | 8 | const store = createStore(reducer, composeEnhancers(applyMiddleware())); 9 | const persister = 'Free'; 10 | 11 | export { store, persister }; 12 | -------------------------------------------------------------------------------- /src/styles/base/font.scss: -------------------------------------------------------------------------------- 1 | // you can change your fonts here 2 | @import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap'); -------------------------------------------------------------------------------- /src/styles/base/theme.scss: -------------------------------------------------------------------------------- 1 | // paper & background 2 | $paper: #ffffff; 3 | 4 | // primary 5 | $primaryLight: #f4dccb; 6 | $primaryMain: #F07826; 7 | $primaryDark: #de981f; 8 | $primary200: #f3a470; 9 | $primary800: #ff802b; 10 | 11 | // secondary 12 | $secondaryLight: #ede7f6; 13 | $secondaryMain: #673ab7; 14 | $secondaryDark: #5e35b1; 15 | $secondary200: #b39ddb; 16 | $secondary800: #4527a0; 17 | 18 | // success Colors 19 | $successLight: #b9f6ca; 20 | $success200: #69f0ae; 21 | $successMain: #00e676; 22 | $successDark: #00c853; 23 | 24 | // error 25 | $errorLight: #ef9a9a; 26 | $errorMain: #f44336; 27 | $errorDark: #c62828; 28 | 29 | // orange 30 | $orangeLight: #fbe9e7; 31 | $orangeMain: #ffab91; 32 | $orangeDark: #d84315; 33 | 34 | // warning 35 | $warningLight: #fff8e1; 36 | $warningMain: #ffe57f; 37 | $warningDark: #ffc107; 38 | 39 | // grey 40 | $grey50: #fafafa; 41 | $grey100: #f5f5f5; 42 | $grey200: #eeeeee; 43 | $grey300: #e0e0e0; 44 | $grey500: #9e9e9e; 45 | $grey600: #757575; 46 | $grey700: #616161; 47 | $grey900: #212121; 48 | 49 | // ==============================|| DARK THEME VARIANTS ||============================== // 50 | 51 | // paper & background 52 | $darkBackground: #1a223f; // level 3 53 | $darkPaper: #111936; // level 4 54 | 55 | // dark 800 & 900 56 | $darkLevel1: #29314f; // level 1 57 | $darkLevel2: #212946; // level 2 58 | 59 | // primary dark 60 | $darkPrimaryLight: #e3f2fd; 61 | $darkPrimaryMain: #2196f3; 62 | $darkPrimaryDark: #1e88e5; 63 | $darkPrimary200: #90caf9; 64 | $darkPrimary800: #1565c0; 65 | 66 | // secondary dark 67 | $darkSecondaryLight: #d1c4e9; 68 | $darkSecondaryMain: #7c4dff; 69 | $darkSecondaryDark: #651fff; 70 | $darkSecondary200: #b39ddb; 71 | $darkSecondary800: #6200ea; 72 | 73 | // text variants 74 | $darkTextTitle: #d7dcec; 75 | $darkTextPrimary: #bdc8f0; 76 | $darkTextSecondary: #8492c4; 77 | 78 | // ==============================|| JAVASCRIPT ||============================== // 79 | 80 | :export { 81 | // paper & background 82 | paper: $paper; 83 | 84 | // primary 85 | primaryLight: $primaryLight; 86 | primary200: $primary200; 87 | primaryMain: $primaryMain; 88 | primaryDark: $primaryDark; 89 | primary800: $primary800; 90 | 91 | // secondary 92 | secondaryLight: $secondaryLight; 93 | secondary200: $secondary200; 94 | secondaryMain: $secondaryMain; 95 | secondaryDark: $secondaryDark; 96 | secondary800: $secondary800; 97 | 98 | // success 99 | successLight: $successLight; 100 | success200: $success200; 101 | successMain: $successMain; 102 | successDark: $successDark; 103 | 104 | // error 105 | errorLight: $errorLight; 106 | errorMain: $errorMain; 107 | errorDark: $errorDark; 108 | 109 | // orange 110 | orangeLight: $orangeLight; 111 | orangeMain: $orangeMain; 112 | orangeDark: $orangeDark; 113 | 114 | // warning 115 | warningLight: $warningLight; 116 | warningMain: $warningMain; 117 | warningDark: $warningDark; 118 | 119 | // grey 120 | grey50: $grey50; 121 | grey100: $grey100; 122 | grey200: $grey200; 123 | grey300: $grey300; 124 | grey500: $grey500; 125 | grey600: $grey600; 126 | grey700: $grey700; 127 | grey900: $grey900; 128 | 129 | // ==============================|| DARK THEME VARIANTS ||============================== // 130 | 131 | // paper & background 132 | darkPaper: $darkPaper; 133 | darkBackground: $darkBackground; 134 | 135 | // dark 800 & 900 136 | darkLevel1: $darkLevel1; 137 | darkLevel2: $darkLevel2; 138 | 139 | // text variants 140 | darkTextTitle: $darkTextTitle; 141 | darkTextPrimary: $darkTextPrimary; 142 | darkTextSecondary: $darkTextSecondary; 143 | 144 | // primary dark 145 | darkPrimaryLight: $darkPrimaryLight; 146 | darkPrimaryMain: $darkPrimaryMain; 147 | darkPrimaryDark: $darkPrimaryDark; 148 | darkPrimary200: $darkPrimary200; 149 | darkPrimary800: $darkPrimary800; 150 | 151 | // secondary dark 152 | darkSecondaryLight: $darkSecondaryLight; 153 | darkSecondaryMain: $darkSecondaryMain; 154 | darkSecondaryDark: $darkSecondaryDark; 155 | darkSecondary200: $darkSecondary200; 156 | darkSecondary800: $darkSecondary800; 157 | } 158 | -------------------------------------------------------------------------------- /src/styles/global/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/styles/global/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; 5 | } 6 | -------------------------------------------------------------------------------- /src/themes/index.js: -------------------------------------------------------------------------------- 1 | import { createTheme } from '@mui/material/styles'; 2 | import colors from 'styles/base/theme.scss'; 3 | import 'styles/base/font.scss'; 4 | import componentStyleOverrides from './overrides'; 5 | import themePalette from './pallete'; 6 | import themeTypography from './typography'; 7 | import themeShapes from './shapes'; 8 | 9 | export const theme = ({ customization }) => { 10 | const color = colors; 11 | 12 | const themeOption = { 13 | colors: color, 14 | heading: color.grey900, 15 | paper: color.paper, 16 | backgroundDefault: color.paper, 17 | background: color.primaryLight, 18 | darkTextPrimary: color.grey700, 19 | darkTextSecondary: color.grey500, 20 | textDark: color.grey900, 21 | menuSelected: color.secondaryDark, 22 | menuSelectedBack: color.secondaryLight, 23 | divider: color.grey200, 24 | customization, 25 | }; 26 | 27 | const themeOptions = { 28 | direction: 'ltr', 29 | palette: themePalette(themeOption), 30 | mixins: { 31 | toolbar: { 32 | minHeight: '48px', 33 | padding: '16px', 34 | '@media (min-width: 600px)': { 35 | minHeight: '48px', 36 | }, 37 | }, 38 | }, 39 | typography: themeTypography(themeOption), 40 | shape: themeShapes(), 41 | }; 42 | 43 | const themes = createTheme(themeOptions); 44 | themes.components = componentStyleOverrides(themeOption); 45 | 46 | return themes; 47 | }; 48 | 49 | export default theme; 50 | -------------------------------------------------------------------------------- /src/themes/overrides.js: -------------------------------------------------------------------------------- 1 | export default function componentStyleOverrides(theme) { 2 | const bgColor = theme.colors?.grey50; 3 | return { 4 | MuiButton: { 5 | styleOverrides: { 6 | root: { 7 | fontWeight: 500, 8 | borderRadius: '8px', 9 | p: 2, 10 | '&:hover': { 11 | backgroundColor: theme.colors.primary200, 12 | color: theme.colors.primary, 13 | }, 14 | }, 15 | }, 16 | }, 17 | MuiPaper: { 18 | defaultProps: { 19 | elevation: 0, 20 | }, 21 | styleOverrides: { 22 | root: { 23 | backgroundImage: 'none', 24 | }, 25 | rounded: { 26 | borderRadius: `${theme?.customization?.borderRadius}px`, 27 | }, 28 | }, 29 | }, 30 | MuiCardHeader: { 31 | styleOverrides: { 32 | root: { 33 | color: theme.colors?.textDark, 34 | padding: '24px', 35 | }, 36 | title: { 37 | fontSize: '1.125rem', 38 | }, 39 | }, 40 | }, 41 | MuiCardContent: { 42 | styleOverrides: { 43 | root: { 44 | padding: '24px', 45 | }, 46 | }, 47 | }, 48 | MuiCardActions: { 49 | styleOverrides: { 50 | root: { 51 | padding: '24px', 52 | }, 53 | }, 54 | }, 55 | MuiListItemButton: { 56 | styleOverrides: { 57 | root: { 58 | color: theme.darkTextPrimary, 59 | paddingTop: '10px', 60 | paddingBottom: '10px', 61 | '&.Mui-selected': { 62 | color: theme.menuSelected, 63 | backgroundColor: theme.menuSelectedBack, 64 | '&:hover': { 65 | backgroundColor: theme.menuSelectedBack, 66 | }, 67 | '& .MuiListItemIcon-root': { 68 | color: theme.menuSelected, 69 | }, 70 | }, 71 | '&:hover': { 72 | backgroundColor: theme.menuSelectedBack, 73 | color: theme.menuSelected, 74 | '& .MuiListItemIcon-root': { 75 | color: theme.menuSelected, 76 | }, 77 | }, 78 | }, 79 | }, 80 | }, 81 | MuiListItemIcon: { 82 | styleOverrides: { 83 | root: { 84 | color: theme.darkTextPrimary, 85 | minWidth: '36px', 86 | }, 87 | }, 88 | }, 89 | MuiListItemText: { 90 | styleOverrides: { 91 | primary: { 92 | color: theme.textDark, 93 | }, 94 | }, 95 | }, 96 | MuiInputBase: { 97 | styleOverrides: { 98 | input: { 99 | color: theme.textDark, 100 | '&::placeholder': { 101 | color: theme.darkTextSecondary, 102 | fontSize: '0.875rem', 103 | }, 104 | }, 105 | }, 106 | }, 107 | MuiOutlinedInput: { 108 | styleOverrides: { 109 | root: { 110 | background: bgColor, 111 | borderRadius: `${theme?.customization?.borderRadius}px`, 112 | '& .MuiOutlinedInput-notchedOutline': { 113 | borderColor: theme.colors?.grey400, 114 | }, 115 | '&:hover $notchedOutline': { 116 | borderColor: theme.colors?.primaryLight, 117 | }, 118 | '&.MuiInputBase-multiline': { 119 | padding: 1, 120 | }, 121 | }, 122 | input: { 123 | fontWeight: 500, 124 | background: bgColor, 125 | padding: '15.5px 14px', 126 | borderRadius: `${theme?.customization?.borderRadius}px`, 127 | '&.MuiInputBase-inputSizeSmall': { 128 | padding: '10px 14px', 129 | '&.MuiInputBase-inputAdornedStart': { 130 | paddingLeft: 0, 131 | }, 132 | }, 133 | }, 134 | inputAdornedStart: { 135 | paddingLeft: 4, 136 | }, 137 | notchedOutline: { 138 | borderRadius: `${theme?.customization?.borderRadius}px`, 139 | }, 140 | }, 141 | }, 142 | MuiSlider: { 143 | styleOverrides: { 144 | root: { 145 | '&.Mui-disabled': { 146 | color: theme.colors?.grey300, 147 | }, 148 | }, 149 | mark: { 150 | backgroundColor: theme.paper, 151 | width: '4px', 152 | }, 153 | valueLabel: { 154 | color: theme?.colors?.primaryLight, 155 | }, 156 | }, 157 | }, 158 | MuiDivider: { 159 | styleOverrides: { 160 | root: { 161 | borderColor: theme.divider, 162 | opacity: 1, 163 | }, 164 | }, 165 | }, 166 | MuiAvatar: { 167 | styleOverrides: { 168 | root: { 169 | color: theme.colors?.primaryDark, 170 | background: theme.colors?.primary200, 171 | }, 172 | }, 173 | }, 174 | MuiChip: { 175 | styleOverrides: { 176 | root: { 177 | '&.MuiChip-deletable .MuiChip-deleteIcon': { 178 | color: 'inherit', 179 | }, 180 | }, 181 | }, 182 | }, 183 | MuiTooltip: { 184 | styleOverrides: { 185 | tooltip: { 186 | color: theme.paper, 187 | background: theme.colors?.grey700, 188 | }, 189 | }, 190 | }, 191 | }; 192 | } 193 | -------------------------------------------------------------------------------- /src/themes/pallete.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Color intention that you want to used in your theme 3 | * @param {JsonObject} theme Theme customization object 4 | */ 5 | 6 | export default function themePalette(theme) { 7 | return { 8 | mode: theme?.customization?.navType, 9 | colors: theme?.colors, 10 | white: '#FFF', 11 | common: { 12 | black: theme.colors?.darkPaper, 13 | }, 14 | primary: { 15 | light: theme.colors?.primaryLight, 16 | main: theme.colors?.primaryMain, 17 | dark: theme.colors?.primaryDark, 18 | 200: theme.colors?.primary200, 19 | 800: theme.colors?.primary800, 20 | }, 21 | secondary: { 22 | light: theme.colors?.secondaryLight, 23 | main: theme.colors?.secondaryMain, 24 | dark: theme.colors?.secondaryDark, 25 | 200: theme.colors?.secondary200, 26 | 800: theme.colors?.secondary800, 27 | }, 28 | error: { 29 | light: theme.colors?.errorLight, 30 | main: theme.colors?.errorMain, 31 | dark: theme.colors?.errorDark, 32 | }, 33 | orange: { 34 | light: theme.colors?.orangeLight, 35 | main: theme.colors?.orangeMain, 36 | dark: theme.colors?.orangeDark, 37 | }, 38 | warning: { 39 | light: theme.colors?.warningLight, 40 | main: theme.colors?.warningMain, 41 | dark: theme.colors?.warningDark, 42 | }, 43 | success: { 44 | light: theme.colors?.successLight, 45 | 200: theme.colors?.success200, 46 | main: theme.colors?.successMain, 47 | dark: theme.colors?.successDark, 48 | }, 49 | grey: { 50 | 50: theme.colors?.grey50, 51 | 100: theme.colors?.grey100, 52 | 500: theme.darkTextSecondary, 53 | 600: theme.heading, 54 | 700: theme.darkTextPrimary, 55 | 900: theme.textDark, 56 | }, 57 | dark: { 58 | light: theme.colors?.darkTextPrimary, 59 | main: theme.colors?.darkLevel1, 60 | dark: theme.colors?.darkLevel2, 61 | 800: theme.colors?.darkBackground, 62 | 900: theme.colors?.darkPaper, 63 | }, 64 | text: { 65 | primary: theme.darkTextPrimary, 66 | secondary: theme.darkTextSecondary, 67 | dark: theme.textDark, 68 | hint: theme.colors?.grey100, 69 | white: '#FFFFFF', 70 | }, 71 | background: { 72 | paper: theme.colors?.primary200, 73 | default: theme.colors?.primaryLight, 74 | }, 75 | eventColor: { 76 | primary: theme?.event?.palette.primary, 77 | secondary: theme?.event?.palette.secondary, 78 | background1: theme?.event?.palette.background1, 79 | background2: theme?.event?.palette.background2, 80 | color1: theme?.event?.palette.color1, 81 | color2: theme?.event?.palette.color2, 82 | color3: theme?.event?.palette.color3, 83 | }, 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /src/themes/shapes.js: -------------------------------------------------------------------------------- 1 | export default function themeShapes() { 2 | return { 3 | button: { 4 | small: 4, 5 | medium: 8, 6 | large: 16, 7 | size: { 8 | small: '15px', 9 | medium: '45px', 10 | large: '75px', 11 | }, 12 | }, 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /src/themes/typography.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Typography used in theme 3 | * @param {JsonObject} theme theme customization object 4 | */ 5 | 6 | export default function themeTypography(theme) { 7 | return { 8 | fontFamily: `'Poppins', sans-serif`, 9 | h6: { 10 | fontWeight: 500, 11 | color: theme.heading, 12 | fontSize: '0.75rem', 13 | }, 14 | h5: { 15 | fontSize: '0.875rem', 16 | color: theme.heading, 17 | fontWeight: 500, 18 | }, 19 | h4: { 20 | fontSize: '1rem', 21 | color: theme.heading, 22 | fontWeight: 600, 23 | }, 24 | h3: { 25 | fontSize: '1.25rem', 26 | color: theme.heading, 27 | fontWeight: 600, 28 | }, 29 | h2: { 30 | fontSize: '1.5rem', 31 | color: theme.heading, 32 | fontWeight: 700, 33 | }, 34 | h1: { 35 | fontSize: '2.125rem', 36 | color: theme.heading, 37 | fontWeight: 700, 38 | }, 39 | subtitle1: { 40 | fontSize: '0.875rem', 41 | fontWeight: 500, 42 | color: theme.textDark, 43 | }, 44 | subtitle2: { 45 | fontSize: '0.75rem', 46 | fontWeight: 400, 47 | color: theme.darkTextSecondary, 48 | }, 49 | caption: { 50 | fontSize: '0.75rem', 51 | color: theme.darkTextSecondary, 52 | fontWeight: 400, 53 | }, 54 | body1: { 55 | fontSize: '0.875rem', 56 | fontWeight: 400, 57 | lineHeight: '1.334em', 58 | }, 59 | body2: { 60 | letterSpacing: '0em', 61 | fontWeight: 400, 62 | lineHeight: '1.5em', 63 | color: theme.darkTextPrimary, 64 | }, 65 | button: { 66 | textTransform: 'capitalize', 67 | }, 68 | customInput: { 69 | marginTop: 1, 70 | marginBottom: 1, 71 | '& > label': { 72 | top: 23, 73 | left: 0, 74 | color: theme.grey500, 75 | '&[data-shrink="false"]': { 76 | top: 5, 77 | }, 78 | }, 79 | '& > div > input': { 80 | padding: '30.5px 14px 11.5px !important', 81 | }, 82 | '& legend': { 83 | display: 'none', 84 | }, 85 | '& fieldset': { 86 | top: 0, 87 | }, 88 | }, 89 | mainContent: { 90 | backgroundColor: theme.background, 91 | width: '100%', 92 | minHeight: 'calc(100vh - 88px)', 93 | flexGrow: 1, 94 | padding: '20px', 95 | marginTop: '88px', 96 | marginRight: '20px', 97 | borderRadius: `${theme?.customization?.borderRadius}px`, 98 | }, 99 | menuCaption: { 100 | fontSize: '0.875rem', 101 | fontWeight: 500, 102 | color: theme.heading, 103 | padding: '6px', 104 | textTransform: 'capitalize', 105 | marginTop: '10px', 106 | }, 107 | subMenuCaption: { 108 | fontSize: '0.6875rem', 109 | fontWeight: 500, 110 | color: theme.darkTextSecondary, 111 | textTransform: 'capitalize', 112 | }, 113 | commonAvatar: { 114 | cursor: 'pointer', 115 | borderRadius: '8px', 116 | }, 117 | smallAvatar: { 118 | width: '22px', 119 | height: '22px', 120 | fontSize: '1rem', 121 | }, 122 | mediumAvatar: { 123 | width: '34px', 124 | height: '34px', 125 | fontSize: '1.2rem', 126 | }, 127 | largeAvatar: { 128 | width: '44px', 129 | height: '44px', 130 | fontSize: '1.5rem', 131 | }, 132 | }; 133 | } 134 | -------------------------------------------------------------------------------- /src/utilities/core/localStorage.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | export const getLocalStorageItem = key => { 3 | try { 4 | return JSON.parse(localStorage.getItem(key)); 5 | } catch (err) { 6 | console.error(err); 7 | return false; 8 | } 9 | }; 10 | 11 | export const setLocalStorageItem = async (key, item) => { 12 | try { 13 | return await localStorage.setItem(key, item); 14 | } catch (err) { 15 | console.error(err); 16 | return false; 17 | } 18 | }; 19 | 20 | export const removeLocalStorageItem = async key => { 21 | try { 22 | return await localStorage.removeItem(key); 23 | } catch (error) { 24 | console.error(error); 25 | return false; 26 | } 27 | }; 28 | 29 | const any = null; 30 | export const setMultipleLocalStorageItem = async (items = [{ key: '', item: any }]) => { 31 | try { 32 | items.forEach(el => setLocalStorageItem(el.key, el.item)); 33 | return true; 34 | } catch (err) { 35 | console.error(err); 36 | return false; 37 | } 38 | }; 39 | 40 | export const getMultipleLocalStorageItems = async (keys = ['']) => { 41 | try { 42 | const data = []; 43 | // eslint-disable-next-line no-plusplus 44 | for (let i = 0; i < keys.length; i++) { 45 | const item = getLocalStorageItem(keys[i]); 46 | const obj = { 47 | key: keys[i], 48 | item, 49 | }; 50 | data.push(obj); 51 | } 52 | return data; 53 | } catch (err) { 54 | console.error(err); 55 | return false; 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /src/utilities/core/process.js: -------------------------------------------------------------------------------- 1 | export const waitFor = (condition, callback) => { 2 | if (!condition()) { 3 | setTimeout(waitFor.bind(null, condition, callback), 100); /* this checks the flag every 100 milliseconds */ 4 | } else { 5 | callback(); 6 | } 7 | }; 8 | 9 | export const wait = seconds => 10 | new Promise(resolve => { 11 | const secs = Math.floor((seconds / 1000) % 60); 12 | setTimeout(() => { 13 | resolve(); 14 | }, secs); 15 | }); 16 | -------------------------------------------------------------------------------- /src/utilities/core/string.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-plusplus */ 2 | /* eslint-disable prefer-regex-literals */ 3 | /* eslint-disable no-bitwise */ 4 | 5 | import value from '../../styles/base/theme.scss'; 6 | 7 | // has number 8 | const hasNumber = number => new RegExp(/[0-9]/).test(number); 9 | 10 | // has mix of small and capitals 11 | const hasMixed = number => new RegExp(/[a-z]/).test(number) && new RegExp(/[A-Z]/).test(number); 12 | 13 | // has special chars 14 | const hasSpecial = number => new RegExp(/[!#@$%^&*)(+=._-]/).test(number); 15 | 16 | // set color based on password strength 17 | export const strengthColor = count => { 18 | if (count < 2) return { label: 'Poor', color: value.errorMain }; 19 | if (count < 3) return { label: 'Weak', color: value.warningDark }; 20 | if (count < 4) return { label: 'Normal', color: value.orangeMain }; 21 | if (count < 5) return { label: 'Good', color: value.successMain }; 22 | if (count < 6) return { label: 'Strong', color: value.successDark }; 23 | return { label: 'Poor', color: value.errorMain }; 24 | }; 25 | 26 | // password strength indicator 27 | export const strengthIndicator = number => { 28 | let strengths = 0; 29 | if (number.length > 5) strengths += 1; 30 | if (number.length > 7) strengths += 1; 31 | if (hasNumber(number)) strengths += 1; 32 | if (hasSpecial(number)) strengths += 1; 33 | if (hasMixed(number)) strengths += 1; 34 | return strengths; 35 | }; 36 | 37 | let color; 38 | const letters = '0123456789ABCDEF'.split(''); 39 | export const AddDigitToColor = limit => { 40 | color += letters[Math.round(Math.random() * limit)]; 41 | }; 42 | 43 | export const makeRandomColor = () => { 44 | color = '#'; 45 | AddDigitToColor(5); 46 | // eslint-disable-next-line no-plusplus 47 | for (let i = 0; i < 5; i++) { 48 | AddDigitToColor(15); 49 | } 50 | return color; 51 | }; 52 | 53 | export const makeRandomDeg = () => Math.random() * 10; 54 | 55 | export const truncate = (str, n, useWordBoundary) => { 56 | if (str.length <= n) { 57 | return str; 58 | } 59 | const subString = str.substr(0, n - 1); // the original check 60 | return `${useWordBoundary ? subString.substr(0, subString.lastIndexOf(' ')) : subString}...`; 61 | }; 62 | 63 | export const generateRandomString = length => { 64 | let result = ''; 65 | const characters = '1234567890'; 66 | const charactersLength = characters.length; 67 | // eslint-disable-next-line no-plusplus 68 | for (let i = 0; i < length; i++) { 69 | result += characters.charAt(Math.floor(Math.random() * charactersLength)); 70 | } 71 | return result; 72 | }; 73 | 74 | export const fancyTimeFormat = duration => { 75 | // Hours, minutes and seconds 76 | const hrs = ~~(duration / 3600); 77 | const mins = ~~((duration % 3600) / 60); 78 | const secs = ~~duration % 60; 79 | 80 | // Output like "1:01" or "4:03:59" or "123:03:59" 81 | let ret = ''; 82 | 83 | if (hrs > 0) { 84 | ret += `${hrs}:${mins < 10 ? '0' : ''}`; 85 | } 86 | 87 | ret += `${mins}:${secs < 10 ? '0' : ''}`; 88 | ret += `${secs}`; 89 | return ret; 90 | }; 91 | 92 | export const toSmallUnit = (price, decimal) => { 93 | return price / 10 ** decimal; 94 | }; 95 | 96 | export const hideBalance = (price, dcm) => { 97 | return toSmallUnit(price, dcm).toString().replace(/[0-9]/g, '*'); 98 | }; 99 | 100 | export const truncateWalletAddress = (input = '', n = 10) => { 101 | if (input.length > n) { 102 | const sbstr = input.substring(0, n - 1); 103 | const revSbstr = input 104 | .split('') 105 | .reverse() 106 | .join('') 107 | .substring(0, n - 2); 108 | const finalString = `${sbstr} ... ${revSbstr}`; 109 | return finalString; 110 | } 111 | return input; 112 | }; 113 | 114 | export const toNormalUnit = (price, decimal) => { 115 | if (typeof price === 'string') { 116 | parseInt(price, 10); 117 | } 118 | return BigInt(price * 10 ** decimal); 119 | }; 120 | 121 | /** 122 | * Change currency to usd 123 | * @param {number} val Provide number of currency 124 | * @param {number} fractionalDigit For a fractional digit 125 | * @return {string} Resolved currency string. 126 | */ 127 | export const toUsd = (val, fractionalDigit) => { 128 | return `$${(Math.trunc(val * 10 ** fractionalDigit) / 10 ** fractionalDigit).toFixed(2).toString()}`; 129 | }; 130 | 131 | /** 132 | * Change currency to rupiah 133 | * @param {number} val Provide number of currency 134 | * @return {string} Resolved currency string. 135 | */ 136 | export const toRupiah = (val, fractionalDigit) => { 137 | const toFixedNum = (Math.trunc(val * 10 ** fractionalDigit) / 10 ** fractionalDigit).toFixed(); 138 | let idr = ''; 139 | const angkarev = toFixedNum.toString().split('').reverse().join(''); 140 | for (let i = 0; i < angkarev.length; i++) if (i % 3 === 0) idr += `${angkarev.substr(i, 3)}.`; 141 | return `Rp. ${idr 142 | .split('', idr.length - 1) 143 | .reverse() 144 | .join('')}`; 145 | }; 146 | 147 | /** 148 | * Join provided url paths. 149 | * @param {...string} paths Provided paths. It doesn't matter if they have trailing slash. 150 | * @return {string} Resolved url without trailing slash. 151 | */ 152 | export const resolveUrl = (...paths) => 153 | paths.reduce((resolvedUrl, path) => { 154 | const urlPath = path.toString().trim(); 155 | if (urlPath) { 156 | // eslint-disable-next-line no-param-reassign 157 | resolvedUrl += (resolvedUrl === '' ? '' : '/') + urlPath.replace(/^\/|\/$/g, ''); 158 | } 159 | // eslint-disable-next-line no-param-reassign 160 | resolvedUrl = resolvedUrl[0] !== '/' ? `/${resolvedUrl}` : resolvedUrl; 161 | return resolvedUrl; 162 | }, ''); 163 | /** 164 | * Resolve a page url adding a trailing slash. 165 | * Needed to prevent 301 redirects cause of Gatsby.js' folder structure. 166 | * @param {...string} path Provided paths. It doesn't matter if they have trailing slash. 167 | * @return {string} Resolved url with trailing slash. 168 | */ 169 | export const resolvePageUrl = (...path) => { 170 | const resolvedUrl = resolveUrl(...path); 171 | return resolvedUrl; 172 | }; 173 | -------------------------------------------------------------------------------- /src/utilities/network/endpoints.js: -------------------------------------------------------------------------------- 1 | const API_BASE_URL_DEV = 'http://localhost:1337/'; 2 | const API_BASE_URL_PROD = 'http://localhost:1337/'; 3 | 4 | const phases = process.env.REACT_APP_PHASES; 5 | const BASE_URL = phases === 'dev' ? API_BASE_URL_DEV : API_BASE_URL_PROD; 6 | 7 | export default BASE_URL; 8 | -------------------------------------------------------------------------------- /src/utilities/network/headers.js: -------------------------------------------------------------------------------- 1 | const DUMMY_BEARER_TOKEN = 'aa2022'; 2 | export function HeaderAuth(token = DUMMY_BEARER_TOKEN) { 3 | return { 4 | headers: { 5 | 'Content-Type': 'application/json', 6 | Authorization: `Bearer ${token}`, 7 | }, 8 | }; 9 | } 10 | 11 | export function HeaderCmc(apiKey) { 12 | return { 13 | headers: { 14 | CMC_PRO_API_KEY: apiKey, 15 | }, 16 | }; 17 | } 18 | 19 | export function HeaderFile(token = DUMMY_BEARER_TOKEN) { 20 | return { 21 | headers: { 22 | 'Content-Type': 'application/json', 23 | Authorization: `Bearer ${token}`, 24 | Accept: '*/*', 25 | }, 26 | }; 27 | } 28 | 29 | export function Header() { 30 | return { 31 | headers: { 32 | 'Content-Type': 'application/json', 33 | }, 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/utilities/network/network.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import axios from 'axios'; 3 | 4 | export function GET(url, header) { 5 | return axios 6 | .get(url, header) 7 | .then(res => res) 8 | .catch(err => { 9 | if (typeof err.response === 'undefined') { 10 | console.log('Network Error, Connection Not Found'); 11 | return { status: 400 }; 12 | } 13 | const errorData = err.response.data || { 14 | message: 'Something Went Wrong', 15 | trace: '', 16 | }; 17 | console.log(`Error ${err.response.status || ''}:${errorData.message} ${errorData.trace}`); 18 | return err.response; 19 | }); 20 | } 21 | 22 | export function GETFILE(url) { 23 | return axios 24 | .get(url, { 25 | responseType: 'blob', 26 | // timeout: 30000, 27 | headers: { 28 | 'Content-Type': 'application/json', 29 | Authorization: `Bearer ${localStorage.getItem('token')}`, 30 | Accept: '*/*', 31 | }, 32 | }) 33 | .then(res => res) 34 | .catch(err => { 35 | if (typeof err.response === 'undefined') { 36 | console.log('Network Error, Connection Not Found'); 37 | return { status: 400 }; 38 | } 39 | const errorData = err.response.data || { 40 | message: 'Something Went Wrong', 41 | trace: '', 42 | }; 43 | console.log(`Error ${err.response.status || ''}:${errorData.message} ${errorData.trace}`); 44 | return err.response; 45 | }); 46 | } 47 | 48 | export function POST(url, body, header) { 49 | return axios 50 | .post(url, body, header) 51 | .then(res => res) 52 | .catch(err => { 53 | if (typeof err.response === 'undefined') { 54 | console.log('Network Error, Connection Not Found'); 55 | return { status: 400 }; 56 | } 57 | const errorData = err.response.data || { 58 | message: 'Something Went Wrong', 59 | trace: '', 60 | }; 61 | console.log(`Error ${err.response.status || ''}:${errorData.message} ${errorData.trace}`); 62 | return err.response; 63 | }); 64 | } 65 | 66 | export function PUT(url, body, header) { 67 | return axios 68 | .put(url, body, header) 69 | .then(res => res) 70 | .catch(err => { 71 | if (typeof err.response === 'undefined') { 72 | console.log('Network Error, Connection Not Found'); 73 | return { status: 400 }; 74 | } 75 | const errorData = err.response.data || { 76 | message: 'Something Went Wrong', 77 | trace: '', 78 | }; 79 | console.log(`Error ${err.response.status || ''}:${errorData.message} ${errorData.trace}`); 80 | return err.response; 81 | }); 82 | } 83 | 84 | export function DELETE(url, header) { 85 | return axios 86 | .delete(url, header) 87 | .then(res => res) 88 | .catch(err => { 89 | if (typeof err.response === 'undefined') { 90 | console.log('Network Error, Connection Not Found'); 91 | return { status: 400 }; 92 | } 93 | const errorData = err.response.data || { 94 | message: 'Something Went Wrong', 95 | trace: '', 96 | }; 97 | console.log(`Error ${err.response.status || ''}:${errorData.message} ${errorData.trace}`); 98 | return err.response; 99 | }); 100 | } 101 | 102 | // Get Unique Colors from Json Data 103 | export const getCartTotal = cartItems => { 104 | let total = 0; 105 | // eslint-disable-next-line no-plusplus 106 | for (let i = 0; i < cartItems.length; i++) { 107 | total += parseInt(cartItems[i].qty, 10) * parseInt((cartItems[i].price * cartItems[i].discount) / 100, 10); 108 | } 109 | return total; 110 | }; 111 | 112 | // Get TOP Collection 113 | export const getTopCollection = products => { 114 | const items = products.filter(product => product.rating > 4); 115 | return items.slice(0, 8); 116 | }; 117 | 118 | // Get Best Seller 119 | export const getBestSeller = products => { 120 | const items = products.filter(product => product.sale === true); 121 | 122 | return items.slice(0, 8); 123 | }; 124 | 125 | // Get Mens Wear 126 | export const getMensWear = products => { 127 | const items = products.filter(product => product.category === 'men'); 128 | 129 | return items.slice(0, 8); 130 | }; 131 | 132 | // Get Single Product 133 | export const getSingleItem = (products, id) => { 134 | const items = products.find(element => element.id === id); 135 | return items; 136 | }; 137 | -------------------------------------------------------------------------------- /src/utilities/web3/chains.js: -------------------------------------------------------------------------------- 1 | export const CHAINS = { 2 | 56: { 3 | urls: ['https://bsc-dataseed1.defibit.io'], 4 | name: 'Mainnet', 5 | }, 6 | 97: { 7 | urls: ['https://data-seed-prebsc-1-s1.binance.org:8545/'], 8 | name: 'Testnet', 9 | }, 10 | }; 11 | 12 | export const WCCHAIN = { 13 | 56: 'https://bsc-dataseed1.defibit.io', 14 | 97: 'https://data-seed-prebsc-1-s1.binance.org:8545/', 15 | }; 16 | 17 | export const URLS = Object.keys(CHAINS).reduce((accumulator, chainId) => { 18 | const validURLs = CHAINS[Number(chainId)].urls; 19 | 20 | if (validURLs.length) { 21 | accumulator[Number(chainId)] = validURLs; 22 | } 23 | 24 | return accumulator; 25 | }, {}); 26 | 27 | function isExtendedChainInformation(chainInformation) { 28 | return !!chainInformation.nativeCurrency; 29 | } 30 | 31 | export function getAddChainParameters(chainId) { 32 | const chainInformation = CHAINS[chainId]; 33 | if (isExtendedChainInformation(chainInformation)) { 34 | return { 35 | chainId, 36 | chainName: chainInformation.name, 37 | nativeCurrency: chainInformation.nativeCurrency, 38 | rpcUrls: chainInformation.urls, 39 | blockExplorerUrls: chainInformation.blockExplorerUrls, 40 | }; 41 | } 42 | return chainId; 43 | } 44 | -------------------------------------------------------------------------------- /templates/Component.hbs: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | function {{componentname}}Component(props) { 5 | return
{{componentname}}
; 6 | } 7 | 8 | {{componentname}}Component.propTypes = {}; 9 | 10 | export default {{componentname}}Component; 11 | -------------------------------------------------------------------------------- /templates/Page.hbs: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | function {{classname}}(props) { 5 | return
{{classname}}
; 6 | } 7 | 8 | {{classname}}.propTypes = {}; 9 | 10 | export default {{classname}}; 11 | -------------------------------------------------------------------------------- /templates/Style.hbs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacklawson-dev/dapps-react-redux-mui-boilerplate/befd5b702e5b2bdae011d96dca3840c07c95892d/templates/Style.hbs -------------------------------------------------------------------------------- /templates/actions.hbs: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import * as types from './constants'; 3 | 4 | export const setHelloExample = (data = '{{reducer}}') => ({ 5 | type: types.HELLO_EXAMPLE, 6 | data 7 | }); -------------------------------------------------------------------------------- /templates/componentTest.hbs: -------------------------------------------------------------------------------- 1 | //use your unit testing 2 | import renderer from 'react-test-renderer'; 3 | import {{componentname}}Component from '../index'; 4 | 5 | it('Describe {{componentname}}.js Tests', () => { 6 | const component = renderer.create(<{{componentname}}Component />); 7 | let tree = component.toJSON(); 8 | expect(tree).toMatchSnapshot(); 9 | // re-rendering 10 | tree = component.toJSON(); 11 | expect(tree).toMatchSnapshot(); 12 | // re-rendering 13 | tree = component.toJSON(); 14 | expect(tree).toMatchSnapshot(); 15 | }); 16 | -------------------------------------------------------------------------------- /templates/constants.hbs: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | // action - {{reducer}} reducer 3 | export const HELLO_EXAMPLE = '@{{reducer}}/HELLO_EXAMPLE'; -------------------------------------------------------------------------------- /templates/exportPage.hbs: -------------------------------------------------------------------------------- 1 | import {{classname}} from './views/index' 2 | 3 | export default {{classname}} -------------------------------------------------------------------------------- /templates/reducer.hbs: -------------------------------------------------------------------------------- 1 | // action - state management 2 | import * as actionTypes from './constants'; 3 | 4 | export const initialState = { 5 | hello: 'world', 6 | }; 7 | 8 | // ==============================|| CUSTOMIZATION REDUCER ||============================== // 9 | 10 | // eslint-disable-next-line default-param-last 11 | const {{reducer}} = (state = initialState, action) => { 12 | switch (action.type) { 13 | case actionTypes.HELLO_EXAMPLE: 14 | return { 15 | ...state, 16 | hello: '{{reducer}}', 17 | }; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | export default {{reducer}}; 24 | -------------------------------------------------------------------------------- /templates/test.hbs: -------------------------------------------------------------------------------- 1 | //use your unit testing 2 | import renderer from 'react-test-renderer'; 3 | import {{classname}} from '../index'; 4 | 5 | it('Describe {{classname}}.js Tests', () => { 6 | const component = renderer.create(<{{classname}} />); 7 | let tree = component.toJSON(); 8 | expect(tree).toMatchSnapshot(); 9 | // re-rendering 10 | tree = component.toJSON(); 11 | expect(tree).toMatchSnapshot(); 12 | // re-rendering 13 | tree = component.toJSON(); 14 | expect(tree).toMatchSnapshot(); 15 | }); 16 | --------------------------------------------------------------------------------