├── .editorconfig
├── .env
├── .eslintrc.js
├── .gitignore
├── .prettierrc.js
├── .vscode
├── launch.json
└── settings.json
├── README.md
├── docs
├── drafts.md
├── references.md
└── roadmap.md
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── src
├── Administration
│ ├── Accounts
│ │ ├── AccountsEditor
│ │ │ └── .gitkeep
│ │ └── AccountsList
│ │ │ └── .gitkeep
│ ├── Administration.tsx
│ ├── README.md
│ ├── Users
│ │ ├── UsersEditor
│ │ │ └── .gitkeep
│ │ └── UsersList
│ │ │ └── .gitkeep
│ └── index.tsx
├── App.test.tsx
├── App.tsx
├── AppRouter.tsx
├── Auth
│ ├── Auth.tsx
│ ├── Login
│ │ └── Login.tsx
│ ├── README.md
│ ├── Recover
│ │ └── Recover.tsx
│ ├── Reset
│ │ └── Reset.tsx
│ ├── Signup
│ │ └── Signup.tsx
│ └── index.tsx
├── Content
│ ├── CategoriesEditor
│ │ └── .gitkeep
│ ├── CategoriesList
│ │ └── .gitkeep
│ ├── Content.tsx
│ ├── ItemsEditor
│ │ └── .gitkeep
│ ├── ItemsList
│ │ └── .gitkeep
│ ├── README.md
│ ├── _api
│ │ ├── _data
│ │ │ ├── itemsCategoriesData.js
│ │ │ ├── itemsData.js
│ │ │ └── itemsItemsCategoriesData.js
│ │ ├── _mocks
│ │ │ └── .gitkeep
│ │ ├── itemsCategoriesService.tsx
│ │ └── itemsService.tsx
│ ├── _types
│ │ ├── Item.tsx
│ │ └── ItemCategory.tsx
│ └── index.tsx
├── Demo
│ ├── Components
│ │ ├── Components.tsx
│ │ └── index.tsx
│ ├── Demo.tsx
│ ├── Discuss
│ │ ├── Discuss.tsx
│ │ └── index.tsx
│ ├── Docs
│ │ ├── Docs.tsx
│ │ └── index.tsx
│ ├── Features
│ │ ├── Features.tsx
│ │ └── index.tsx
│ ├── README.md
│ ├── Supporters
│ │ ├── Supporters.tsx
│ │ └── index.tsx
│ └── index.tsx
├── Misc
│ ├── NotFound
│ │ ├── NotFound.tsx
│ │ └── index.tsx
│ └── README.md
├── Organization
│ ├── Organization.tsx
│ ├── Preview
│ │ └── .gitkeep
│ ├── README.md
│ ├── Settings
│ │ └── .gitkeep
│ ├── Users
│ │ ├── UsersEditor
│ │ │ └── .gitkeep
│ │ └── UsersList
│ │ │ └── .gitkeep
│ └── index.tsx
├── Profile
│ ├── Preview
│ │ └── .gitkeep
│ ├── Profile.tsx
│ ├── README.md
│ ├── Settings
│ │ └── .gitkeep
│ └── index.tsx
├── README.md
├── Sales
│ ├── Customers
│ │ ├── Customers.tsx
│ │ ├── CustomersEditor
│ │ │ └── .gitkeep
│ │ ├── CustomersList
│ │ │ └── .gitkeep
│ │ └── index.tsx
│ ├── Locations
│ │ └── .gitkeep
│ ├── Orders
│ │ ├── Orders.tsx
│ │ ├── OrdersEditor
│ │ │ └── .gitkeep
│ │ ├── OrdersList
│ │ │ └── .gitkeep
│ │ └── index.tsx
│ ├── Overview
│ │ ├── OrdersHistory
│ │ │ ├── OrdersHistory.tsx
│ │ │ ├── index.tsx
│ │ │ └── ordersHistoryService.ts
│ │ ├── OrdersLatest
│ │ │ ├── OrdersLatest.tsx
│ │ │ └── index.tsx
│ │ ├── Overview.tsx
│ │ ├── OverviewActions.tsx
│ │ ├── index.tsx
│ │ └── overviewContext.ts
│ ├── Payments
│ │ └── Payments.tsx
│ ├── Products
│ │ ├── ProductsCategoriesEditor
│ │ │ └── .gitkeep
│ │ ├── ProductsCategoriesList
│ │ │ └── .gitkeep
│ │ ├── ProductsEditor
│ │ │ └── .gitkeep
│ │ └── ProductsList
│ │ │ └── .gitkeep
│ ├── README.md
│ ├── Sales.tsx
│ ├── SalesService.tsx
│ ├── Stock
│ │ └── Stock.tsx
│ ├── Stores
│ │ └── Stores.tsx
│ ├── _api
│ │ ├── _data
│ │ │ ├── categoriesData.ts
│ │ │ ├── customersData.ts
│ │ │ ├── locationsData.ts
│ │ │ ├── ordersData.ts
│ │ │ ├── paymentsData.ts
│ │ │ ├── productsData.ts
│ │ │ ├── productsRelCategoriesData.ts
│ │ │ ├── stockActionsData.ts
│ │ │ └── stocksData.ts
│ │ ├── _mocks
│ │ │ ├── index.ts
│ │ │ └── ordersMocks.ts
│ │ ├── customers.ts
│ │ ├── index.ts
│ │ ├── orders.ts
│ │ ├── products.ts
│ │ └── productsCategories.ts
│ ├── _services
│ │ └── .gitkeep
│ ├── _types
│ │ ├── Customer.ts
│ │ ├── EntityOwned.ts
│ │ ├── Location.ts
│ │ ├── Order.ts
│ │ ├── Payment.ts
│ │ ├── Product.ts
│ │ ├── ProductAttachment.ts
│ │ ├── ProductCategory.ts
│ │ ├── ProductImage.ts
│ │ ├── ProductToProductCategory.ts
│ │ ├── Stock.ts
│ │ └── StockAction.ts
│ └── index.tsx
├── _api
│ ├── _data
│ │ ├── notificationsData.ts
│ │ ├── organizationsData.ts
│ │ ├── organizationsToUsersData.ts
│ │ └── usersData.ts
│ ├── _mocks
│ │ ├── index.ts
│ │ ├── organizationsMocks.ts
│ │ └── usersMocks.ts
│ ├── client.ts
│ ├── index.ts
│ ├── notifications.ts
│ ├── organizations.ts
│ └── users.ts
├── _common
│ ├── Button
│ │ └── Button.tsx
│ ├── Dropdown
│ │ └── Dropdown.tsx
│ ├── Logo
│ │ └── Logo.tsx
│ ├── PageBreadcrumbs
│ │ ├── PageBreadcrumbs.tsx
│ │ └── index.tsx
│ ├── PageContainer
│ │ ├── PageContainer.tsx
│ │ └── index.tsx
│ ├── PageTitle
│ │ └── .gitkeep
│ └── PageToolbar
│ │ ├── PageToolbar.tsx
│ │ └── index.tsx
├── _config
│ └── index.ts
├── _layouts
│ ├── DashboardLayout
│ │ ├── DashboardLayout.tsx
│ │ ├── Footer
│ │ │ ├── Footer.tsx
│ │ │ └── index.tsx
│ │ ├── Header
│ │ │ ├── Header.tsx
│ │ │ ├── HeaderDemo.tsx
│ │ │ ├── HeaderNotifications.tsx
│ │ │ ├── HeaderProfile.tsx
│ │ │ ├── HeaderSearch.tsx
│ │ │ └── index.tsx
│ │ ├── Sidebar
│ │ │ ├── Sidebar.tsx
│ │ │ ├── SidebarNav.tsx
│ │ │ ├── SidebarNavItem.tsx
│ │ │ ├── SidebarNavItems.tsx
│ │ │ └── index.tsx
│ │ └── index.tsx
│ └── index.tsx
├── _services
│ ├── authService.tsx
│ └── helpersService.tsx
├── _state
│ ├── appModel.ts
│ ├── appState.ts
│ └── index.ts
├── _theme
│ └── index.ts
├── _types
│ ├── Entity.ts
│ ├── Organization.ts
│ ├── OrganizationToUser.ts
│ └── User.ts
├── index.tsx
├── react-app-env.d.ts
└── serviceWorker.ts
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | SKIP_PREFLIGHT_CHECK=true
2 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser', // Specifies the ESLint parser
3 | extends: [
4 | 'eslint:recommended',
5 | 'react-app',
6 | 'plugin:react/recommended',
7 | 'plugin:@typescript-eslint/recommended',
8 | 'prettier/@typescript-eslint',
9 | 'plugin:prettier/recommended',
10 | ],
11 | plugins: [
12 | 'react',
13 | '@typescript-eslint',
14 | 'prettier'
15 | ],
16 | env: {
17 | 'browser': true,
18 | 'jasmine': true,
19 | 'jest': true,
20 | 'node': true,
21 | },
22 | rules: {
23 | 'prettier/prettier': ['error', { 'singleQuote': true }],
24 | '@typescript-eslint/explicit-function-return-type': [0],
25 | '@typescript-eslint/no-use-before-define': [0],
26 | '@typescript-eslint/no-empty-interface': [0],
27 | '@typescript-eslint/explicit-member-accessibility': [0],
28 | '@typescript-eslint/no-explicit-any': [0],
29 | '@typescript-eslint/no-angle-bracket-type-assertion': [0],
30 | '@typescript-eslint/no-object-literal-type-assertion': [0],
31 | '@typescript-eslint/no-triple-slash-reference': [0],
32 | '@typescript-eslint/prefer-interface': [0],
33 | 'react/prop-types': [0],
34 | 'react/display-name': [0],
35 | 'no-console': [0],
36 | },
37 | settings: {
38 | react: {
39 | pragma: 'React',
40 | version: 'detect',
41 | }
42 | },
43 | parserOptions: {
44 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
45 | sourceType: 'module', // Allows for the use of imports
46 | },
47 | };
48 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: false,
3 | trailingComma: 'all',
4 | singleQuote: true,
5 | printWidth: 90,
6 | tabWidth: 2,
7 | };
8 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Chrome",
6 | "type": "chrome",
7 | "request": "launch",
8 | "url": "http://localhost:3000",
9 | "webRoot": "${workspaceFolder}/src",
10 | "sourceMapPathOverrides": {
11 | "webpack:///src/*": "${webRoot}/*"
12 | }
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": false,
3 | "eslint.autoFixOnSave": true,
4 | "eslint.validate": [
5 | "javascript",
6 | "javascriptreact",
7 | {
8 | "language": "typescript",
9 | "autoFix": true
10 | },
11 | {
12 | "language": "typescriptreact",
13 | "autoFix": true
14 | }
15 | ],
16 | }
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Modular Material Admin + React
2 |
3 |
4 |
5 | Free [MaterialUI](https://material-ui.com/) dashboard theme implemented by using **ReactJS** + **TypeScript** + **Redux** + **Rematch** + **React Hooks**
6 |
7 | **(in development)**
8 |
9 |
10 |
11 | [](https://modular-material-admin-react.modularcode.io/)
12 |
13 |
14 |
15 | View Demo
16 |
17 |
18 |
19 | ## Getting Started
20 |
21 | In the project directory, you can run:
22 |
23 | ### `npm start`
24 |
25 | Runs the app in the development mode.
26 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
27 |
28 | The page will reload if you make edits.
29 | You will also see any lint errors in the console.
30 |
31 | ### `npm test`
32 |
33 | Launches the test runner in the interactive watch mode.
34 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
35 |
36 | ### `npm run build`
37 |
38 | Builds the app for production to the `build` folder.
39 | It correctly bundles React in production mode and optimizes the build for the best performance.
40 |
41 | The build is minified and the filenames include the hashes.
42 | Your app is ready to be deployed!
43 |
44 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
45 |
46 | ## Learn More
47 |
48 |
49 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
50 |
51 | To learn React, check out the [React documentation](https://reactjs.org/).
52 |
--------------------------------------------------------------------------------
/docs/drafts.md:
--------------------------------------------------------------------------------
1 | ## This is just a draft document with a thoughts :)
2 |
3 | The points below are still in progress
4 |
5 |
6 | ### Features
7 |
8 | - Modular and scalable architecture
9 | - Data driven and function ready with pre-set REST API entities, sample data and state management system
10 | - Static typechecking with TypeScript for large scale projects
11 | - Built in authentication with JWT
12 | - Typed data entities
13 | - Uses react hooks
14 | - Simpler state management with Redux + Rematch + Immer for reducing Redux code boilerplate
15 | - Material UI kit
16 | - Smart base applications with real-world usage (sales management, content management)
17 | - Users/Organizations roles with many-to-many relations
18 | - Designed in SAAS setup in mind, with subscription planes and global system roles
19 | - and more...
20 |
21 |
22 | ### State management
23 |
24 |
25 |
26 |
27 | ## FAQ
28 |
29 | #### Who is Modular Material Admin for?
30 |
31 | Modular Material Admin is a good fit for advanced developers who want to build a large-scale dashboard application with a good coding practicies.
32 |
33 | #### How Modular Material Admin is different from hundreds of other admin templates out there?
34 |
35 | The most of the dashboard themes and templates are more focused on the UI components. This brings to the situation, that for the most dashboard you need to fully rewrite or reimplement things like authorization, state management and api entities services setup. Modular Material Admin aims to provide that basic architecture setup out of the box
36 |
37 |
38 | #### How Modular Material Admin is different from other Admin UI ready CMS systems like Keystonejs, Strapi, WordPress, etc?
39 |
40 | Even though there are similarities and can be similar use cases there are major differences between ready-made CMS systems and Modular Material Admin
41 |
42 | CMS systems usually adopt the configuration over coding principle which means that in order to extend the existing functionality you need to make lot's of configurations and extend the functionality by the CMS plugins API. Another thing is that the Admin UI layout, style and design is usually predefined without the ability to customize it.
43 |
44 | On the controversary the Modular Material Admin + React is a **starter project template** which is meant to be a solid foundation to **build your own** admin system with the best React + TypeScript + MaterialUI coding practices.
45 |
46 | So the AdminUI ready CMS systems are really a good fit if
47 |
48 | - Your primary objective is content management
49 | - You want to prototype something quickly
50 | - You're fine with extending the AdminUI via CMS plugins API
51 | - You don't need to customize the AdminUI layout
52 | - You don't need to add a lot's of custom business logic which isn't supported via the CMS
53 |
54 | Modular Material Admin + React is a good fit if
55 |
56 | - You want to write your AdminUI code and business logic
57 | - You want to use React, React Hooks and TypeScript for your project
58 | - You want to extend the existing functionality via your own code
59 | - You don't want to depend on the CMS ecosystem
60 |
61 |
62 | #### Summing up: what is the Modular Material Admin + React usage niche?
63 |
64 |
65 | Modular Material Admin + React stand beetween a basic React Admin UI templates and a Admin UI ready CMS systems.
66 |
67 | | Basic Admin Template | | | Modular Material Admin | | | AdminUI Ready CMS |
68 | |----------------------|---|---|:------------------------:|---|---|--------------------:|
69 | | Less features | | | Moderate features | | | More Features |
70 | | High customizability | | | Moderate customizability | | | Low customizability |
71 |
72 | #### Why do you call it modular?
73 |
74 | I've been working on different large scale apps for years already, and have developed my own way of structuring and naming the application parts. This turned out a really successfull approach, which helped me to ofocus on a single functionality module. The modules are like lego building blocks and mostly represent the application logical structure. If you think about our app in a small pieces, it's all about a tree! There are also directories starting with underscore, e.g. `_services`, which combine files or modules of the same type or purpose.
75 |
76 |
77 | #### Why do use TypeScript?
78 |
79 | I beleive that the fucture of the JavaScript in large scale applications is TypeScript.
80 | It improves the code quality, helps to avoid of lot's of runtime errors, during refactoring and maintenance. In addition it provides a great intelisense, which boosts the productivity. TypeScript comes with it's overhead cost though, so if you want to build something small, it might be a bit of complicated to use.
81 |
82 | #### Why do you use MaterialUI?
83 |
84 | Although I'm not a big fan of material design, but the MaterialUI gives a huge solid fundation for building user interfaces. I never enjoyed using any UI framework that much as I did with MaterialUI. Big appreciation and my credits to the MaterialUI team.
85 |
86 | #### Why Redux + Rematch for the state management?
87 |
88 | Redux has became de-facto standart solution for the React state management. Unfortunately you need to write lot's of boilerplate code by default. Rematch is a framework build on top of Redux which simplifies working with redux and eliminates lot's of boilerplate code.
89 |
90 |
91 | #### Can I use Apollo and GraphQL instead of Redux?
92 |
93 | The state shape withing the `_state` directories is designed in a way to be simillar to Appolo Client state provided by It's useState, useMutation hooks.
94 |
95 | To abstract away from the app state management solution implementation the Modular Material Admin components use custom hooks (e.g. useDashboardState, useDashboardEffects).
96 |
97 | So if you want to use Apollo Client with it's recommended way of state management instead of Redux, you should be able to use `useQuery` and `useMutation` hooks instead or inside `useDashboardState` and `useDashboardEffects` without any dramatical changes in UI
98 |
99 | The GraphQL + Apollo learning curve was too steep, that's why I decided to exclude it from the initial version of Modular Material Admin.
100 |
101 | -----
102 |
103 | Not Valid
104 |
105 | #### Why do you use two different approaches for state management?
106 |
107 | Initially I was thinking about using Redux only as a state management with GraphQL Apollo Client as well, but it turns out that Appolo Client is also prefered way to manage the UI state.
108 |
109 | More info available here
110 | https://github.com/rematch/rematch/issues/366
111 | https://www.apollographql.com/docs/react/essentials/local-state/
112 |
113 |
114 | #### So REST or GraphQL?
115 |
116 | I'm trying to build the Modular Material Admin in a way that you would be able to use your desired approach. Each component that needs an ASYNC data, has it's own data usage custom hook, which can be Redux state or Appolo state.
117 | The custom hook of the component abstracts away the state management implementation under the hood, so we can use the preferred method for the state management: Apollo GraphQL or Redux + Rematch
118 |
--------------------------------------------------------------------------------
/docs/references.md:
--------------------------------------------------------------------------------
1 | ## Useful links
2 |
3 | #### Icons
4 | https://material.io/resources/icons/
5 |
6 |
7 | #### Adding polls
8 | https://github.com/isaacs/github/issues/1533
9 |
10 |
11 | #### React
12 | https://www.robinwieruch.de/react-state-usereducer-usestate-usecontext/
13 |
14 | React with Redux and hooks
15 | https://medium.com/@koss_lebedev/rematch-with-hooks-50a8380c46e4
16 |
17 | Layouts
18 | https://stackoverflow.com/questions/33996484/using-multiple-layouts-for-react-router-components
19 | https://gist.github.com/avinmathew/e82fe7e757b20cb337d5219e0ab8dc2c
20 |
21 | Context API vs Redux
22 | https://daveceddia.com/context-api-vs-redux/
23 |
24 |
25 | #### TypeScript
26 |
27 | Typescript Book:
28 | https://basarat.gitbooks.io/typescript/docs/promise.html
29 |
30 | Infering React properties TypeScript definition from React runtime PropTypes
31 | https://dev.to/busypeoples/notes-on-typescript-inferring-react-proptypes-1g88
32 |
33 | https://dev.to/ferdaber/typescript-and-jsx-part-iii---typing-the-props-for-a-component-1pg2
34 | https://dev.to/benweiser/how-to-set-up-eslint-typescript-prettier-with-create-react-app-3675
35 | https://medium.com/@dors718/linting-your-react-typescript-project-with-eslint-and-prettier-2423170c3d42
36 | https://medium.com/quick-code/how-to-integrate-eslint-prettier-in-react-6efbd206d5c4
37 | https://dev.to/robertcoopercode/using-eslint-and-prettier-in-a-typescript-project-53jb
38 |
39 |
40 | #### REST api
41 |
42 | Query interface
43 | https://www.odata.org/
44 | https://swagger.io/
45 | https://swagger.io/resources/open-api/
46 | https://swagger.io/tools/swaggerhub/
47 | https://www.npmjs.com/package/typeorm-express-query-builder
48 | https://www.npmjs.com/package/odata-v4-typeorm
49 | https://www.odata.org/documentation/odata-version-2-0/uri-conventions/
50 |
51 |
52 | #### Misc
53 |
54 | https://archfirst.org/domain-driven-design/
55 |
--------------------------------------------------------------------------------
/docs/roadmap.md:
--------------------------------------------------------------------------------
1 | # Services module
2 |
3 | The services module allows to manage different institutions provided services
4 |
5 | - See the services overview dashboard
6 | - See and manage the orders
7 | - See and manage the payments
8 | - See and manage the provided services and categories
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@modularcode/material-admin-react",
3 | "version": "0.1.0",
4 | "description": "Modular Material Admin + React: Free MaterialUI, ReactJS, TypeScript theme",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/modularcode/modular-material-admin-react.git"
8 | },
9 | "author": "Gevorg Harutyunnyan ",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/modularcode/modular-material-admin-react/issues"
13 | },
14 | "homepage": "http://modular-material-admin-react.modularcode.io",
15 | "dependencies": {
16 | "@material-ui/core": "^4.5.2",
17 | "@material-ui/icons": "^4.5.1",
18 | "@rematch/core": "^1.2.0",
19 | "@rematch/immer": "^1.2.0",
20 | "@types/axios": "^0.14.0",
21 | "@types/chart.js": "^2.8.10",
22 | "@types/jest": "^24.0.21",
23 | "@types/lodash": "^4.14.144",
24 | "@types/moment": "^2.13.0",
25 | "@types/node": "^12.12.3",
26 | "@types/react": "^16.9.11",
27 | "@types/react-dom": "^16.9.3",
28 | "@types/react-redux": "^7.1.5",
29 | "@types/react-router-dom": "^5.1.1",
30 | "@types/store": "^2.0.2",
31 | "@types/uuid": "^3.4.6",
32 | "axios": "^0.19.0",
33 | "axios-mock-adapter": "^1.17.0",
34 | "chart.js": "^2.9.1",
35 | "cross-env": "^6.0.3",
36 | "disqus-react": "^1.0.7",
37 | "eslint-config-react": "^1.1.7",
38 | "gh-pages": "^2.1.1",
39 | "graphql": "^14.5.8",
40 | "lodash": "^4.17.15",
41 | "moment": "^2.24.0",
42 | "node-sass": "^4.13.0",
43 | "opencollective": "^1.0.3",
44 | "react": "^16.11.0",
45 | "react-chartist": "^0.13.3",
46 | "react-dom": "^16.11.0",
47 | "react-redux": "^7.1.1",
48 | "react-router-dom": "^5.1.2",
49 | "react-scripts": "^3.2.0",
50 | "recharts": "^1.8.5",
51 | "redux": "^4.0.4",
52 | "rematch": "^0.1.3",
53 | "reselect": "^4.0.0",
54 | "store": "^2.0.12",
55 | "typeface-roboto": "0.0.75",
56 | "typescript": "^3.6.4",
57 | "uuid": "^3.3.3"
58 | },
59 | "scripts": {
60 | "start": "cross-env TSC_WATCHFILE=UseFsEventsWithFallbackDynamicPolling react-scripts start",
61 | "build": "react-scripts build",
62 | "test": "react-scripts test",
63 | "eject": "react-scripts eject",
64 | "postinstall": "opencollective postinstall",
65 | "deploy": "npm run build && echo modular-material-admin-react.modularcode.io > ./build/CNAME && gh-pages -d build --repo git@github.com:modularcode/modular-material-admin-react.git --branch gh-pages"
66 | },
67 | "eslintConfig": {
68 | "extends": "react-app"
69 | },
70 | "browserslist": {
71 | "production": [
72 | ">0.2%",
73 | "not dead",
74 | "not op_mini all"
75 | ],
76 | "development": [
77 | "last 1 chrome version",
78 | "last 1 firefox version",
79 | "last 1 safari version"
80 | ]
81 | },
82 | "devDependencies": {
83 | "eslint-config-prettier": "^6.5.0",
84 | "eslint-plugin-prettier": "^3.1.1",
85 | "eslint-plugin-react": "^7.16.0",
86 | "prettier": "^1.18.2"
87 | },
88 | "collective": {
89 | "type": "opencollective",
90 | "url": "https://opencollective.com/modular-admin",
91 | "logo": "https://opencollective.com/opencollective/logo.txt"
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 | Modular Material Admin + React
23 |
24 |
25 | You need to enable JavaScript to run this app.
26 |
27 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Modular Material Admin + React",
3 | "name": "Modular Material Admin + React",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/Administration/Accounts/AccountsEditor/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Administration/Accounts/AccountsEditor/.gitkeep
--------------------------------------------------------------------------------
/src/Administration/Accounts/AccountsList/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Administration/Accounts/AccountsList/.gitkeep
--------------------------------------------------------------------------------
/src/Administration/Administration.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Admin = () => {
4 | return Admin
5 | }
6 |
7 | export default Admin
8 |
--------------------------------------------------------------------------------
/src/Administration/README.md:
--------------------------------------------------------------------------------
1 | # Admininstration module
2 |
3 | Allows to manage all system accounts and users. This module might be suitable for the platform owners/admins, who should be able to manage other accounts data
4 |
5 | - View all accounts
6 | - Edit all accounts
7 | - Veiw all users
8 | - Manage all users
9 |
--------------------------------------------------------------------------------
/src/Administration/Users/UsersEditor/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Administration/Users/UsersEditor/.gitkeep
--------------------------------------------------------------------------------
/src/Administration/Users/UsersList/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Administration/Users/UsersList/.gitkeep
--------------------------------------------------------------------------------
/src/Administration/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Administration'
2 |
--------------------------------------------------------------------------------
/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './App'
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div')
7 | ReactDOM.render( , div)
8 | ReactDOM.unmountComponentAtNode(div)
9 | })
10 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Provider } from 'react-redux'
3 | import { ThemeProvider } from '@material-ui/styles'
4 |
5 | import CssBaseline from '@material-ui/core/CssBaseline'
6 |
7 | import store from './_state'
8 | import theme from './_theme'
9 |
10 | import AppRouter from './AppRouter'
11 |
12 | const App: React.FC = () => {
13 | return (
14 |
18 | )
19 | }
20 | export default () => (
21 |
22 |
23 |
24 |
25 |
26 | )
27 |
--------------------------------------------------------------------------------
/src/AppRouter.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import {
3 | HashRouter,
4 | BrowserRouter,
5 | Route,
6 | RouteProps,
7 | Redirect,
8 | Switch,
9 | } from 'react-router-dom' //
10 | import LinearProgress from '@material-ui/core/LinearProgress'
11 |
12 | import config from './_config'
13 | import authService from './_services/authService'
14 | import { useAppState, useAppStateMethods } from './_state/appState'
15 |
16 | import DashboardLayout from '_layouts/DashboardLayout'
17 |
18 | // Import application modules
19 | import Sales from './Sales'
20 | import Content from './Content'
21 | import Administration from './Administration'
22 |
23 | // Import core modules
24 | import Auth from './Auth/Auth'
25 | import Profile from './Profile'
26 | import Organization from './Organization'
27 | import NotFound from './Misc/NotFound'
28 |
29 | // Theme demo module
30 | import Demo from './Demo'
31 |
32 | const LoggedInRouter = () => {
33 | const { loading, error } = useAppState()
34 | const appStateMethods = useAppStateMethods()
35 |
36 | useEffect(() => {
37 | appStateMethods.request()
38 | }, [appStateMethods])
39 |
40 | if (loading) return
41 | if (error) return Error :(
42 |
43 | return (
44 |
45 | } />
46 |
47 |
48 |
53 |
54 |
59 |
60 |
61 |
62 | )
63 | }
64 |
65 | // Use different router type depending on configuration
66 | const AppRouterComponent: React.ComponentType =
67 | config.navigationType === 'history' ? BrowserRouter : HashRouter
68 |
69 | const AppRouter: React.FC = () => (
70 |
71 |
72 |
73 |
74 |
75 |
76 | )
77 |
78 | const RouteWithLayout = ({ component: Component, layout: Layout, ...rest }: any) => (
79 | {
82 | if (Layout) {
83 | return (
84 |
85 |
86 |
87 | )
88 | } else {
89 | return
90 | }
91 | }}
92 | />
93 | )
94 |
95 | // See https://reacttraining.com/react-router/web/example/auth-workflow
96 | const RoutePrivate: React.FC = ({
97 | component: Component,
98 | ...rest
99 | }: RouteProps) => {
100 | if (!Component) {
101 | return
102 | }
103 |
104 | return (
105 |
108 | authService.isAuthenticated() ? (
109 |
110 | ) : (
111 |
116 | )
117 | }
118 | />
119 | )
120 | }
121 |
122 | export default AppRouter
123 |
--------------------------------------------------------------------------------
/src/Auth/Auth.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Auth = () => {
4 | return Auth
5 | }
6 |
7 | export default Auth
8 |
--------------------------------------------------------------------------------
/src/Auth/Login/Login.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Login = () => {
4 | return
5 | }
6 |
7 | export default Login
8 |
--------------------------------------------------------------------------------
/src/Auth/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Auth/README.md
--------------------------------------------------------------------------------
/src/Auth/Recover/Recover.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Recover = () => {
4 | return Recover
5 | }
6 |
7 | export default Recover
8 |
--------------------------------------------------------------------------------
/src/Auth/Reset/Reset.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Reset = () => {
4 | return Reset
5 | }
6 |
7 | export default Reset
8 |
--------------------------------------------------------------------------------
/src/Auth/Signup/Signup.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Signup = () => {
4 | return Signup
5 | }
6 |
7 | export default Signup
8 |
--------------------------------------------------------------------------------
/src/Auth/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Auth'
2 |
--------------------------------------------------------------------------------
/src/Content/CategoriesEditor/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Content/CategoriesEditor/.gitkeep
--------------------------------------------------------------------------------
/src/Content/CategoriesList/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Content/CategoriesList/.gitkeep
--------------------------------------------------------------------------------
/src/Content/Content.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Content = () => {
4 | return Content
5 | }
6 |
7 | export default Content
8 |
--------------------------------------------------------------------------------
/src/Content/ItemsEditor/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Content/ItemsEditor/.gitkeep
--------------------------------------------------------------------------------
/src/Content/ItemsList/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Content/ItemsList/.gitkeep
--------------------------------------------------------------------------------
/src/Content/README.md:
--------------------------------------------------------------------------------
1 | # Content module
2 |
3 | Allows you to manage your content items. Items migh be books, articles, pages, movies, events, and any other content entity.
4 |
5 | You can
6 |
7 | - Veiw content items
8 | - View content categories
9 | - Create/edit content items
10 | - Create/edit content categories
11 |
--------------------------------------------------------------------------------
/src/Content/_api/_data/itemsCategoriesData.js:
--------------------------------------------------------------------------------
1 | export default {}
2 |
--------------------------------------------------------------------------------
/src/Content/_api/_data/itemsData.js:
--------------------------------------------------------------------------------
1 | export default {}
2 |
--------------------------------------------------------------------------------
/src/Content/_api/_data/itemsItemsCategoriesData.js:
--------------------------------------------------------------------------------
1 | export default {}
2 |
--------------------------------------------------------------------------------
/src/Content/_api/_mocks/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Content/_api/_mocks/.gitkeep
--------------------------------------------------------------------------------
/src/Content/_api/itemsCategoriesService.tsx:
--------------------------------------------------------------------------------
1 | const itemsCategoriesService = {}
2 |
3 | export default itemsCategoriesService
4 |
--------------------------------------------------------------------------------
/src/Content/_api/itemsService.tsx:
--------------------------------------------------------------------------------
1 | const itemsService = {}
2 |
3 | export default itemsService
4 |
--------------------------------------------------------------------------------
/src/Content/_types/Item.tsx:
--------------------------------------------------------------------------------
1 | export default interface Item {}
2 |
--------------------------------------------------------------------------------
/src/Content/_types/ItemCategory.tsx:
--------------------------------------------------------------------------------
1 | export default interface ItemCategory {}
2 |
--------------------------------------------------------------------------------
/src/Content/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Content'
2 |
--------------------------------------------------------------------------------
/src/Demo/Components/Components.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import PageContainer from '../../_common/PageContainer'
4 |
5 | const Components = () => {
6 | return Components
7 | }
8 |
9 | export default Components
10 |
--------------------------------------------------------------------------------
/src/Demo/Components/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Components'
2 |
--------------------------------------------------------------------------------
/src/Demo/Demo.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Switch, Route, RouteComponentProps } from 'react-router-dom'
3 |
4 | import Features from './Features'
5 | import Docs from './Docs'
6 | import Supporters from './Supporters'
7 | import Discuss from './Discuss'
8 |
9 | export interface DemoProps extends RouteComponentProps {}
10 |
11 | const Demo: React.FC = ({ match }) => {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default Demo
23 |
--------------------------------------------------------------------------------
/src/Demo/Discuss/Discuss.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { makeStyles, createStyles } from '@material-ui/core/styles'
3 | import Disqus from 'disqus-react'
4 | // import Typography from '@material-ui/core/Typography'
5 | import Paper from '@material-ui/core/Paper'
6 |
7 | import { Theme } from '_theme'
8 | import PageContainer from '../../_common/PageContainer'
9 |
10 | const Discuss = () => {
11 | const classes = useStyles()
12 |
13 | const disqusShortname = 'modularcode-material-admin-react'
14 | const disqusConfig = {
15 | url: 'https://modular-material-admin-react.modularcode.io',
16 | identifier: 'discussion',
17 | title: 'Modular Material Admin + React: Discussion',
18 | }
19 |
20 | return (
21 |
22 | Let's discuss this!
23 |
24 | {/*
25 | Discuss
26 | */}
27 |
28 |
29 |
30 | )
31 | }
32 |
33 | const useStyles = makeStyles((theme: Theme) =>
34 | createStyles({
35 | content: {
36 | padding: theme.spacing(3, 2),
37 | },
38 | }),
39 | )
40 |
41 | export default Discuss
42 |
--------------------------------------------------------------------------------
/src/Demo/Discuss/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Discuss'
2 |
--------------------------------------------------------------------------------
/src/Demo/Docs/Docs.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import PageContainer from '../../_common/PageContainer'
4 |
5 | const Docs = () => {
6 | return (
7 |
8 | Docs
9 |
10 | )
11 | }
12 |
13 | export default Docs
14 |
--------------------------------------------------------------------------------
/src/Demo/Docs/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Docs'
2 |
--------------------------------------------------------------------------------
/src/Demo/Features/Features.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import PageContainer from '../../_common/PageContainer'
4 |
5 | const Features = () => {
6 | return (
7 |
8 | Features
9 |
10 | )
11 | }
12 |
13 | export default Features
14 |
--------------------------------------------------------------------------------
/src/Demo/Features/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Features'
2 |
--------------------------------------------------------------------------------
/src/Demo/README.md:
--------------------------------------------------------------------------------
1 | # Demo module
2 |
3 | This module is only for the demo purposes and demonstrating theme features, etc
4 |
--------------------------------------------------------------------------------
/src/Demo/Supporters/Supporters.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import PageContainer from '../../_common/PageContainer'
4 |
5 | const Supporters = () => {
6 | return (
7 |
8 | Supporters
9 |
14 | Support me on Patreon to release this project faster!
15 |
16 |
17 |
21 |
22 |
23 | )
24 | }
25 |
26 | export default Supporters
27 |
--------------------------------------------------------------------------------
/src/Demo/Supporters/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Supporters'
2 |
--------------------------------------------------------------------------------
/src/Demo/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Demo'
2 |
--------------------------------------------------------------------------------
/src/Misc/NotFound/NotFound.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import PageContainer from '../../_common/PageContainer'
4 |
5 | const NotFound = () => {
6 | return Page Not Found!
7 | }
8 |
9 | export default NotFound
10 |
--------------------------------------------------------------------------------
/src/Misc/NotFound/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './NotFound'
2 |
--------------------------------------------------------------------------------
/src/Misc/README.md:
--------------------------------------------------------------------------------
1 | # Misc module
2 |
--------------------------------------------------------------------------------
/src/Organization/Organization.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Account = () => {
4 | return Account
5 | }
6 |
7 | export default Account
8 |
--------------------------------------------------------------------------------
/src/Organization/Preview/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Organization/Preview/.gitkeep
--------------------------------------------------------------------------------
/src/Organization/README.md:
--------------------------------------------------------------------------------
1 | # Account module
2 |
3 | Allows to manage a single account
4 |
5 | - Update account settings (profile picture, info, etc, account owner)
6 | - Manage account users
7 | - Preview the account
8 |
--------------------------------------------------------------------------------
/src/Organization/Settings/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Organization/Settings/.gitkeep
--------------------------------------------------------------------------------
/src/Organization/Users/UsersEditor/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Organization/Users/UsersEditor/.gitkeep
--------------------------------------------------------------------------------
/src/Organization/Users/UsersList/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Organization/Users/UsersList/.gitkeep
--------------------------------------------------------------------------------
/src/Organization/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Organization'
2 |
--------------------------------------------------------------------------------
/src/Profile/Preview/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Profile/Preview/.gitkeep
--------------------------------------------------------------------------------
/src/Profile/Profile.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Profile = () => {
4 | return Profile
5 | }
6 |
7 | export default Profile
8 |
--------------------------------------------------------------------------------
/src/Profile/README.md:
--------------------------------------------------------------------------------
1 | # Profile module
2 |
3 | Allows to manage a single profile
4 |
5 | - Update profile settings (email, password, notifications, profile picture, info, etc)
6 | - Preview the profile
7 |
8 |
9 | One profile can belong to multiple accounts
10 |
--------------------------------------------------------------------------------
/src/Profile/Settings/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Profile/Settings/.gitkeep
--------------------------------------------------------------------------------
/src/Profile/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Profile'
2 |
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | # App module
2 |
3 | This is the application entry component. It handles the main routing and authentication redirects.
4 | Sub modules
5 |
6 | ### Account
7 | single account management module
8 |
9 | ### Admin
10 | all accounts and users management module (convenient for superusers, can be extended)
11 |
12 | ### Content
13 | content management module (useful for creating various entities like books, articles, posts, pages, etc)
14 |
15 | ### Demo
16 | theme demo pages
17 |
18 | ### NotFound
19 | Fallback page, if route doesn't match any module
20 |
21 | ### Profile
22 | logged-in user profile management module
23 |
24 | ### Sales
25 | can be used for managing sales/orders
26 |
--------------------------------------------------------------------------------
/src/Sales/Customers/Customers.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | // import clsx from 'clsx'
3 | import { makeStyles } from '@material-ui/core/styles'
4 |
5 | import PageContainer from '../../_common/PageContainer'
6 |
7 | // import Grid from '@material-ui/core/Grid'
8 | import Paper from '@material-ui/core/Paper'
9 |
10 | import { Theme } from '_theme'
11 |
12 | export default function Customers() {
13 | const classes = useStyles()
14 |
15 | return (
16 |
17 | Customers
18 |
19 | )
20 | }
21 |
22 | const useStyles = makeStyles((theme: Theme) => ({}))
23 |
--------------------------------------------------------------------------------
/src/Sales/Customers/CustomersEditor/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Sales/Customers/CustomersEditor/.gitkeep
--------------------------------------------------------------------------------
/src/Sales/Customers/CustomersList/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Sales/Customers/CustomersList/.gitkeep
--------------------------------------------------------------------------------
/src/Sales/Customers/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Customers'
2 |
--------------------------------------------------------------------------------
/src/Sales/Locations/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Sales/Locations/.gitkeep
--------------------------------------------------------------------------------
/src/Sales/Orders/Orders.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import Grid from '@material-ui/core/Grid'
4 | import Paper from '@material-ui/core/Paper'
5 | import Button from '@material-ui/core/Button'
6 |
7 | // import { Theme } from '_theme'
8 |
9 | import PageContainer from '../../_common/PageContainer'
10 | import PageToolbar from '../../_common/PageToolbar'
11 |
12 | const Orders = () => {
13 | const PageTitle = 'Orders'
14 |
15 | const PageActions = (
16 |
17 | Link
18 |
19 | )
20 |
21 | return (
22 |
23 |
24 |
25 | )
26 | }
27 |
28 | export default Orders
29 |
--------------------------------------------------------------------------------
/src/Sales/Orders/OrdersEditor/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Sales/Orders/OrdersEditor/.gitkeep
--------------------------------------------------------------------------------
/src/Sales/Orders/OrdersList/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Sales/Orders/OrdersList/.gitkeep
--------------------------------------------------------------------------------
/src/Sales/Orders/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Orders'
2 |
--------------------------------------------------------------------------------
/src/Sales/Overview/OrdersHistory/OrdersHistory.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, useContext } from 'react' //useState
2 | import { makeStyles } from '@material-ui/core/styles'
3 | import Card from '@material-ui/core/Card'
4 | import CardContent from '@material-ui/core/CardContent'
5 | import { Chart } from 'chart.js'
6 |
7 | import overviewContext from '../overviewContext'
8 | import mainHistoryService from './ordersHistoryService'
9 |
10 | // ref: https://www.robinwieruch.de/react-hooks-fetch-data/
11 | const MainHistory = () => {
12 | const { filter } = useContext(overviewContext)
13 | const canvasRef = useRef(null)
14 | const classes = useStyles()
15 |
16 | useEffect(() => {
17 | if (canvasRef.current === null) {
18 | return
19 | }
20 |
21 | async function renderChart(canvasRefNode: HTMLCanvasElement) {
22 | if (!canvasRef || !filter) {
23 | return
24 | }
25 |
26 | // console.log('filter', filter)
27 |
28 | const chartConfigurationRes = await mainHistoryService.getChartConfiguration({
29 | filter,
30 | })
31 |
32 | new Chart(canvasRefNode, chartConfigurationRes)
33 | }
34 |
35 | renderChart(canvasRef.current)
36 | }, [canvasRef, filter])
37 |
38 | return (
39 |
40 |
41 | Order History
42 |
43 |
44 |
45 | )
46 | }
47 |
48 | const useStyles = makeStyles(theme => ({
49 | content: {},
50 | chart: {
51 | width: '100%',
52 | height: '200px',
53 | },
54 | }))
55 |
56 | export default MainHistory
57 |
--------------------------------------------------------------------------------
/src/Sales/Overview/OrdersHistory/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './OrdersHistory'
2 |
--------------------------------------------------------------------------------
/src/Sales/Overview/OrdersHistory/ordersHistoryService.ts:
--------------------------------------------------------------------------------
1 | import { Chart } from 'chart.js'
2 | import { random } from 'lodash'
3 | import moment from 'moment'
4 |
5 | // import ordersData from '../../_api/_data/ordersData'
6 | import api from '../../_api'
7 | import { SalesDahsboardContextFilter } from '../overviewContext'
8 |
9 | export interface GetChartDataOptions {
10 | filter: SalesDahsboardContextFilter
11 | }
12 |
13 | export default {
14 | getRandomNumber(min = 0, max = 1): number {
15 | return random(min, max)
16 | },
17 | async getChartData({ filter }: GetChartDataOptions): Promise {
18 | interface ChartDataSet extends Chart.ChartDataSets {
19 | data: number[]
20 | }
21 |
22 | const orders = await api.orders.getSum({
23 | $select: [['func_sum', '']],
24 | $filter: {
25 | createdAt: {
26 | ge: filter.dateFrom,
27 | le: filter.dateTo,
28 | },
29 | },
30 | $groupBy: [['func_date_day', 'createdAt']],
31 | })
32 |
33 | console.log('orders', orders)
34 |
35 | // const timeFormat = 'MM/DD/YYYY'
36 |
37 | // const today = moment().startOf('day')
38 | // const oneMonthAgo = moment(today).subtract(15, 'days')
39 |
40 | const labels: string[] = []
41 | // const signups: ChartDataSet = {
42 | // type: 'bar',
43 | // label: 'Signups',
44 | // backgroundColor: '#e8e8e8',
45 | // data: [],
46 | // }
47 | const ordersCount: ChartDataSet = {
48 | type: 'line',
49 | label: 'Number of orders',
50 | backgroundColor: '#1e88e5',
51 | borderColor: '#1e88e5',
52 | fill: false,
53 | data: [],
54 | }
55 | const ordersSum: ChartDataSet = {
56 | type: 'line',
57 | label: 'Total orders $',
58 | backgroundColor: '#95de3c',
59 | borderColor: '#95de3c',
60 | fill: false,
61 | data: [],
62 | yAxisID: 'y-axis-2',
63 | }
64 |
65 | // for (let now = moment(oneMonthAgo); now.isSameOrBefore(today); now.add(1, 'day')) {
66 | // labels.push(now.format(timeFormat))
67 |
68 | // const signupsValue = random(10, 150)
69 | // const ordersCountValue = random(0, 25)
70 | // const ordersSumValue = random(10, 315)
71 |
72 | // signups.data.push(signupsValue)
73 | // ordersCount.data.push(ordersCountValue)
74 | // ordersSum.data.push(ordersSumValue)
75 | // }
76 |
77 | return {
78 | labels,
79 | datasets: [ordersSum, ordersCount], // signups
80 | }
81 | },
82 | getChartOptions(options: GetChartDataOptions): Chart.ChartOptions {
83 | const timeFormat = 'MM/DD/YYYY'
84 |
85 | return {
86 | // responsive: true,
87 | title: {
88 | text: 'Chart.js Combo Time Scale',
89 | },
90 | scales: {
91 | xAxes: [
92 | {
93 | type: 'time',
94 | display: true,
95 | time: {
96 | parser: timeFormat,
97 | },
98 | },
99 | ],
100 | yAxes: [
101 | {
102 | type: 'linear',
103 | display: true,
104 | position: 'left',
105 | id: 'y-axis-1',
106 | },
107 | {
108 | type: 'linear',
109 | display: true,
110 | position: 'right',
111 | id: 'y-axis-2',
112 | // grid line settings
113 | gridLines: {
114 | drawOnChartArea: false, // only want the grid lines for one axis to show up
115 | },
116 | ticks: {
117 | // Include a dollar sign in the ticks
118 | callback: function(value, index, values) {
119 | return '$' + value
120 | },
121 | },
122 | },
123 | ],
124 | },
125 | tooltips: {
126 | mode: 'index',
127 | intersect: false,
128 | },
129 | hover: {
130 | mode: 'index',
131 | intersect: false,
132 | },
133 | }
134 | },
135 | // In real life this would be an http call
136 | async getChartConfiguration(
137 | options: GetChartDataOptions,
138 | ): Promise {
139 | const configuration = {
140 | type: 'bar',
141 | options: this.getChartOptions(options),
142 | data: await this.getChartData(options),
143 | }
144 |
145 | return new Promise(resolve => setTimeout(() => resolve(configuration), 300))
146 | },
147 | }
148 |
--------------------------------------------------------------------------------
/src/Sales/Overview/OrdersLatest/OrdersLatest.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-script-url */
2 |
3 | import React from 'react'
4 | import Link from '@material-ui/core/Link'
5 | import { makeStyles } from '@material-ui/core/styles'
6 | import Card from '@material-ui/core/Card'
7 | import CardContent from '@material-ui/core/CardContent'
8 | import Table from '@material-ui/core/Table'
9 | import TableBody from '@material-ui/core/TableBody'
10 | import TableCell from '@material-ui/core/TableCell'
11 | import TableHead from '@material-ui/core/TableHead'
12 | import TableRow from '@material-ui/core/TableRow'
13 |
14 | // Generate Order Data
15 | function createData(
16 | id: number,
17 | date: string,
18 | name: string,
19 | shipTo: string,
20 | paymentMethod: string,
21 | amount: number,
22 | ) {
23 | return { id, date, name, shipTo, paymentMethod, amount }
24 | }
25 |
26 | const rows = [
27 | createData(0, '16 Mar, 2019', 'Elvis Presley', 'Tupelo, MS', 'VISA ⠀•••• 3719', 312.44),
28 | createData(
29 | 1,
30 | '16 Mar, 2019',
31 | 'Paul McCartney',
32 | 'London, UK',
33 | 'VISA ⠀•••• 2574',
34 | 866.99,
35 | ),
36 | createData(2, '16 Mar, 2019', 'Tom Scholz', 'Boston, MA', 'MC ⠀•••• 1253', 100.81),
37 | createData(3, '16 Mar, 2019', 'Michael Jackson', 'Gary, IN', 'AMEX ⠀•••• 2000', 654.39),
38 | createData(
39 | 4,
40 | '15 Mar, 2019',
41 | 'Bruce Springsteen',
42 | 'Long Branch, NJ',
43 | 'VISA ⠀•••• 5919',
44 | 212.79,
45 | ),
46 | ]
47 |
48 | const MainOrders: React.FC = () => {
49 | const classes = useStyles()
50 | return (
51 |
52 |
53 |
54 |
55 |
56 | Date
57 | Name
58 | Ship To
59 | Payment Method
60 | Sale Amount
61 |
62 |
63 |
64 | {rows.map(row => (
65 |
66 | {row.date}
67 | {row.name}
68 | {row.shipTo}
69 | {row.paymentMethod}
70 | {row.amount}
71 |
72 | ))}
73 |
74 |
75 |
76 |
77 | See more orders
78 |
79 |
80 |
81 |
82 | )
83 | }
84 |
85 | const useStyles = makeStyles(theme => ({
86 | seeMore: {
87 | marginTop: theme.spacing(3),
88 | },
89 | }))
90 |
91 | export default MainOrders
92 |
--------------------------------------------------------------------------------
/src/Sales/Overview/OrdersLatest/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './OrdersLatest'
2 |
--------------------------------------------------------------------------------
/src/Sales/Overview/Overview.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import moment from 'moment'
3 | // import clsx from 'clsx'
4 | // import { makeStyles } from '@material-ui/core/styles'
5 |
6 | import Grid from '@material-ui/core/Grid'
7 |
8 | import PageContainer from '../../_common/PageContainer'
9 | import PageToolbar from '../../_common/PageToolbar'
10 |
11 | import { SalesDashboardProvider } from './overviewContext'
12 |
13 | import SalesDashboardActions from './OverviewActions'
14 | import OrdersHistory from './OrdersHistory'
15 | import OrdersLatest from './OrdersLatest'
16 |
17 | export default function SalesDashboard() {
18 | const [filter, setFilter] = useState({
19 | dateFrom: moment()
20 | .subtract(14, 'day')
21 | .startOf('day'),
22 | dateTo: moment().startOf('day'),
23 | })
24 |
25 | const PageTitle = 'Sales Dashboard'
26 |
27 | return (
28 |
29 |
30 |
34 |
35 |
36 | {/* Chart */}
37 |
38 |
39 |
40 | {/* Recent Deposits */}
41 | {/*
42 |
43 |
44 |
45 | */}
46 | {/* Recent Orders */}
47 |
48 |
49 |
50 |
51 |
52 |
53 | )
54 | }
55 |
56 | // const useStyles = makeStyles((theme: Theme) => ({
57 | // container: {
58 | // paddingTop: theme.spacing(4),
59 | // paddingBottom: theme.spacing(4),
60 | // },
61 | // }))
62 |
--------------------------------------------------------------------------------
/src/Sales/Overview/OverviewActions.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext } from 'react'
2 |
3 | import { makeStyles } from '@material-ui/core/styles'
4 | import Button from '@material-ui/core/Button'
5 | import Tooltip from '@material-ui/core/Tooltip'
6 | import IconMore from '@material-ui/icons/MoreVert'
7 | import IconFilter from '@material-ui/icons/Tune'
8 | import IconDropDown from '@material-ui/icons/ArrowDropDown'
9 | import IconNew from '@material-ui/icons/Add'
10 |
11 | import { Theme } from '../../_theme'
12 | import salesDashboardContext from './overviewContext'
13 |
14 | const MainActions: React.FC = () => {
15 | const classes = useStyles()
16 | const { filter, setFilter } = useContext(salesDashboardContext)
17 |
18 | const dateFilterLabel = filter
19 | ? `${filter.dateFrom.format('ll')} - ${filter.dateTo.format('ll')}`
20 | : 'Date Filter'
21 |
22 | return (
23 |
24 |
25 |
26 | {dateFilterLabel}
27 |
28 |
29 |
30 |
31 |
32 | New
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | )
47 | }
48 |
49 | const useStyles = makeStyles((theme: Theme) => ({
50 | iconNew: {
51 | marginRight: 5,
52 | },
53 | }))
54 |
55 | export default MainActions
56 |
--------------------------------------------------------------------------------
/src/Sales/Overview/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Overview'
2 |
--------------------------------------------------------------------------------
/src/Sales/Overview/overviewContext.ts:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import moment, { Moment } from 'moment'
3 |
4 | export interface SalesDahsboardContextFilter {
5 | dateFrom: Moment
6 | dateTo: Moment
7 | }
8 |
9 | export interface SalesDahsboardContext {
10 | filter?: SalesDahsboardContextFilter
11 | setFilter?: any
12 | }
13 |
14 | // The default context, which is used when there is no provider
15 | // (might be used for components testing)
16 | export const salesDashboardContextDefault: SalesDahsboardContext = {
17 | filter: {
18 | dateFrom: moment()
19 | .subtract(14, 'day')
20 | .startOf('day'),
21 | dateTo: moment().startOf('day'),
22 | },
23 | }
24 |
25 | export const salesDashboardContext = React.createContext(
26 | salesDashboardContextDefault,
27 | )
28 |
29 | export const SalesDashboardProvider = salesDashboardContext.Provider
30 | export const SalesDashboardConsumer = salesDashboardContext.Consumer
31 |
32 | export default salesDashboardContext
33 |
--------------------------------------------------------------------------------
/src/Sales/Payments/Payments.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Patments = () => {
4 | return
5 | }
6 |
7 | export default Patments
8 |
--------------------------------------------------------------------------------
/src/Sales/Products/ProductsCategoriesEditor/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Sales/Products/ProductsCategoriesEditor/.gitkeep
--------------------------------------------------------------------------------
/src/Sales/Products/ProductsCategoriesList/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Sales/Products/ProductsCategoriesList/.gitkeep
--------------------------------------------------------------------------------
/src/Sales/Products/ProductsEditor/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Sales/Products/ProductsEditor/.gitkeep
--------------------------------------------------------------------------------
/src/Sales/Products/ProductsList/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Sales/Products/ProductsList/.gitkeep
--------------------------------------------------------------------------------
/src/Sales/README.md:
--------------------------------------------------------------------------------
1 | # Sales module
2 |
3 | The sales module is a good if you're planning to sell some goods in your system.
4 | It allows you to
5 |
6 | - See the sales overview dashboard
7 | - See and manage the orders
8 | - See and manage the payments
9 | - See and manage the products and categories
10 | - See and manage the stock of products (for physical products)
11 | - See and manage your products locations (stores, warehouses, shops, trading points, branches, etc)
12 |
13 | Use cases
14 |
15 | - Single store
16 | - Stores network
17 | - Delivery service
18 |
--------------------------------------------------------------------------------
/src/Sales/Sales.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Switch, Route, RouteComponentProps } from 'react-router-dom'
3 |
4 | import Overview from './Overview'
5 | import Orders from './Orders'
6 | import Customers from './Customers'
7 |
8 | export interface SalesProps extends RouteComponentProps {}
9 |
10 | const Sales = ({ match }: SalesProps) => {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 | )
18 | }
19 |
20 | export default Sales
21 |
--------------------------------------------------------------------------------
/src/Sales/SalesService.tsx:
--------------------------------------------------------------------------------
1 | export default {}
2 |
--------------------------------------------------------------------------------
/src/Sales/Stock/Stock.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Stock = () => {
4 | return Products Stock
5 | }
6 |
7 | export default Stock
8 |
--------------------------------------------------------------------------------
/src/Sales/Stores/Stores.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Warehouses = () => {
4 | return Warehouses
5 | }
6 |
7 | export default Warehouses
8 |
--------------------------------------------------------------------------------
/src/Sales/_api/_data/categoriesData.ts:
--------------------------------------------------------------------------------
1 | import _keyBy from 'lodash/keyBy'
2 | import Category from '../../_types/ProductCategory'
3 |
4 | /*
5 | Electronics, Computers & Office
6 | ELECTRONICS
7 | TV & Video
8 | Home Audio & Theater
9 | Cell Phones & Accessories
10 | Wearable Technology
11 | COMPUTERS
12 | Computers, Tablets, & PC Products
13 | Monitors
14 | Drives & Storage
15 | Books & Audible
16 | Sports & Outdoors
17 | */
18 |
19 | const list: Category[] = [
20 | {
21 | id: 1,
22 | name: 'Electronics, Computers & Office',
23 | },
24 | {
25 | id: 2,
26 | name: 'Electronics',
27 | parentId: 1,
28 | },
29 | {
30 | id: 3,
31 | name: 'TV & Video',
32 | parentId: 2,
33 | },
34 | {
35 | id: 4,
36 | name: 'Home Audio & Theater',
37 | parentId: 2,
38 | },
39 | {
40 | id: 5,
41 | name: 'Cell Phones & Accessories',
42 | parentId: 2,
43 | },
44 | {
45 | id: 6,
46 | name: 'Wearable Technology',
47 | parentId: 2,
48 | },
49 | {
50 | id: 7,
51 | name: 'Computers',
52 | parentId: 1,
53 | },
54 | {
55 | id: 8,
56 | name: 'Computers, Tablets, & PC Products',
57 | parentId: 7,
58 | },
59 | {
60 | id: 9,
61 | name: 'Monitors',
62 | parentId: 7,
63 | },
64 | {
65 | id: 10,
66 | name: 'Drives & Storage',
67 | parentId: 7,
68 | },
69 | {
70 | id: 11,
71 | name: 'Books & Audible',
72 | },
73 | {
74 | id: 11,
75 | name: 'Sports & Outdoors',
76 | },
77 | ]
78 |
79 | const byId: { [key: number]: Category } = _keyBy(list, 'id')
80 |
81 | export default {
82 | list,
83 | byId,
84 | }
85 |
--------------------------------------------------------------------------------
/src/Sales/_api/_data/customersData.ts:
--------------------------------------------------------------------------------
1 | export default {}
2 |
--------------------------------------------------------------------------------
/src/Sales/_api/_data/locationsData.ts:
--------------------------------------------------------------------------------
1 | export default {}
2 |
--------------------------------------------------------------------------------
/src/Sales/_api/_data/ordersData.ts:
--------------------------------------------------------------------------------
1 | import uuidv4 from 'uuid/v4'
2 | import moment, { Moment } from 'moment'
3 | import _keyBy from 'lodash/keyBy'
4 | import _sortBy from 'lodash/sortBy'
5 | import _random from 'lodash/random'
6 | import _shuffle from 'lodash/shuffle'
7 | import Order, { OrderStatus } from '../../_types/Order'
8 | import productsData from './productsData'
9 |
10 | const orderStatuses: OrderStatus[] = [
11 | 'received',
12 | 'preparing',
13 | 'shipped',
14 | 'delivered',
15 | 'rejected',
16 | 'refunded',
17 | ]
18 |
19 | // Generate orders data for lat 90 days (do it one per browser session)
20 | const list = generateRandomOrdersHistory(90)
21 | const byId: { [key: number]: Order } = _keyBy(list, 'id')
22 |
23 | export default {
24 | orderStatuses,
25 | list,
26 | byId,
27 | }
28 |
29 | export function generateRandomOrdersHistory(numDays: number) {
30 | let list: Order[] = []
31 | const now = moment()
32 |
33 | for (
34 | let date = moment().subtract(numDays, 'days'), dayNumber = 1;
35 | date.isSameOrBefore(now);
36 | date.add(1, 'day'), dayNumber++
37 | ) {
38 | const dailyOrders = generateDailyRandomOrders(date)
39 |
40 | list = list.concat(dailyOrders)
41 | }
42 |
43 | return list
44 | }
45 |
46 | export function generateDailyRandomOrders(date: Moment): Order[] {
47 | // Ensure the orders are date sorted
48 | return _sortBy(
49 | new Array(_random(0, 10)).fill(undefined).map(num => generateRandomOrder(date)),
50 | order => moment(order.createdAt).toDate(),
51 | )
52 | }
53 |
54 | export function generateRandomOrder(date: Moment): Order {
55 | const shuffledProducts = _shuffle(productsData.list)
56 | const numProducts = _random(1, 3)
57 | const orderProductItems = shuffledProducts.slice(0, numProducts)
58 | const orderProducts = orderProductItems.map(product => ({
59 | id: product.id,
60 | price: product.price,
61 | quantity: _random(1, 2),
62 | }))
63 | const orderSum = orderProducts.reduce((sum, { price = 0, quantity = 0 }) => {
64 | return sum + price * quantity
65 | }, 0)
66 | const isSuccessfulOrder = _random(1, 10) > 2
67 | const orderStatus = isSuccessfulOrder
68 | ? orderStatuses[_random(0, 3)]
69 | : orderStatuses[_random(4, 5)]
70 | const orderDate = moment(date).set({
71 | hour: _random(0, 23),
72 | minute: _random(0, 59),
73 | second: _random(0, 59),
74 | })
75 |
76 | const order = {
77 | id: uuidv4(),
78 | products: orderProducts,
79 | sum: orderSum,
80 | status: orderStatus,
81 | createdAt: orderDate.format(),
82 | updatedAt: orderDate.format(),
83 | }
84 |
85 | return order
86 | }
87 |
--------------------------------------------------------------------------------
/src/Sales/_api/_data/paymentsData.ts:
--------------------------------------------------------------------------------
1 | export default {}
2 |
--------------------------------------------------------------------------------
/src/Sales/_api/_data/productsData.ts:
--------------------------------------------------------------------------------
1 | import _keyBy from 'lodash/keyBy'
2 | import Product from '../../_types/Product'
3 |
4 | const list: Product[] = [
5 | {
6 | id: 1,
7 | name: 'Simple Mobile Prepaid - Apple iPhone 6s (32GB) - Space Gray',
8 | price: 300,
9 | },
10 | {
11 | id: 2,
12 | name: 'iPhoneXS',
13 | price: 399,
14 | },
15 | {
16 | id: 3,
17 | name: 'Apple iPhone 6, GSM Unlocked, 64GB - Space Gray (Renewed)',
18 | price: 500,
19 | },
20 | {
21 | id: 4,
22 | name: 'All-New Fire 7 Tablet (7" display, 16 GB) - Black',
23 | images: [
24 | {
25 | url: 'https://images-na.ssl-images-amazon.com/images/I/61N1v5re7SL._SL1000_.jpg',
26 | },
27 | ],
28 | price: 99.99,
29 | },
30 | {
31 | id: 5,
32 | name:
33 | 'Echo (2nd Generation) - Smart speaker with Alexa and Dolby processing - Sandstone Fabric',
34 | images: [
35 | {
36 | url: 'https://images-na.ssl-images-amazon.com/images/I/610d8E4usyL._SL1000_.jpg',
37 | },
38 | ],
39 | price: 49.99,
40 | },
41 | ]
42 |
43 | const byId: { [key: number]: Product } = _keyBy(list, 'id')
44 |
45 | const productsData = {
46 | list,
47 | byId,
48 | }
49 |
50 | export default productsData
51 |
--------------------------------------------------------------------------------
/src/Sales/_api/_data/productsRelCategoriesData.ts:
--------------------------------------------------------------------------------
1 | export default {}
2 |
--------------------------------------------------------------------------------
/src/Sales/_api/_data/stockActionsData.ts:
--------------------------------------------------------------------------------
1 | export default {}
2 |
--------------------------------------------------------------------------------
/src/Sales/_api/_data/stocksData.ts:
--------------------------------------------------------------------------------
1 | export default {}
2 |
--------------------------------------------------------------------------------
/src/Sales/_api/_mocks/index.ts:
--------------------------------------------------------------------------------
1 | import MockAdapter from 'axios-mock-adapter'
2 |
3 | import ordersMocks from './ordersMocks'
4 |
5 | const init = (mockAdapter: MockAdapter) => {
6 | ordersMocks.init(mockAdapter)
7 | }
8 |
9 | export default {
10 | init,
11 | }
12 |
--------------------------------------------------------------------------------
/src/Sales/_api/_mocks/ordersMocks.ts:
--------------------------------------------------------------------------------
1 | import uuidv4 from 'uuid/v4'
2 | import moment from 'moment'
3 | import MockAdapter from 'axios-mock-adapter'
4 | import ordersData from '../_data/ordersData'
5 |
6 | export default {
7 | init(mock: MockAdapter) {
8 | mock.onGet('/orders').reply((config: any) => {
9 | console.log('request config:', config)
10 |
11 | const matchingOrders = ordersData.list
12 | const ordersRes = matchingOrders.slice(0, 49)
13 |
14 | return [
15 | 200,
16 | {
17 | orders: ordersRes,
18 | count: matchingOrders.length,
19 | },
20 | ]
21 | })
22 |
23 | //
24 | mock.onGet(/\/orders\/.*?/).reply((config: any) => {
25 | console.log('request config:', config)
26 |
27 | const urlPaths = config.url.split('/')
28 | const orderId = urlPaths[urlPaths.length - 1]
29 | const order = ordersData.byId[orderId]
30 |
31 | if (order) {
32 | return [200, { ...order }]
33 | } else {
34 | return [404, { message: 'User not found ' }]
35 | }
36 | })
37 |
38 | mock.onPut(/\/orders\/.*?/).reply((config: any) => {
39 | const urlPaths = config.url.split('/')
40 | const orderId = urlPaths[urlPaths.length - 1]
41 | const orderData = JSON.parse(config.data)
42 | const order = ordersData.byId[orderId]
43 |
44 | if (order) {
45 | return [200, { ...order, ...orderData }]
46 | } else {
47 | return [403, { message: 'Update not permitted' }]
48 | }
49 | })
50 |
51 | mock.onPost(/\/orders/).reply((config: any) => {
52 | const orderData = JSON.parse(config.data)
53 | const order = {
54 | ...orderData,
55 | id: uuidv4(),
56 | createdAt: moment().format(),
57 | updatedAt: moment().format(),
58 | }
59 |
60 | return [200, order]
61 | })
62 |
63 | mock.onDelete(/\/orders\/\d+/).reply((config: any) => {
64 | return [200, { message: 'Order removed' }]
65 | })
66 | },
67 | }
68 |
--------------------------------------------------------------------------------
/src/Sales/_api/customers.ts:
--------------------------------------------------------------------------------
1 | const customersService = {}
2 |
3 | export default customersService
4 |
--------------------------------------------------------------------------------
/src/Sales/_api/index.ts:
--------------------------------------------------------------------------------
1 | import { AxiosInstance } from 'axios'
2 | import { ApiInitOptions } from '_api'
3 |
4 | import mocks from './_mocks'
5 | import orders from './orders'
6 | import MockAdapter from 'axios-mock-adapter/types'
7 |
8 | export interface SalesApiInitOptions extends ApiInitOptions {
9 | instance: AxiosInstance
10 | mockAdapter?: MockAdapter
11 | }
12 |
13 | const init = (options: SalesApiInitOptions) => {
14 | if (options.useSampleData && options.mockAdapter) {
15 | mocks.init(options.mockAdapter)
16 | }
17 | }
18 |
19 | export default { init, orders }
20 | export { init, orders }
21 |
--------------------------------------------------------------------------------
/src/Sales/_api/orders.ts:
--------------------------------------------------------------------------------
1 | import { AxiosResponse } from 'axios'
2 | import Order, { OrderSubmissionData, OrderId } from '../_types/Order'
3 | import apiClient from '_api/client'
4 |
5 | export interface OrdersService {
6 | get(params?: any): Promise
7 | getList(params?: any): Promise
8 | getSum(params?: any): Promise
9 | getCount(params?: any): Promise
10 | getOne(orderId: OrderId): Promise
11 | create(order: OrderSubmissionData): Promise
12 | update(orderId: OrderId, order: OrderSubmissionData): Promise
13 | remove(orderId: OrderId): Promise
14 | }
15 |
16 | export interface OrdersListResponse {
17 | orders: Order[]
18 | count: number
19 | }
20 |
21 | const ordersService: OrdersService = {
22 | get(params: any) {
23 | return apiClient
24 | .get(`/orders`, {
25 | params,
26 | })
27 | .then((res: AxiosResponse) => res.data)
28 | },
29 | getList(params: any) {
30 | return apiClient
31 | .get(`/orders`, {
32 | params,
33 | })
34 | .then((res: AxiosResponse) => res.data)
35 | },
36 | getSum(params: any) {
37 | return apiClient
38 | .get(`/orders/sum`, {
39 | params,
40 | })
41 | .then((res: AxiosResponse) => res.data)
42 | },
43 | getCount(params: any) {
44 | return apiClient
45 | .get(`/orders/count`, {
46 | params,
47 | })
48 | .then((res: AxiosResponse) => res.data)
49 | },
50 | getOne(orderId) {
51 | return apiClient
52 | .get(`/orders/${orderId}`)
53 | .then((res: AxiosResponse) => res.data)
54 | },
55 | create(order: OrderSubmissionData) {
56 | return apiClient.post(`/orders`, order).then((res: AxiosResponse) => res.data)
57 | },
58 | update(orderId, order) {
59 | return apiClient
60 | .put(`/orders/${orderId}`, order)
61 | .then((res: AxiosResponse) => res.data)
62 | },
63 | remove(orderId) {
64 | return apiClient
65 | .delete(`/orders/${orderId}`)
66 | .then((res: AxiosResponse) => res.data)
67 | },
68 | }
69 |
70 | export default ordersService
71 |
--------------------------------------------------------------------------------
/src/Sales/_api/products.ts:
--------------------------------------------------------------------------------
1 | const productsService = {}
2 |
3 | export default productsService
4 |
--------------------------------------------------------------------------------
/src/Sales/_api/productsCategories.ts:
--------------------------------------------------------------------------------
1 | const productsCategoriesService = {}
2 |
3 | export default productsCategoriesService
4 |
--------------------------------------------------------------------------------
/src/Sales/_services/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/Sales/_services/.gitkeep
--------------------------------------------------------------------------------
/src/Sales/_types/Customer.ts:
--------------------------------------------------------------------------------
1 | import { EntityId } from '_types/Entity'
2 |
3 | export type CustomerId = EntityId
4 |
5 | export default interface Customer {
6 | id?: CustomerId
7 | name?: string
8 | email?: string
9 | details?: object
10 | }
11 |
--------------------------------------------------------------------------------
/src/Sales/_types/EntityOwned.ts:
--------------------------------------------------------------------------------
1 | import Entity, { EntityId } from '_types/Entity'
2 | import { UserId } from '_types/User'
3 | import { OrganizationId } from '_types/Organization'
4 |
5 | export type EntitiyOwnedId = EntityId
6 |
7 | // The entity may be owned by an organization or an individual user
8 | export default interface EntityOwned extends Entity {
9 | id?: EntityId
10 | ownerUserId?: UserId
11 | owenrOrganizationId?: OrganizationId
12 | }
13 |
--------------------------------------------------------------------------------
/src/Sales/_types/Location.ts:
--------------------------------------------------------------------------------
1 | import EntityOwned, { EntitiyOwnedId } from './EntityOwned'
2 |
3 | export type LocationId = EntitiyOwnedId
4 |
5 | export default interface Location extends EntityOwned {
6 | id?: LocationId
7 | location?: {
8 | lat: number
9 | lng: number
10 | }
11 | name: string
12 | }
13 |
--------------------------------------------------------------------------------
/src/Sales/_types/Order.ts:
--------------------------------------------------------------------------------
1 | import EntityOwned, { EntitiyOwnedId } from './EntityOwned'
2 |
3 | import { PaymentId } from './Payment'
4 | import { ProductId } from './Product'
5 |
6 | export type OrderId = EntitiyOwnedId
7 | export type OrderStatus =
8 | | 'received'
9 | | 'preparing'
10 | | 'shipped'
11 | | 'delivered'
12 | | 'rejected'
13 | | 'refunded'
14 |
15 | export interface OrderProduct {
16 | id?: ProductId
17 | price?: number
18 | quantity?: number
19 | }
20 |
21 | export interface OrderSubmissionData {
22 | products?: any[]
23 | customerNotes?: string
24 | }
25 |
26 | // Order can be owned by a single account or user
27 | export default interface Order extends EntityOwned {
28 | id?: OrderId
29 | name?: string
30 | status: OrderStatus
31 | customerNotes?: string
32 | staffNotes?: string
33 | paymentId?: PaymentId
34 | products?: any[]
35 | sum: number
36 | }
37 |
--------------------------------------------------------------------------------
/src/Sales/_types/Payment.ts:
--------------------------------------------------------------------------------
1 | import { EntityId } from '_types/Entity'
2 |
3 | export type PaymentId = EntityId
4 |
5 | export default interface Payment {
6 | id?: PaymentId
7 | status?: string
8 | transactionId?: string
9 | transactionStatus?: string
10 | }
11 |
--------------------------------------------------------------------------------
/src/Sales/_types/Product.ts:
--------------------------------------------------------------------------------
1 | import Entity, { EntityId } from '_types/Entity'
2 | import { UserId } from '_types/User'
3 | import { OrganizationId } from '_types/Organization'
4 | import ProductImage from './ProductImage'
5 |
6 | export type ProductId = EntityId
7 |
8 | export interface ProductVariation {
9 | name?: string
10 | price?: number
11 | details?: object
12 | }
13 |
14 | export default interface Product extends Entity {
15 | id?: ProductId
16 | name: string
17 | details?: object
18 | description?: string
19 | variations?: ProductVariation[]
20 | price: number
21 | priceDiscounted?: number
22 | images?: ProductImage[]
23 | ownerUserId?: UserId
24 | ownerOrganizationId?: OrganizationId
25 | }
26 |
--------------------------------------------------------------------------------
/src/Sales/_types/ProductAttachment.ts:
--------------------------------------------------------------------------------
1 | import Entity, { EntityId } from '_types/Entity'
2 |
3 | export default interface ProductAttachment extends Entity {
4 | id?: EntityId
5 | name?: string
6 | url?: string
7 | }
8 |
--------------------------------------------------------------------------------
/src/Sales/_types/ProductCategory.ts:
--------------------------------------------------------------------------------
1 | import EntityOwned, { EntitiyOwnedId } from './EntityOwned'
2 | import ProductImage from './ProductImage'
3 |
4 | export type ProductCategoryId = EntitiyOwnedId
5 |
6 | export default interface Category extends EntityOwned {
7 | name: string
8 | description?: string
9 | parentId?: EntitiyOwnedId
10 | images?: ProductImage[]
11 | }
12 |
--------------------------------------------------------------------------------
/src/Sales/_types/ProductImage.ts:
--------------------------------------------------------------------------------
1 | import Entity, { EntityId } from '_types/Entity'
2 |
3 | export default interface ProductImage extends Entity {
4 | id?: EntityId
5 | name?: string
6 | url?: string
7 | }
8 |
--------------------------------------------------------------------------------
/src/Sales/_types/ProductToProductCategory.ts:
--------------------------------------------------------------------------------
1 | import { ProductId } from './Product'
2 | import { ProductCategoryId } from './ProductCategory'
3 |
4 | export default interface ProductToProductCategory {
5 | productId: ProductId
6 | productCategoryId: ProductCategoryId
7 | }
8 |
--------------------------------------------------------------------------------
/src/Sales/_types/Stock.ts:
--------------------------------------------------------------------------------
1 | import { ProductId } from './Product'
2 | import { LocationId } from './Location'
3 |
4 | export default interface Stock {
5 | productId: ProductId
6 | locationId?: LocationId
7 | qunatity: number
8 | }
9 |
--------------------------------------------------------------------------------
/src/Sales/_types/StockAction.ts:
--------------------------------------------------------------------------------
1 | import { ProductId } from './Product'
2 | import { LocationId } from './Location'
3 |
4 | export default interface StockAction {
5 | productId: ProductId
6 | locationId?: LocationId
7 | qunatity: number
8 | action: string
9 | }
10 |
--------------------------------------------------------------------------------
/src/Sales/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Sales'
2 |
--------------------------------------------------------------------------------
/src/_api/_data/notificationsData.ts:
--------------------------------------------------------------------------------
1 | const notificationsData = {}
2 |
3 | export default notificationsData
4 |
--------------------------------------------------------------------------------
/src/_api/_data/organizationsData.ts:
--------------------------------------------------------------------------------
1 | import _keyBy from 'lodash/keyBy'
2 | import Organization from '_types/Organization'
3 | // import organizationsToUsersData from './organizationsToUsersData'
4 |
5 | const list: Organization[] = [
6 | {
7 | id: 1,
8 | name: 'ModularCode',
9 | plan: {
10 | id: 'silver',
11 | name: 'Silver',
12 | },
13 | },
14 | {
15 | id: 2,
16 | name: 'Cool LLC',
17 | plan: {
18 | id: 'gold',
19 | name: 'Gold',
20 | },
21 | // organizationToUsers: organizationsToUsersData.byOrganizationId[2],
22 | },
23 | {
24 | id: 3,
25 | name: 'Other LLC',
26 | plan: {
27 | id: 'trial',
28 | name: 'Trial',
29 | },
30 | // organizationToUsers: organizationsToUsersData.byOrganizationId[3],
31 | },
32 | ]
33 |
34 | const byId: { [key: number]: Organization } = _keyBy(list, 'id')
35 |
36 | const organizationsData = {
37 | list,
38 | byId,
39 | }
40 |
41 | export default organizationsData
42 |
--------------------------------------------------------------------------------
/src/_api/_data/organizationsToUsersData.ts:
--------------------------------------------------------------------------------
1 | import _groupBy from 'lodash/groupBy'
2 | import OrganizationToUser from '_types/OrganizationToUser'
3 |
4 | // import organizationsData from './organizationsData'
5 | // import usersData from './usersData'
6 |
7 | const list: OrganizationToUser[] = [
8 | {
9 | id: 1,
10 | organizationId: 1,
11 | userId: 1,
12 | role: 'owner',
13 | // organization: organizationsData.byId[1],
14 | // user: usersData.byId[1],
15 | },
16 | {
17 | id: 2,
18 | organizationId: 1,
19 | userId: 2,
20 | role: 'admin',
21 | // organization: organizationsData.byId[1],
22 | // user: usersData.byId[2],
23 | },
24 | {
25 | id: 3,
26 | organizationId: 1,
27 | userId: 2,
28 | role: 'member',
29 | // organization: organizationsData.byId[1],
30 | // user: usersData.byId[2],
31 | },
32 | {
33 | id: 4,
34 | organizationId: 2,
35 | userId: 2,
36 | role: 'owner',
37 | // organization: organizationsData.byId[2],
38 | // user: usersData.byId[2],
39 | },
40 | {
41 | id: 5,
42 | organizationId: 3,
43 | userId: 3,
44 | role: 'owner',
45 | // organization: organizationsData.byId[3],
46 | // user: usersData.byId[3],
47 | },
48 | {
49 | id: 6,
50 | organizationId: 3,
51 | userId: 2,
52 | role: 'member',
53 | // organization: organizationsData.byId[3],
54 | // user: usersData.byId[2],
55 | },
56 | ]
57 |
58 | const byUserId = _groupBy(list, 'userId')
59 | const byOrganizationId = _groupBy(list, 'organizationId')
60 |
61 | export default {
62 | list,
63 | byUserId,
64 | byOrganizationId,
65 | }
66 |
--------------------------------------------------------------------------------
/src/_api/_data/usersData.ts:
--------------------------------------------------------------------------------
1 | import _keyBy from 'lodash/keyBy'
2 | import User from '_types/User'
3 | import organizationsToUsersData from './organizationsToUsersData'
4 |
5 | const list: User[] = [
6 | {
7 | id: 1,
8 | firstName: 'Gevorg',
9 | lastName: 'H',
10 | username: 'johndoe1',
11 | email: 'john@doe.com',
12 | avatarUrl: 'https://avatars3.githubusercontent.com/u/3959008?v=3&s=40',
13 | userToOrganizations: organizationsToUsersData.byUserId[1],
14 | globalRole: 'admin',
15 | },
16 | {
17 | id: 2,
18 | firstName: 'Jay',
19 | lastName: 'Nickolson',
20 | username: null,
21 | email: 'example@gmail.com',
22 | avatarUrl:
23 | 'https://tinyfac.es/data/avatars/475605E3-69C5-4D2B-8727-61B7BB8C4699-500w.jpeg',
24 | userToOrganizations: organizationsToUsersData.byUserId[2],
25 | },
26 | {
27 | id: 3,
28 | firstName: 'Ana',
29 | lastName: 'De Armas',
30 | username: null,
31 | email: 'Ana+De+Armas@example.com',
32 | avatarUrl:
33 | 'https://images-na.ssl-images-amazon.com/images/M/MV5BMjA3NjYzMzE1MV5BMl5BanBnXkFtZTgwNTA4NDY4OTE@._V1_UX172_CR0,0,172,256_AL_.jpg',
34 | userToOrganizations: organizationsToUsersData.byUserId[3],
35 | },
36 | {
37 | id: 4,
38 | firstName: 'Armas',
39 | lastName: 'De Ana',
40 | username: null,
41 | email: 'Ana+De+Armas@example.com',
42 | avatarUrl:
43 | 'https://images-na.ssl-images-amazon.com/images/M/MV5BMjA3NjYzMzE1MV5BMl5BanBnXkFtZTgwNTA4NDY4OTE@._V1_UX172_CR0,0,172,256_AL_.jpg',
44 | userToOrganizations: organizationsToUsersData.byUserId[4],
45 | },
46 | {
47 | id: 5,
48 | firstName: 'Sonequa',
49 | lastName: 'Martin-Green',
50 | email: 'Sonequa+Martin+Green@example.com',
51 | avatarUrl:
52 | 'https://images-na.ssl-images-amazon.com/images/M/MV5BMTgxMTc1MTYzM15BMl5BanBnXkFtZTgwNzI5NjMwOTE@._V1_UY256_CR16,0,172,256_AL_.jpg',
53 | userToOrganizations: organizationsToUsersData.byUserId[5],
54 | },
55 | ]
56 |
57 | const byId: { [key: number]: User } = _keyBy(list, 'id')
58 |
59 | const usersData = {
60 | list,
61 | byId,
62 | current: byId[1],
63 | }
64 |
65 | export default usersData
66 |
--------------------------------------------------------------------------------
/src/_api/_mocks/index.ts:
--------------------------------------------------------------------------------
1 | import { AxiosInstance } from 'axios'
2 | import MockAdapter from 'axios-mock-adapter'
3 |
4 | import usersMocks from './usersMocks'
5 | import organizationsMocks from './organizationsMocks'
6 |
7 | const init = (instance: AxiosInstance) => {
8 | const mockAdapter = new MockAdapter(instance, { delayResponse: 200 })
9 |
10 | usersMocks.init(mockAdapter)
11 | organizationsMocks.init(mockAdapter)
12 |
13 | return mockAdapter
14 | }
15 |
16 | export default {
17 | init,
18 | }
19 |
--------------------------------------------------------------------------------
/src/_api/_mocks/organizationsMocks.ts:
--------------------------------------------------------------------------------
1 | import MockAdapter from 'axios-mock-adapter'
2 | import organizationsData from '../_data/organizationsData'
3 |
4 | export default {
5 | init(mock: MockAdapter) {
6 | mock.onGet('/organizations').reply(200, {
7 | organizations: {
8 | ...organizationsData.list,
9 | },
10 | count: organizationsData.list.length,
11 | })
12 |
13 | //
14 | mock.onGet(/\/organizations\/\d+/).reply((config: any) => {
15 | // console.log(config)
16 | const urlPaths = config.url.split('/')
17 | const organizationId = urlPaths[urlPaths.length - 1]
18 | const organization = organizationsData.byId[organizationId]
19 |
20 | if (organization) {
21 | return [200, { ...organization }]
22 | } else {
23 | return [404, { message: 'Organization not found ' }]
24 | }
25 | })
26 |
27 | mock.onPut(/\/organizations\/\d+/).reply((config: any) => {
28 | const urlPaths = config.url.split('/')
29 | const organizationId = urlPaths[urlPaths.length - 1]
30 | const organizationData = JSON.parse(config.data)
31 | const organization = organizationsData.byId[organizationId]
32 |
33 | if (organization) {
34 | return [200, { ...organization, ...organizationData }]
35 | } else {
36 | return [403, { message: 'Update not permitted' }]
37 | }
38 | })
39 |
40 | mock.onPost(/\/organizations/).reply((config: any) => {
41 | const organizationData = JSON.parse(config.data)
42 |
43 | return [200, { id: 100, ...organizationData }]
44 | })
45 |
46 | mock.onDelete(/\/organizations\/\d+/).reply((config: any) => {
47 | return [200, { message: 'Organization deleted' }]
48 | })
49 | },
50 | }
51 |
--------------------------------------------------------------------------------
/src/_api/_mocks/usersMocks.ts:
--------------------------------------------------------------------------------
1 | import MockAdapter from 'axios-mock-adapter'
2 | import usersData from '../_data/usersData'
3 |
4 | export default {
5 | init(mock: MockAdapter) {
6 | mock.onGet('/users/profile').reply(200, {
7 | ...usersData.current,
8 | })
9 |
10 | mock.onGet('/users').reply(200, {
11 | users: {
12 | ...usersData.list,
13 | },
14 | count: usersData.list.length,
15 | })
16 |
17 | //
18 | mock.onGet(/\/users\/\d+/).reply((config: any) => {
19 | // console.log(config)
20 | const urlPaths = config.url.split('/')
21 | const userId = urlPaths[urlPaths.length - 1]
22 | const user = usersData.byId[userId]
23 |
24 | if (user) {
25 | return [200, { ...user }]
26 | } else {
27 | return [404, { message: 'User not found ' }]
28 | }
29 | })
30 |
31 | mock.onPut(/\/users\/\d+/).reply((config: any) => {
32 | const urlPaths = config.url.split('/')
33 | const userId = urlPaths[urlPaths.length - 1]
34 | const userData = JSON.parse(config.data)
35 | const user = usersData.byId[userId]
36 |
37 | if (user) {
38 | return [200, { ...user, ...userData }]
39 | } else {
40 | return [403, { message: 'Update not permitted' }]
41 | }
42 | })
43 |
44 | mock.onPost(/\/users/).reply((config: any) => {
45 | const userData = JSON.parse(config.data)
46 |
47 | console.log('config', config)
48 | console.log('userData', userData)
49 |
50 | return [200, { id: 100, ...userData }]
51 | })
52 |
53 | mock.onDelete(/\/users\/\d+/).reply((config: any) => {
54 | return [200, { message: 'User removed' }]
55 | })
56 | },
57 | }
58 |
--------------------------------------------------------------------------------
/src/_api/client.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import config from '../_config'
3 | import authService from '../_services/authService'
4 |
5 | const apiService = axios.create({
6 | baseURL: config.api.baseUrl,
7 | })
8 |
9 | // Use the Token header for all requests
10 | apiService.interceptors.request.use(
11 | config => {
12 | const token = authService.getToken()
13 | config.headers.Authorization = `Bearer ${token}`
14 |
15 | return config
16 | },
17 | error => {
18 | return Promise.reject(error)
19 | },
20 | )
21 |
22 | // Unauth the token if we get 401 unauth response from the server
23 | apiService.interceptors.response.use(
24 | response => {
25 | return response
26 | },
27 | error => {
28 | if (error.response.status === 401) {
29 | authService.unauth()
30 | }
31 |
32 | return Promise.reject(error)
33 | },
34 | )
35 |
36 | export default apiService
37 |
--------------------------------------------------------------------------------
/src/_api/index.ts:
--------------------------------------------------------------------------------
1 | import instance from './client'
2 | import mocks from './_mocks'
3 | import organizations from './organizations'
4 | import users from './users'
5 |
6 | // Submodules
7 | import apiSales from '../Sales/_api'
8 |
9 | export interface ApiInitOptions {
10 | useSampleData?: boolean
11 | }
12 |
13 | const init = (options: ApiInitOptions = {}) => {
14 | const mockAdapter = options.useSampleData ? mocks.init(instance) : undefined
15 |
16 | apiSales.init({
17 | ...options,
18 | instance,
19 | mockAdapter,
20 | })
21 |
22 | return instance
23 | }
24 |
25 | export default { instance, organizations, users, init }
26 | export { init, instance, organizations, users }
27 |
--------------------------------------------------------------------------------
/src/_api/notifications.ts:
--------------------------------------------------------------------------------
1 | const notificationsService = {}
2 |
3 | export default notificationsService
4 |
--------------------------------------------------------------------------------
/src/_api/organizations.ts:
--------------------------------------------------------------------------------
1 | import { AxiosResponse } from 'axios'
2 | import Organization, {
3 | OrganizationSubmissionData,
4 | OrganizationId,
5 | } from '_types/Organization'
6 | import apiClient from './client'
7 |
8 | export interface OrganizationsService {
9 | getOne(organizationId: OrganizationId): Promise
10 | getList(params: any): Promise
11 | create(organization: OrganizationSubmissionData): Promise
12 | update(
13 | organizationId: OrganizationId,
14 | organization: OrganizationSubmissionData,
15 | ): Promise
16 | remove(organizationId: OrganizationId): Promise
17 | }
18 |
19 | export interface OrganizationsListResponse {
20 | organizations: Organization[]
21 | count: number
22 | }
23 |
24 | const OrganizationsService: OrganizationsService = {
25 | getOne(organizationId) {
26 | return apiClient
27 | .get(`/organizations/${organizationId}`)
28 | .then((res: AxiosResponse) => res.data)
29 | },
30 | getList(params: any) {
31 | return apiClient
32 | .get(`/organizations`, {
33 | params,
34 | })
35 | .then((res: AxiosResponse) => res.data)
36 | },
37 | create(organization: OrganizationSubmissionData) {
38 | return apiClient
39 | .post(`/organizations`, organization)
40 | .then((res: AxiosResponse) => res.data)
41 | },
42 | update(organizationId, organization) {
43 | return apiClient
44 | .put(`/organizations/${organizationId}`, organization)
45 | .then((res: AxiosResponse) => res.data)
46 | },
47 | remove(organizationId) {
48 | return apiClient
49 | .delete(`/organizations/${organizationId}`)
50 | .then((res: AxiosResponse) => res.data)
51 | },
52 | }
53 |
54 | export default OrganizationsService
55 |
--------------------------------------------------------------------------------
/src/_api/users.ts:
--------------------------------------------------------------------------------
1 | import { AxiosResponse } from 'axios'
2 | import User, { UserSubmissionData, UserId } from '_types/User'
3 | import apiClient from './client'
4 |
5 | export interface UsersService {
6 | getProfile(): Promise
7 | getOne(userId: UserId): Promise
8 | getList(params?: any): Promise
9 | create(user: UserSubmissionData): Promise
10 | update(userId: UserId, user: UserSubmissionData): Promise
11 | remove(userId: UserId): Promise
12 | }
13 |
14 | export interface UsersListResponse {
15 | users: User[]
16 | count: number
17 | }
18 |
19 | const usersService: UsersService = {
20 | getProfile() {
21 | return apiClient.get('/users/profile').then((res: AxiosResponse) => res.data)
22 | },
23 | getOne(userId) {
24 | return apiClient.get(`/users/${userId}`).then((res: AxiosResponse) => res.data)
25 | },
26 | getList(params: any) {
27 | return apiClient
28 | .get(`/users`, {
29 | params,
30 | })
31 | .then((res: AxiosResponse) => res.data)
32 | },
33 | create(user: UserSubmissionData) {
34 | return apiClient.post(`/users`, user).then((res: AxiosResponse) => res.data)
35 | },
36 | update(userId, user) {
37 | return apiClient
38 | .put(`/users/${userId}`, user)
39 | .then((res: AxiosResponse) => res.data)
40 | },
41 | remove(userId) {
42 | return apiClient
43 | .delete(`/users/${userId}`)
44 | .then((res: AxiosResponse) => res.data)
45 | },
46 | }
47 |
48 | export default usersService
49 |
--------------------------------------------------------------------------------
/src/_common/Button/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Button = () => {
4 | return
5 | }
6 |
7 | export default Button
8 |
--------------------------------------------------------------------------------
/src/_common/Dropdown/Dropdown.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Dropdown = () => {
4 | return
5 | }
6 |
7 | export default Dropdown
8 |
--------------------------------------------------------------------------------
/src/_common/Logo/Logo.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 | import { makeStyles } from '@material-ui/core/styles'
4 |
5 | interface LogoProps {
6 | className?: string
7 | size?: number
8 | }
9 |
10 | const Logo = (props: LogoProps) => {
11 | const classes = useStyles(props)
12 |
13 | return (
14 |
20 | Modular Material Admin + React
21 |
22 |
27 |
32 |
33 |
34 | )
35 | }
36 |
37 | const useStyles = makeStyles(theme => ({
38 | Logo: (props: LogoProps) => ({
39 | display: 'inline-block',
40 | verticalAlign: 'text-bottom',
41 | width: props.size,
42 | height: props.size,
43 | }),
44 | path: {
45 | transition: 'all .3s ease',
46 | },
47 | outline: {
48 | fill: 'currentColor',
49 | },
50 | letter: {
51 | fill: 'currentColor',
52 | },
53 | }))
54 |
55 | export default Logo
56 |
--------------------------------------------------------------------------------
/src/_common/PageBreadcrumbs/PageBreadcrumbs.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Breadcrumbs from '@material-ui/core/Breadcrumbs'
3 | import Link from '@material-ui/core/Link'
4 | import Typography from '@material-ui/core/Typography'
5 |
6 | const PageBreadcrumbs = () => {
7 | return (
8 |
9 |
10 | Material-UI
11 |
12 |
13 | Core
14 |
15 | Breadcrumb
16 |
17 | )
18 | }
19 |
20 | export default PageBreadcrumbs
21 |
--------------------------------------------------------------------------------
/src/_common/PageBreadcrumbs/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './PageBreadcrumbs'
2 |
--------------------------------------------------------------------------------
/src/_common/PageContainer/PageContainer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { makeStyles } from '@material-ui/core/styles'
4 | import Container from '@material-ui/core/Container'
5 |
6 | import { Theme } from '_theme'
7 |
8 | interface PageContainerProps {
9 | children?: any
10 | }
11 |
12 | const PageContainer = ({ children }: PageContainerProps) => {
13 | const classes = useStyles()
14 |
15 | return (
16 |
17 | {children}
18 |
19 | )
20 | }
21 |
22 | const useStyles = makeStyles((theme: Theme) => ({
23 | container: {
24 | flex: 1,
25 | paddingTop: theme.spacing(4),
26 | paddingBottom: theme.spacing(4),
27 | },
28 | }))
29 |
30 | export default PageContainer
31 |
--------------------------------------------------------------------------------
/src/_common/PageContainer/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './PageContainer'
2 |
--------------------------------------------------------------------------------
/src/_common/PageTitle/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/modularcode/modular-admin-react-pro/80104bdc06a5114a392f9cdd6ef5250f95ef50a3/src/_common/PageTitle/.gitkeep
--------------------------------------------------------------------------------
/src/_common/PageToolbar/PageToolbar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 |
4 | import { makeStyles, createStyles } from '@material-ui/core/styles'
5 | import Grid from '@material-ui/core/Grid'
6 | import Typography from '@material-ui/core/Typography'
7 |
8 | import { Theme } from '_theme'
9 |
10 | export interface PageToolbarProps {
11 | title?: React.ReactNode
12 | titleComponent?: React.ComponentType
13 | actions?: React.ReactElement
14 | actionsComponent?: React.ComponentType
15 | classes?: {
16 | container?: string
17 | titleContainer?: string
18 | actionsContainer?: string
19 | }
20 | }
21 |
22 | const PageToolbar: React.FC = (props: PageToolbarProps) => {
23 | const classes = useStyles()
24 | const userClasses = props.classes || {}
25 |
26 | const Title = props.title ? (
27 |
28 | {props.title}
29 |
30 | ) : null
31 | const TitleComponent = props.titleComponent
32 |
33 | const Actions = props.actions
34 | const ActionsComponent = props.actionsComponent
35 |
36 | return (
37 |
42 |
49 | {Title && Title}
50 | {TitleComponent && }
51 |
52 |
59 | {Actions && Actions}
60 | {ActionsComponent && }
61 |
62 |
63 | )
64 | }
65 |
66 | const useStyles = makeStyles((theme: Theme) =>
67 | createStyles({
68 | container: {
69 | marginBottom: '1.5rem',
70 | },
71 | titleContainer: {},
72 | actionsContainer: {
73 | display: 'flex',
74 | justifyContent: 'flex-end',
75 | },
76 | }),
77 | )
78 |
79 | export default PageToolbar
80 |
--------------------------------------------------------------------------------
/src/_common/PageToolbar/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './PageToolbar'
2 |
--------------------------------------------------------------------------------
/src/_config/index.ts:
--------------------------------------------------------------------------------
1 | interface Config {
2 | navigationType: 'hash' | 'history'
3 | useSampleData?: boolean
4 | api: {
5 | baseUrl: string
6 | }
7 | }
8 |
9 | const config: Config = {
10 | navigationType: 'hash',
11 | useSampleData: true,
12 | api: {
13 | baseUrl: 'http://localhost:4000/api',
14 | },
15 | }
16 |
17 | export default config
18 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/DashboardLayout.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 |
4 | import { makeStyles, useTheme } from '@material-ui/core/styles'
5 | import useMediaQuery from '@material-ui/core/useMediaQuery'
6 | import Drawer from '@material-ui/core/Drawer'
7 | import Hidden from '@material-ui/core/Hidden'
8 |
9 | import { Theme } from '_theme'
10 | import Header from './Header/Header'
11 | import Sidebar from './Sidebar/Sidebar'
12 | import Footer from './Footer'
13 |
14 | export interface DashboardProps {
15 | children?: any
16 | }
17 |
18 | export default function Dashboard({ children }: DashboardProps) {
19 | const classes = useStyles()
20 | const theme = useTheme()
21 | const isDesktop = useMediaQuery(theme.breakpoints.up('md'))
22 | const isMobile = !isDesktop
23 |
24 | const [isSidebarOpenMobile, setIsSidebarOpenMobile] = React.useState(false)
25 | const [isSidebarCollapsedDesktop, setIsSidebarCollapsedDesktop] = React.useState(false)
26 |
27 | function handleSidebarMobileToggle() {
28 | setIsSidebarOpenMobile(!isSidebarOpenMobile)
29 | }
30 |
31 | function handleSidebarToggle() {
32 | // Open/close on mobile
33 | if (isMobile) {
34 | setIsSidebarOpenMobile(!isSidebarOpenMobile)
35 | }
36 | // Collapse/uncollapse on desktop
37 | else {
38 | setIsSidebarCollapsedDesktop(!isSidebarCollapsedDesktop)
39 | }
40 | }
41 |
42 | return (
43 |
44 |
53 |
54 |
55 |
65 |
66 |
78 |
84 |
85 |
86 |
87 |
97 |
103 |
104 |
105 |
106 |
107 |
108 | {children}
109 |
110 |
111 |
112 | )
113 | }
114 |
115 | const useStyles = makeStyles((theme: Theme) => ({
116 | dashboardContainer: {
117 | display: 'flex',
118 | background: '#f5f5f5',
119 | },
120 | headerContainer: {
121 | top: 0,
122 | left: 0,
123 | right: 0,
124 | position: 'absolute',
125 | zIndex: theme.zIndex.drawer + 1,
126 | transition: theme.transitions.create(['width', 'margin'], {
127 | easing: theme.transitions.easing.sharp,
128 | duration: theme.transitions.duration.leavingScreen,
129 | }),
130 | },
131 | headerContainerDesktop: {
132 | left: 'auto',
133 | width: `calc(100% - ${theme.sidebar.width}px)`,
134 | transition: theme.transitions.create(['width', 'margin'], {
135 | easing: theme.transitions.easing.sharp,
136 | duration: theme.transitions.duration.enteringScreen,
137 | }),
138 | },
139 | headerContainerDesktopDrawerCollapsed: {
140 | width: `calc(100% - ${theme.sidebar.widthCollapsed}px)`,
141 | },
142 | sidebarContainer: {
143 | position: 'relative',
144 | [theme.breakpoints.up('md')]: {
145 | width: theme.sidebar.width,
146 | flexShrink: 0,
147 | },
148 | transition: theme.transitions.create('width', {
149 | easing: theme.transitions.easing.sharp,
150 | duration: theme.transitions.duration.leavingScreen,
151 | }),
152 | },
153 | sidebarContainerMobile: {
154 | width: 0,
155 | },
156 | sidebarContainerDesktop: {
157 | width: theme.sidebar.width,
158 | },
159 | sidebarContainerDesktopDrawerCollapsed: {
160 | [theme.breakpoints.up('md')]: {
161 | width: theme.sidebar.widthCollapsed,
162 | },
163 | },
164 | drawer: {},
165 | drawerMobile: {
166 | width: theme.sidebar.width,
167 | },
168 | drawerDesktop: {
169 | width: '100%',
170 | position: 'absolute',
171 | },
172 | drawerDesktopCollapsed: {
173 | overflowX: 'hidden',
174 | },
175 | headerSpacer: theme.mixins.toolbar,
176 | content: {
177 | flexGrow: 1,
178 | height: '100vh',
179 | overflow: 'auto',
180 | flexDirection: 'column',
181 | display: 'flex',
182 | },
183 | paper: {
184 | padding: theme.spacing(2),
185 | display: 'flex',
186 | overflow: 'auto',
187 | flexDirection: 'column',
188 | },
189 | fixedHeight: {
190 | height: 240,
191 | },
192 | }))
193 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Footer/Footer.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Typography from '@material-ui/core/Typography'
3 | import Link from '@material-ui/core/Link'
4 | import { makeStyles } from '@material-ui/core/styles'
5 |
6 | import { Theme } from '_theme'
7 |
8 | const Footer = () => {
9 | const classes = useStyles()
10 |
11 | return (
12 |
13 |
14 | {'Find me on: '}
15 |
16 | GitHub
17 |
18 | {' | '}
19 |
20 | Twitter
21 |
22 | {' | '}
23 |
24 | LinkedIn
25 |
26 |
27 |
28 | {'Built with '}
29 |
30 | Material-UI
31 |
32 | {' by '}
33 |
34 | Gevorg Harutyunyan
35 |
36 |
37 |
38 | )
39 | }
40 |
41 | const useStyles = makeStyles((theme: Theme) => ({
42 | footer: {
43 | display: 'flex',
44 | background: '#fff',
45 | padding: '0.5rem 1rem',
46 | justifyContent: 'space-between',
47 | },
48 | }))
49 |
50 | export default Footer
51 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Footer/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Footer'
2 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Header/Header.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 |
4 | import { makeStyles } from '@material-ui/core/styles'
5 | import AppBar from '@material-ui/core/AppBar'
6 | import Toolbar from '@material-ui/core/Toolbar'
7 | import IconButton from '@material-ui/core/IconButton'
8 |
9 | import IconMenu from '@material-ui/icons/Menu'
10 |
11 | import HeaderDemo from './HeaderDemo'
12 |
13 | import HeaderSearch from './HeaderSearch'
14 | import HeaderNotifications from './HeaderNotifications'
15 | import HeaderProfile from './HeaderProfile'
16 |
17 | interface HeaderProps {
18 | onToggle?: (event: React.MouseEvent) => void
19 | }
20 |
21 | const Header = ({ onToggle }: HeaderProps) => {
22 | const classes = useStyles()
23 |
24 | return (
25 |
26 |
27 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | )
45 | }
46 |
47 | const useStyles = makeStyles(theme => ({
48 | header: {
49 | background: '#fff',
50 | color: '#7b7b7b',
51 | boxShadow: 'none',
52 | },
53 | toolbar: {},
54 | menuButton: {},
55 | actions: {
56 | marginLeft: 'auto',
57 | alignItems: 'center',
58 | display: 'flex',
59 | },
60 | notificationsButton: {
61 | marginRight: 23,
62 | },
63 | title: {
64 | flexGrow: 1,
65 | },
66 | }))
67 |
68 | export default Header
69 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Header/HeaderDemo.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Link } from 'react-router-dom'
3 |
4 | import { makeStyles } from '@material-ui/core/styles'
5 | import Tooltip from '@material-ui/core/Tooltip'
6 | import Button from '@material-ui/core/Button'
7 | import IconCode from '@material-ui/icons/Code'
8 | import IconFavorite from '@material-ui/icons/Favorite'
9 | import IconStar from '@material-ui/icons/Star'
10 |
11 | // const SupportLink = React.forwardRef((props, ref) => (
12 | //
13 | // ))
14 |
15 | const HeaderDemo: React.FC = props => {
16 | const classes = useStyles(props)
17 |
18 | return (
19 |
20 |
21 |
28 |
29 | View on GitHub
30 |
31 |
32 |
33 |
41 |
42 | Support Me
43 |
44 |
45 |
46 |
53 |
54 | Rate
55 |
56 |
57 |
58 | )
59 | }
60 |
61 | const useStyles = makeStyles(theme => ({
62 | demo: {
63 | flex: 1,
64 | display: 'flex',
65 | justifyContent: 'center',
66 | alignItems: 'center',
67 | },
68 | demoIcon: {},
69 | demoName: {
70 | marginLeft: theme.spacing(1),
71 | [theme.breakpoints.down('md')]: {
72 | display: 'none',
73 | },
74 | },
75 | button: {
76 | margin: theme.spacing(1),
77 | [theme.breakpoints.down('md')]: {
78 | margin: 3,
79 | },
80 | },
81 | }))
82 |
83 | export default HeaderDemo
84 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Header/HeaderNotifications.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { makeStyles } from '@material-ui/core/styles'
4 | import IconButton from '@material-ui/core/IconButton'
5 | import Badge from '@material-ui/core/Badge'
6 | import IconNotifications from '@material-ui/icons/Notifications'
7 | import Menu from '@material-ui/core/Menu'
8 | import List from '@material-ui/core/List'
9 | import ListItem from '@material-ui/core/ListItem'
10 | import ListItemText from '@material-ui/core/ListItemText'
11 | import ListItemAvatar from '@material-ui/core/ListItemAvatar'
12 | import Avatar from '@material-ui/core/Avatar'
13 | import Typography from '@material-ui/core/Typography'
14 |
15 | const notifications = [
16 | {
17 | user: {
18 | name: 'Remy Sharp',
19 | image: 'https://material-ui.com/static/images/avatar/1.jpg',
20 | },
21 | title: 'New Order',
22 | content: " — I'll be in your neighborhood doing errands this…",
23 | },
24 | {
25 | user: {
26 | name: 'Travis Howard',
27 | image: 'https://material-ui.com//static/images/avatar/2.jpg',
28 | },
29 | title: 'New Signup',
30 | content: " — Wish I could come, but I'm out of town this…",
31 | },
32 | {
33 | user: {
34 | name: 'Oui Oui',
35 | image: 'https://material-ui.com//static/images/avatar/3.jpg',
36 | },
37 | title: 'Refund Request',
38 | content: 'please provide me a refund for my order',
39 | },
40 | ]
41 |
42 | const HeaderNotifications = () => {
43 | const classes = useStyles()
44 | const [anchorEl, setAnchorEl] = React.useState(null)
45 |
46 | function handleClick(event: React.MouseEvent) {
47 | setAnchorEl(event.currentTarget)
48 | }
49 |
50 | function handleClose() {
51 | setAnchorEl(null)
52 | }
53 |
54 | return (
55 |
56 |
65 |
66 |
67 |
68 |
69 |
113 |
114 | )
115 | }
116 |
117 | // const HeaderNotificationsContent = () => {
118 | // const classes = useStyles()
119 |
120 | // return
121 | // }
122 |
123 | const useStyles = makeStyles(theme => ({
124 | headerNotifications: {
125 | marginRight: 23,
126 | // position: 'relative',
127 | // position: 'absolute'
128 | },
129 | notificationsContainer: {
130 | // position: 'relative',
131 | },
132 | button: {},
133 | badge: {
134 | color: '#fff',
135 | },
136 | notifications: {
137 | // width: 360,
138 | maxWidth: 360,
139 | backgroundColor: theme.palette.background.paper,
140 | },
141 | inline: {
142 | display: 'inline',
143 | },
144 | }))
145 |
146 | export default HeaderNotifications
147 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Header/HeaderProfile.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsx from 'clsx'
3 |
4 | import { Link } from 'react-router-dom'
5 |
6 | import { makeStyles } from '@material-ui/core/styles'
7 | import IconButton from '@material-ui/core/IconButton'
8 | import Avatar from '@material-ui/core/Avatar'
9 | import Menu from '@material-ui/core/Menu'
10 | import MenuItem from '@material-ui/core/MenuItem'
11 | import ListItemText from '@material-ui/core/ListItemText'
12 | import ListItemIcon from '@material-ui/core/ListItemIcon'
13 | import Divider from '@material-ui/core/Divider'
14 |
15 | import IconArrowDropDown from '@material-ui/icons/ArrowDropDown'
16 | import IconProfile from '@material-ui/icons/AccountBox'
17 | import IconAccount from '@material-ui/icons/AccountBalance'
18 | import IconSettings from '@material-ui/icons/Settings'
19 | import IconLogout from '@material-ui/icons/ExitToApp'
20 |
21 | import { useAppStateData } from '../../../_state/appState'
22 |
23 | const HeaderProfile = () => {
24 | const classes = useStyles()
25 | const [anchorEl, setAnchorEl] = React.useState(null)
26 | const { user } = useAppStateData()
27 |
28 | if (!user) {
29 | return
30 | }
31 |
32 | function handleClick(event: React.MouseEvent) {
33 | setAnchorEl(event.currentTarget)
34 | }
35 |
36 | function handleClose() {
37 | setAnchorEl(null)
38 | }
39 |
40 | return (
41 |
42 |
51 |
56 | {user.firstName}
57 |
58 |
59 |
101 |
102 | )
103 | }
104 |
105 | const useStyles = makeStyles(theme => ({
106 | headerProfile: {
107 | display: 'inline-flex',
108 | },
109 | profileButton: {
110 | borderRadius: 30,
111 | fontSize: '1.2rem',
112 | padding: 8,
113 | },
114 | profileAvatar: {
115 | width: 35,
116 | height: 35,
117 | marginRight: 10,
118 | },
119 | profileName: {
120 | fontWeight: 500,
121 | marginRight: 5,
122 | [theme.breakpoints.down('sm')]: {
123 | display: 'none',
124 | },
125 | },
126 | profileMenu: {
127 | marginLeft: '-16px',
128 | },
129 | }))
130 |
131 | export default HeaderProfile
132 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Header/HeaderSearch.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { makeStyles } from '@material-ui/core/styles'
3 | import Button from '@material-ui/core/Button'
4 | import TextField from '@material-ui/core/TextField'
5 | import Dialog from '@material-ui/core/Dialog'
6 | import DialogActions from '@material-ui/core/DialogActions'
7 | import DialogContent from '@material-ui/core/DialogContent'
8 | import DialogContentText from '@material-ui/core/DialogContentText'
9 | import DialogTitle from '@material-ui/core/DialogTitle'
10 |
11 | import IconSearch from '@material-ui/icons/Search'
12 | import IconButton from '@material-ui/core/IconButton'
13 |
14 | const HeaderSearch = () => {
15 | const classes = useStyles()
16 | const [open, setOpen] = React.useState(false)
17 |
18 | function handleClickOpen() {
19 | setOpen(true)
20 | }
21 |
22 | function handleClose() {
23 | setOpen(false)
24 | }
25 |
26 | return (
27 |
28 |
35 |
36 |
37 |
46 | Search...
47 |
48 |
49 | You may provide some extra search hints here
50 |
51 |
59 |
60 |
61 |
62 | Cancel
63 |
64 |
65 | Search
66 |
67 |
68 |
69 |
70 | )
71 | }
72 |
73 | const useStyles = makeStyles(theme => ({
74 | searchButton: {
75 | marginRight: 20,
76 | },
77 | scrollPaper: {
78 | alignItems: 'flex-start',
79 | },
80 | }))
81 |
82 | export default HeaderSearch
83 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Header/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Header'
2 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Sidebar/Sidebar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { makeStyles } from '@material-ui/core/styles'
3 |
4 | import { Link } from 'react-router-dom'
5 | import Typography from '@material-ui/core/Typography'
6 |
7 | import { Theme } from '_theme'
8 | import Logo from '_common/Logo/Logo'
9 | import SidebarNav from './SidebarNav'
10 |
11 | interface SidebarProps {
12 | onToggleClick?: (event: React.MouseEvent) => void
13 | isDesktop: boolean
14 | isMobile: boolean
15 | isSidebarCollapsedDesktop: boolean
16 | isSidebarOpenMobile: boolean
17 | }
18 |
19 | const Sidebar = (props: SidebarProps) => {
20 | const { isDesktop, isSidebarCollapsedDesktop } = props
21 |
22 | const classes = useStyles(props)
23 |
24 | return (
25 |
43 | )
44 | }
45 |
46 | const useStyles = makeStyles((theme: Theme) => ({
47 | sidebar: {
48 | position: 'absolute',
49 | top: 0,
50 | bottom: 0,
51 | width: '100%',
52 | height: '100%',
53 | color: theme.sidebar.color,
54 | background: theme.sidebar.background,
55 | overflowX: 'hidden',
56 | overflowY: 'auto',
57 | },
58 | sidebarHeader: {
59 | display: 'flex',
60 | alignItems: 'center',
61 | justifyContent: 'center',
62 | whiteSpace: 'nowrap',
63 | padding: '0 8px',
64 | ...theme.mixins.toolbar,
65 | },
66 | sidebarTitleLink: {
67 | textDecoration: 'none',
68 | color: 'inherit',
69 | display: 'flex',
70 | // '&:hover': {
71 | // '& $logo': {
72 | // color: '#fff',
73 | // },
74 | // },
75 | },
76 | logo: {
77 | color: theme.palette.primary.main,
78 | },
79 | title: (props: SidebarProps) => ({
80 | // fontSize: '20px',
81 | // fontWeight: 400,
82 | position: 'relative',
83 | overflow: 'visible',
84 | marginLeft: '5px',
85 | display: props.isDesktop && props.isSidebarCollapsedDesktop ? 'none' : 'block',
86 | }),
87 | name: {},
88 | tagline: {
89 | fontSize: 8,
90 | fontWeight: 'bold',
91 | position: 'absolute',
92 | top: '100%',
93 | marginTop: -5,
94 | background: theme.palette.primary.main,
95 | color: '#fff',
96 | borderRadius: 2,
97 | padding: '1px 3px',
98 | right: 0,
99 | },
100 | }))
101 |
102 | export default Sidebar
103 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Sidebar/SidebarNav.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { makeStyles, createStyles } from '@material-ui/core/styles'
3 | import List from '@material-ui/core/List'
4 | import ListSubheader from '@material-ui/core/ListSubheader'
5 |
6 | import IconSales from '@material-ui/icons/MonetizationOn'
7 | import IconProfile from '@material-ui/icons/AccountBox'
8 | import IconAccount from '@material-ui/icons/AccountBalance' //
9 | import IconAdmin from '@material-ui/icons/VpnKey'
10 | import IconMisc from '@material-ui/icons/MoreHoriz'
11 |
12 | import IconDashboard from '@material-ui/icons/Dashboard'
13 | import IconProducts from '@material-ui/icons/LocalMall'
14 | import IconOrders from '@material-ui/icons/ShoppingCart'
15 | import IconPeople from '@material-ui/icons/People'
16 | import IconPersonalVideo from '@material-ui/icons/PersonalVideo'
17 | import IconLibraryBooks from '@material-ui/icons/LibraryBooks'
18 | import IconQuestionAnswer from '@material-ui/icons/QuestionAnswer'
19 | import IconStars from '@material-ui/icons/Stars'
20 | import IconNewReleases from '@material-ui/icons/NewReleases'
21 | import IconSettings from '@material-ui/icons/Settings'
22 | import IconGroup from '@material-ui/icons/Group'
23 | import IconInfo from '@material-ui/icons/Info' //
24 | import IconPreson from '@material-ui/icons/Person' //
25 | // import IconSync from '@material-ui/icons/Sync'
26 | // import IconPhone from '@material-ui/icons/Phone'
27 | import IconStock from '@material-ui/icons/LocalShipping'
28 | import IconLocation from '@material-ui/icons/LocationOn'
29 |
30 | import { Theme } from '_theme'
31 | import SidebarNavItems from './SidebarNavItems'
32 |
33 | export interface SidebarNavProps {
34 | isCollapsed: boolean
35 | }
36 |
37 | const SidebarNav = (props: SidebarNavProps) => {
38 | const { isCollapsed } = props
39 | const classes = useStyles()
40 |
41 | const itemsSales = [
42 | {
43 | name: 'Sales Dashboard',
44 | link: '/sales/dashboard',
45 | Icon: IconDashboard,
46 | },
47 | {
48 | name: 'Orders',
49 | link: '/sales/orders',
50 | Icon: IconOrders,
51 | },
52 | {
53 | name: 'Customers',
54 | link: '/sales/customers',
55 | Icon: IconPeople,
56 | },
57 | {
58 | name: 'Products',
59 | Icon: IconProducts,
60 | items: [
61 | {
62 | name: 'All Products',
63 | link: '/sales/products',
64 | },
65 | {
66 | name: 'Add New',
67 | link: '/sales/products/new',
68 | },
69 | {
70 | name: 'Categories',
71 | link: '/sales/products/categories',
72 | },
73 | ],
74 | },
75 | {
76 | name: 'Stock',
77 | link: '/sales/stock',
78 | Icon: IconStock,
79 | },
80 | {
81 | name: 'Locations',
82 | link: '/sales/locations',
83 | Icon: IconLocation,
84 | },
85 | ]
86 |
87 | // eslint-disable-next-line
88 | const itemsContent = [
89 | {
90 | name: 'All Items',
91 | link: '/content/items',
92 | },
93 | {
94 | name: 'Add New',
95 | link: '/content/items/new',
96 | },
97 |
98 | {
99 | name: 'Categories',
100 | link: '/content/categories',
101 | },
102 | ]
103 |
104 | const itemsProfile = [
105 | {
106 | name: 'My Profile',
107 | link: '/profile',
108 | Icon: IconInfo,
109 | },
110 | {
111 | name: 'Profile Settings',
112 | link: '/profile/settings',
113 | Icon: IconSettings,
114 | },
115 | ]
116 |
117 | const itemsOrganizations = [
118 | {
119 | name: 'My Organizations',
120 | link: '/organizations',
121 | Icon: IconInfo,
122 | },
123 | {
124 | name: 'Organization Settings',
125 | link: '/organizations/settings',
126 | Icon: IconSettings,
127 | },
128 | {
129 | name: 'Team',
130 | link: '/organizations/users',
131 | Icon: IconGroup,
132 | },
133 | ]
134 |
135 | const itemsAuth = [
136 | {
137 | name: 'Login',
138 | link: '/auth/login',
139 | },
140 | {
141 | name: 'Signup',
142 | link: '/auth/signup',
143 | },
144 | {
145 | name: 'Recover',
146 | link: '/auth/recover',
147 | },
148 | {
149 | name: 'Reset',
150 | link: '/auth/reset',
151 | },
152 | ]
153 |
154 | const itemsAdmin = [
155 | {
156 | name: 'Admin Dashboard',
157 | link: '/admin/dashboard',
158 | Icon: IconDashboard,
159 | },
160 | {
161 | name: 'All Organizations',
162 | link: '/admin/accounts',
163 | Icon: IconAccount,
164 | },
165 | {
166 | name: 'All Users',
167 | link: '/admin/users',
168 | Icon: IconGroup,
169 | },
170 | ]
171 |
172 | const itemsMisc = [
173 | {
174 | name: 'Search',
175 | link: '/search',
176 | },
177 | {
178 | name: 'Not Found',
179 | link: '/notfound',
180 | },
181 | ]
182 |
183 | // eslint-disable-next-line
184 | const itemsAppModules = [
185 | {
186 | name: 'Sales Management',
187 | link: '/sales',
188 | Icon: IconSales,
189 | items: itemsSales,
190 | },
191 | // {
192 | // name: 'Customer Support',
193 | // link: '/support',
194 | // Icon: IconPhone,
195 | // },
196 | // {
197 | // name: 'Content Management',
198 | // link: '/content',
199 | // Icon: IconContent,
200 | // items: itemsContent,
201 | // },
202 | // {
203 | // name: 'Services',
204 | // link: '/services',
205 | // Icon: IconSync,
206 | // },
207 | ]
208 |
209 | const itemsCoreModules = [
210 | {
211 | name: 'Auth',
212 | items: itemsAuth,
213 | Icon: IconPreson,
214 | },
215 | {
216 | name: 'Profile',
217 | items: itemsProfile,
218 | Icon: IconProfile,
219 | },
220 | {
221 | name: 'Organizations',
222 | items: itemsOrganizations,
223 | Icon: IconAccount,
224 | },
225 | {
226 | name: 'Administration',
227 | items: itemsAdmin,
228 | Icon: IconAdmin,
229 | },
230 | {
231 | name: 'Misc Pages',
232 | items: itemsMisc,
233 | Icon: IconMisc,
234 | },
235 | ]
236 |
237 | const itemsUI = [
238 | {
239 | name: 'UI Components',
240 | link: '/demo/components',
241 | Icon: IconPersonalVideo,
242 | },
243 | ]
244 |
245 | const itemsTheme = [
246 | {
247 | name: 'Why Modular?',
248 | link: '/demo/features',
249 | Icon: IconNewReleases,
250 | IconClassName: classes.iconFeatures,
251 | },
252 | {
253 | name: 'Docs',
254 | link: '/demo/docs',
255 | Icon: IconLibraryBooks,
256 | IconClassName: classes.iconDocs,
257 | },
258 | {
259 | name: 'Supporters',
260 | link: '/demo/supporters',
261 | Icon: IconStars,
262 | IconClassName: classes.iconSupporters,
263 | },
264 | {
265 | name: 'Discuss',
266 | link: '/demo/discuss',
267 | Icon: IconQuestionAnswer,
268 | IconClassName: classes.iconDiscuss,
269 | },
270 | ]
271 |
272 | return (
273 |
274 |
275 | {!isCollapsed && (
276 |
277 | Applications
278 |
279 | )}
280 |
281 |
282 |
283 |
284 | {!isCollapsed && (
285 |
286 | Basic Functionality
287 |
288 | )}
289 |
290 |
291 |
292 |
293 | {!isCollapsed && (
294 |
295 | UI & Utils
296 |
297 | )}
298 |
299 |
300 |
301 | {!isCollapsed && (
302 |
303 | Misc
304 |
305 | )}
306 |
307 |
308 |
309 | )
310 | }
311 |
312 | const useStyles = makeStyles((theme: Theme) =>
313 | createStyles({
314 | navList: {
315 | width: theme.sidebar.width,
316 | fontSize: '1.1em',
317 | fontWeight: 400,
318 | lineHeight: 1.5,
319 | letterSpacing: '0.00938em',
320 | },
321 | navListHeader: {
322 | textAlign: 'center',
323 | },
324 | iconFeatures: {
325 | color: '#95de3c',
326 | },
327 | iconDocs: {
328 | color: '#f8cda9',
329 | },
330 | iconSupporters: {
331 | color: '#e3b546',
332 | },
333 | iconDiscuss: {
334 | color: '#ccc',
335 | },
336 | }),
337 | )
338 |
339 | export default SidebarNav
340 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Sidebar/SidebarNavItem.tsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef, HTMLAttributes } from 'react'
2 | import clsx from 'clsx'
3 |
4 | import { makeStyles, createStyles } from '@material-ui/core/styles'
5 | import { NavLink, NavLinkProps } from 'react-router-dom'
6 | import List from '@material-ui/core/List'
7 | import ListItem from '@material-ui/core/ListItem'
8 | import ListItemIcon from '@material-ui/core/ListItemIcon'
9 | import ListItemText from '@material-ui/core/ListItemText'
10 | import Tooltip from '@material-ui/core/Tooltip'
11 | import Collapse from '@material-ui/core/Collapse'
12 |
13 | import { SvgIconProps } from '@material-ui/core/SvgIcon'
14 | import IconExpandLess from '@material-ui/icons/ExpandLess'
15 | import IconExpandMore from '@material-ui/icons/ExpandMore'
16 | import IconSpacer from '@material-ui/icons/FiberManualRecord'
17 |
18 | import { Theme } from '_theme'
19 |
20 | export interface SidebarNavItemProps {
21 | name: string
22 | link?: string
23 | Icon?: React.ComponentType
24 | IconStyles?: React.CSSProperties
25 | IconClassName?: string
26 | IconClassNameActive?: string
27 | isCollapsed?: boolean
28 | isOpen?: boolean
29 | isNested?: boolean
30 | nestingLevel?: number
31 | nestingOffset?: number
32 | className?: string
33 | items?: SidebarNavItemProps[]
34 | match?: object
35 | }
36 |
37 | export interface ListItemLinkProps extends NavLinkProps {}
38 |
39 | export interface ListItemComponentProps extends HTMLAttributes {
40 | link?: string | null
41 | children?: any
42 | isCollapsed?: boolean | null
43 | }
44 |
45 | // ----------------------------------------------------------------------
46 |
47 | export const ListItemLink: React.ExoticComponent = forwardRef(
48 | (props: ListItemLinkProps, ref: React.Ref) => (
49 |
50 | ),
51 | )
52 |
53 | // Can be a link, or button
54 | export const ListItemComponent: React.ExoticComponent<
55 | ListItemComponentProps
56 | > = forwardRef((props: ListItemComponentProps, ref: React.Ref) => {
57 | // Omit isCollapsed
58 | const { isCollapsed, ...newProps } = props
59 | const classes = useStyles()
60 |
61 | const component =
62 | typeof props.link === 'string' ? (
63 |
64 | ) : (
65 |
66 | )
67 |
68 | return (
69 |
70 | {component}
71 |
72 | )
73 | })
74 |
75 | const SidebarNavItem: React.FC = (props: SidebarNavItemProps) => {
76 | const {
77 | name,
78 | link,
79 | Icon,
80 | IconStyles = {},
81 | IconClassName = '',
82 | isCollapsed,
83 | // isNested,
84 | nestingLevel = 0,
85 | nestingOffset = 16,
86 | className,
87 | items = [],
88 | } = props
89 | const isTooltipEnabeld = isCollapsed
90 | const classes = useStyles()
91 | const hasChildren = items && items.length > 0
92 |
93 | // Flattened array of all children
94 | function getItemsAll(items: SidebarNavItemProps[]): SidebarNavItemProps[] {
95 | return items.reduce((allItems: SidebarNavItemProps[], item: SidebarNavItemProps) => {
96 | // let res = allItems.concat([item])
97 |
98 | if (item.items && item.items.length) {
99 | return allItems.concat([item], getItemsAll(item.items))
100 | } else {
101 | return allItems.concat([item])
102 | }
103 | }, [])
104 | }
105 |
106 | const itemsAll = getItemsAll(items)
107 | const hasChildrenAndIsActive =
108 | hasChildren &&
109 | itemsAll.filter(item => `#${item.link}` === window.location.hash).length > 0
110 | const isOpen = hasChildrenAndIsActive || false
111 | const [open, setOpen] = React.useState(isOpen)
112 |
113 | function handleClick() {
114 | setOpen(!open)
115 | }
116 |
117 | const ListItemIconInner =
118 | (!!Icon && ) ||
119 | (isCollapsed && ) ||
120 | ''
121 |
122 | const nestingOffsetChildren = !isCollapsed ? nestingOffset + 16 : 16
123 |
124 | const ListItemElement = (
125 |
141 | {!!ListItemIconInner && (
142 |
146 | {ListItemIconInner}
147 |
148 | )}
149 |
150 | {hasChildren && !open && }
151 | {hasChildren && open && }
152 |
153 | )
154 |
155 | const ListItemRoot = isTooltipEnabeld ? (
156 |
163 | {ListItemElement}
164 |
165 | ) : (
166 | ListItemElement
167 | )
168 |
169 | const ListItemChildren = hasChildren ? (
170 |
171 |
172 | {/* */}
173 |
174 | {items.map(item => (
175 |
184 | ))}
185 |
186 |
187 |
188 | ) : null
189 |
190 | return (
191 |
197 | {ListItemRoot}
198 | {ListItemChildren}
199 |
200 | )
201 | }
202 |
203 | const useStyles = makeStyles((theme: Theme) =>
204 | createStyles({
205 | // nested: {
206 | // paddingLeft: theme.spacing(10),
207 | // },
208 | navItemWrapper: {
209 | position: 'relative',
210 | },
211 | navItemWrapperActive: {
212 | // background: 'rgba(0, 0, 0, 0.08)',
213 | },
214 | navItemWrapperActiveCollapsed: {
215 | background: 'rgba(0, 0, 0, 0.08)',
216 | },
217 | navItem: {
218 | position: 'relative',
219 | transition: 'background .23s ease',
220 | '&.active': {
221 | color: theme.palette.secondary.main,
222 | // background: 'rgba(0, 0, 0, 0.08)',
223 | '& .MuiListItemIcon-root': {
224 | // color: '#fff',
225 | color: theme.palette.secondary.main,
226 | },
227 | },
228 | },
229 | navItemChildren: {
230 | transition: 'background .23s ease',
231 | // position: 'absolute',
232 | },
233 | navItemChildrenActive: {
234 | // background: 'rgba(0, 0, 0, 0.1)',
235 | },
236 | navItemCollapsed: {
237 | whiteSpace: 'nowrap',
238 | flexWrap: 'nowrap',
239 | width: theme.sidebar.widthCollapsed,
240 | '& $iconToggle': {
241 | position: 'absolute',
242 | bottom: -1,
243 | fontSize: 14,
244 | left: '50%',
245 | marginLeft: '-0.5em',
246 | },
247 | '&.active': {
248 | background: 'rgba(0, 0, 0, 0.08)',
249 | },
250 | },
251 | navItemCollapsedWrapper: {
252 | width: theme.sidebar.widthCollapsed,
253 | },
254 | navItemIcon: {
255 | minWidth: 40,
256 | },
257 | iconToggle: {},
258 | iconSpacer: {
259 | fontSize: 13,
260 | marginLeft: 6,
261 | },
262 | }),
263 | )
264 |
265 | export default SidebarNavItem
266 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Sidebar/SidebarNavItems.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import SidebarNavItem, { SidebarNavItemProps } from './SidebarNavItem'
4 |
5 | export interface SidebarNavItemsProps {
6 | items: SidebarNavItemProps[]
7 | isNested?: boolean
8 | isCollapsed?: boolean
9 | }
10 |
11 | const SidebarNavItems: React.FC = (props: SidebarNavItemsProps) => {
12 | const { items = [], isCollapsed = false, isNested = false } = props
13 | // const classes = useStyles()
14 |
15 | return (
16 | <>
17 | {items.map((item, index) => (
18 |
24 | ))}
25 | >
26 | )
27 | }
28 |
29 | export default SidebarNavItems
30 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/Sidebar/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './Sidebar'
2 |
--------------------------------------------------------------------------------
/src/_layouts/DashboardLayout/index.tsx:
--------------------------------------------------------------------------------
1 | export { default } from './DashboardLayout'
2 |
--------------------------------------------------------------------------------
/src/_layouts/index.tsx:
--------------------------------------------------------------------------------
1 | import DashboardLayout from './DashboardLayout'
2 |
3 | export { DashboardLayout }
4 |
--------------------------------------------------------------------------------
/src/_services/authService.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * For the demo purposes we'll be using this predefined JWT token as the token of the signed in user
3 | * https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.Db8fjZU7MkBZoJDjmjuvv2EeDgG9RSaZ1xKm__qHelw
4 | */
5 |
6 | import store from 'store'
7 |
8 | // import config from '../config'
9 |
10 | const sampleToken =
11 | 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.Db8fjZU7MkBZoJDjmjuvv2EeDgG9RSaZ1xKm__qHelw'
12 |
13 | export interface AuthService {
14 | token: string | null
15 | init(options?: AuthServiceInitOptions): void
16 | auth(token: string): void
17 | unauth(): void
18 | isAuthenticated(): boolean
19 | getToken(): string | null
20 | }
21 |
22 | interface AuthServiceInitOptions {
23 | useSampleData?: boolean
24 | }
25 |
26 | const authService: AuthService = {
27 | token: null,
28 | init({ useSampleData = false } = {}) {
29 | if (useSampleData) {
30 | this.token = sampleToken
31 | } else {
32 | this.token = store.get('token') || null
33 | }
34 | },
35 | auth(token) {
36 | store.set('token', token)
37 | },
38 | unauth() {
39 | store.remove('token')
40 | },
41 | isAuthenticated() {
42 | return !!this.token
43 | },
44 | getToken() {
45 | return this.token
46 | },
47 | }
48 |
49 | export default authService
50 |
--------------------------------------------------------------------------------
/src/_services/helpersService.tsx:
--------------------------------------------------------------------------------
1 | // Helper type to ovverride types
2 | // https://stackoverflow.com/questions/43080547/how-to-override-type-properties-in-typescript?rq=1
3 | export type Override = Pick> & U
4 |
5 | const helpersService = {}
6 |
7 | export default helpersService
8 |
--------------------------------------------------------------------------------
/src/_state/appModel.ts:
--------------------------------------------------------------------------------
1 | import { createModel } from '@rematch/core' // RematchDispatch
2 | import User from '_types/User'
3 | import users from '_api/users'
4 |
5 | export interface AppStateStatus {
6 | loading?: boolean
7 | error?: Error
8 | }
9 |
10 | export interface AppStateData {
11 | user?: User
12 | }
13 | export interface AppState extends AppStateStatus {
14 | data: AppStateData
15 | }
16 |
17 | const initialState: AppState = {
18 | loading: true,
19 | error: undefined,
20 | data: {},
21 | }
22 |
23 | const model = createModel({
24 | state: initialState,
25 | reducers: {
26 | setStatus: (
27 | state: AppState,
28 | payload: { loading?: boolean; error?: any },
29 | ): AppState => ({
30 | ...state,
31 | ...payload,
32 | }),
33 | setData: (state: AppState, payload: AppStateData): AppState => ({
34 | ...state,
35 | data: {
36 | ...state.data,
37 | ...payload,
38 | },
39 | }),
40 | },
41 | // dispatch: RematchDispatch
42 | effects: () => ({
43 | // payload, rootState
44 | async request() {
45 | this.setStatus({
46 | loading: true,
47 | error: null,
48 | })
49 |
50 | try {
51 | const currentUser = await users.getProfile()
52 |
53 | this.setData({
54 | user: currentUser,
55 | })
56 | } catch (e) {
57 | this.setStatus({
58 | error: e,
59 | })
60 | }
61 |
62 | this.setStatus({
63 | loading: false,
64 | })
65 | },
66 | }),
67 | })
68 |
69 | export default model
70 |
--------------------------------------------------------------------------------
/src/_state/appState.ts:
--------------------------------------------------------------------------------
1 | import { useDispatch, useSelector } from 'react-redux'
2 | import { RootState, RootDispatch } from '_state'
3 |
4 | import dashboard, { AppStateData } from './appModel' //AppState
5 |
6 | export function useAppState() {
7 | return useSelector((state: RootState) => state.dashboard)
8 | }
9 |
10 | export function useAppStateData(): AppStateData {
11 | const { data } = useAppState()
12 |
13 | return data
14 | }
15 |
16 | export function useAppStateMethods() {
17 | const dispatch = useDispatch()
18 | return dispatch.dashboard
19 | }
20 |
21 | export default dashboard
22 |
--------------------------------------------------------------------------------
/src/_state/index.ts:
--------------------------------------------------------------------------------
1 | import { init, RematchRootState, RematchDispatch } from '@rematch/core'
2 | import immerPlugin from '@rematch/immer'
3 |
4 | import dashboard from '_state/appState'
5 |
6 | const immer = immerPlugin()
7 |
8 | const models = {
9 | dashboard,
10 | }
11 |
12 | export const store = init({
13 | models,
14 | plugins: [immer],
15 | })
16 |
17 | export type Store = typeof store
18 | export type RootState = RematchRootState
19 | export type RootDispatch = RematchDispatch
20 |
21 | export default store
22 |
--------------------------------------------------------------------------------
/src/_theme/index.ts:
--------------------------------------------------------------------------------
1 | import { Theme as MuiTheme, createMuiTheme } from '@material-ui/core/styles'
2 | import { blue } from '@material-ui/core/colors'
3 |
4 | export interface Theme extends MuiTheme {
5 | sidebar: {
6 | width: number
7 | widthCollapsed: number
8 | background: string
9 | color: string
10 | }
11 | header: {
12 | background: string
13 | }
14 | }
15 |
16 | const baseTheme = createMuiTheme({
17 | props: {
18 | MuiPaper: {
19 | elevation: 0,
20 | },
21 | MuiAppBar: {
22 | elevation: 1,
23 | },
24 | MuiButton: {
25 | // elevation: 0,
26 | },
27 | MuiMenu: {
28 | elevation: 1,
29 | },
30 | MuiCard: {
31 | elevation: 0,
32 | },
33 | },
34 | overrides: {
35 | MuiButton: {
36 | root: {
37 | minWidth: 0,
38 | },
39 | contained: {
40 | boxShadow: 'none',
41 | '&:active': {
42 | boxShadow: 'none',
43 | },
44 | '&:focus': {
45 | boxShadow: 'none',
46 | },
47 | },
48 | containedSecondary: {
49 | color: '#fff',
50 | '&:hover': {
51 | backgroundColor: 'rgb(118, 195, 21)',
52 | },
53 | },
54 | },
55 | MuiButtonGroup: {
56 | root: {
57 | boxShadow: 'none',
58 | },
59 | contained: {
60 | boxShadow: 'none',
61 | '&:active': {
62 | boxShadow: 'none',
63 | },
64 | '&:focus': {
65 | boxShadow: 'none',
66 | },
67 | },
68 | },
69 | MuiListItemIcon: {
70 | root: {
71 | minWidth: 40,
72 | },
73 | },
74 | },
75 | palette: {
76 | secondary: {
77 | main: '#8cd136', //indigo[600],
78 | },
79 | primary: {
80 | main: blue[600], //'#619f30',
81 | },
82 | },
83 | typography: {
84 | h1: {
85 | fontSize: '2rem',
86 | },
87 | h2: {
88 | fontSize: '1.8rem',
89 | },
90 | h3: {
91 | fontSize: '1.6rem',
92 | },
93 | h4: {
94 | fontSize: '1.4rem',
95 | },
96 | h5: {
97 | fontSize: '1.2rem',
98 | },
99 | h6: {
100 | fontSize: '1rem',
101 | },
102 | },
103 | })
104 |
105 | const adminTheme = {
106 | header: {
107 | background: '#fff',
108 | },
109 | sidebar: {
110 | width: 255,
111 | widthCollapsed: baseTheme.spacing(7),
112 | background: '#4a4d5a;',
113 | color: '#fff',
114 | },
115 | }
116 |
117 | const theme = {
118 | ...baseTheme,
119 | ...adminTheme,
120 | }
121 |
122 | export default theme
123 |
--------------------------------------------------------------------------------
/src/_types/Entity.ts:
--------------------------------------------------------------------------------
1 | export type EntityId = number | string
2 |
3 | export default interface Entity {
4 | createdAt?: string
5 | updatedAt?: string
6 | }
7 |
--------------------------------------------------------------------------------
/src/_types/Organization.ts:
--------------------------------------------------------------------------------
1 | import Entity, { EntityId } from '_types/Entity'
2 | import User from './User'
3 | import OrganizationToUser from './OrganizationToUser'
4 |
5 | export type OrganizationId = EntityId
6 | export interface OrganizationPlan {
7 | id: number | string
8 | name: string
9 | features?: {}
10 | }
11 |
12 | export interface OrganizationSubmissionData {
13 | name: string
14 | username?: string
15 | }
16 |
17 | export default interface Organization extends OrganizationSubmissionData, Entity {
18 | id: OrganizationId
19 | plan: OrganizationPlan
20 | users?: User[]
21 | organizationToUsers?: OrganizationToUser[]
22 | }
23 |
--------------------------------------------------------------------------------
/src/_types/OrganizationToUser.ts:
--------------------------------------------------------------------------------
1 | import Entity, { EntityId } from '_types/Entity'
2 |
3 | import Organization, { OrganizationId } from './Organization'
4 | import User, { UserId } from './User'
5 |
6 | export type OrganizationToUserId = EntityId
7 | export type OrganizationUserRole = 'member' | 'admin' | 'owner'
8 |
9 | export default interface OrganizationToUser extends Entity {
10 | id: OrganizationToUserId
11 | organizationId: OrganizationId
12 | userId: UserId
13 | role: OrganizationUserRole
14 | organization?: Organization
15 | user?: User
16 | }
17 |
--------------------------------------------------------------------------------
/src/_types/User.ts:
--------------------------------------------------------------------------------
1 | import Entity, { EntityId } from './Entity'
2 |
3 | import Organization from './Organization'
4 | import OrganizationToUser from './OrganizationToUser'
5 |
6 | export type UserId = EntityId
7 |
8 | // global user role across the system (useful for SAAS or if organizations arn't used)
9 | // Each user can have only one global role
10 | export type UserGlobalRole = 'admin' | 'support' | 'member'
11 |
12 | export interface UserSubmissionData {
13 | firstName?: string
14 | lastName?: string
15 | displayName?: string
16 | username?: string | null
17 | email: string
18 | password?: string
19 | avatarUrl?: string
20 | globalRole?: UserGlobalRole
21 | }
22 |
23 | export default interface User extends UserSubmissionData, Entity {
24 | id: UserId
25 | organizations?: Organization[]
26 | userToOrganizations?: OrganizationToUser[]
27 | }
28 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import 'typeface-roboto'
2 |
3 | import React from 'react'
4 | import ReactDOM from 'react-dom'
5 | import App from './App'
6 | import * as serviceWorker from './serviceWorker'
7 |
8 | import config from './_config'
9 | import authService from './_services/authService'
10 | import api from './_api'
11 |
12 | // Init the API service
13 | authService.init({
14 | useSampleData: config.useSampleData,
15 | })
16 |
17 | // Init rest API client
18 | api.init({
19 | useSampleData: config.useSampleData,
20 | })
21 |
22 | ReactDOM.render( , document.getElementById('root'))
23 |
24 | // If you want your app to work offline and load faster, you can change
25 | // unregister() to register() below. Note this comes with some pitfalls.
26 | // Learn more about service workers: https://bit.ly/CRA-PWA
27 | serviceWorker.unregister()
28 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/serviceWorker.ts:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/),
19 | )
20 |
21 | interface Config {
22 | onSuccess?: (registration: ServiceWorkerRegistration) => void
23 | onUpdate?: (registration: ServiceWorkerRegistration) => void
24 | }
25 |
26 | export function register(config?: Config): void {
27 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
28 | // The URL constructor is available in all browsers that support SW.
29 | const publicUrl = new URL((process as { env: { [key: string]: string } }).env.PUBLIC_URL, window.location.href)
30 | if (publicUrl.origin !== window.location.origin) {
31 | // Our service worker won't work if PUBLIC_URL is on a different origin
32 | // from what our page is served on. This might happen if a CDN is used to
33 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
34 | return
35 | }
36 |
37 | window.addEventListener('load', (): void => {
38 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`
39 |
40 | if (isLocalhost) {
41 | // This is running on localhost. Let's check if a service worker still exists or not.
42 | checkValidServiceWorker(swUrl, config)
43 |
44 | // Add some additional logging to localhost, pointing developers to the
45 | // service worker/PWA documentation.
46 | navigator.serviceWorker.ready.then((): void => {
47 | console.log(
48 | 'This web app is being served cache-first by a service ' +
49 | 'worker. To learn more, visit https://bit.ly/CRA-PWA',
50 | )
51 | })
52 | } else {
53 | // Is not localhost. Just register service worker
54 | registerValidSW(swUrl, config)
55 | }
56 | })
57 | }
58 | }
59 |
60 | function registerValidSW(swUrl: string, config?: Config): void {
61 | navigator.serviceWorker
62 | .register(swUrl)
63 | .then(registration => {
64 | registration.onupdatefound = () => {
65 | const installingWorker = registration.installing
66 | if (installingWorker == null) {
67 | return
68 | }
69 | installingWorker.onstatechange = () => {
70 | if (installingWorker.state === 'installed') {
71 | if (navigator.serviceWorker.controller) {
72 | // At this point, the updated precached content has been fetched,
73 | // but the previous service worker will still serve the older
74 | // content until all client tabs are closed.
75 | console.log(
76 | 'New content is available and will be used when all ' +
77 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.',
78 | )
79 |
80 | // Execute callback
81 | if (config && config.onUpdate) {
82 | config.onUpdate(registration)
83 | }
84 | } else {
85 | // At this point, everything has been precached.
86 | // It's the perfect time to display a
87 | // "Content is cached for offline use." message.
88 | console.log('Content is cached for offline use.')
89 |
90 | // Execute callback
91 | if (config && config.onSuccess) {
92 | config.onSuccess(registration)
93 | }
94 | }
95 | }
96 | }
97 | }
98 | })
99 | .catch(error => {
100 | console.error('Error during service worker registration:', error)
101 | })
102 | }
103 |
104 | function checkValidServiceWorker(swUrl: string, config?: Config) {
105 | // Check if the service worker can be found. If it can't reload the page.
106 | fetch(swUrl)
107 | .then(response => {
108 | // Ensure service worker exists, and that we really are getting a JS file.
109 | const contentType = response.headers.get('content-type')
110 | if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload()
115 | })
116 | })
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config)
120 | }
121 | })
122 | .catch(() => {
123 | console.log('No internet connection found. App is running in offline mode.')
124 | })
125 | }
126 |
127 | export function unregister() {
128 | if ('serviceWorker' in navigator) {
129 | navigator.serviceWorker.ready.then(registration => {
130 | registration.unregister()
131 | })
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "src",
4 | "target": "es2017",
5 | "lib": [
6 | "dom",
7 | "dom.iterable",
8 | "esnext"
9 | ],
10 | "allowJs": true,
11 | "skipLibCheck": true,
12 | "esModuleInterop": true,
13 | "allowSyntheticDefaultImports": true,
14 | "strict": true,
15 | "forceConsistentCasingInFileNames": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "preserve",
22 | "typeRoots" : ["./node_modules/@types/", "./src/_types"]
23 | },
24 | "include": [
25 | "src"
26 | ],
27 | "exclude": [
28 | "node_modules/@nivo/line/index.d.ts"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------