├── .github ├── ISSUE_TEMPLATE │ ├── ---bug-report.md │ └── ---feature-request.md └── windmill-dashboard-react.png ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── @types │ └── icons │ │ └── index.d.ts ├── App.tsx ├── assets │ ├── css │ │ └── tailwind.css │ └── img │ │ ├── create-account-office-dark.jpeg │ │ ├── create-account-office.jpeg │ │ ├── forgot-password-office-dark.jpeg │ │ ├── forgot-password-office.jpeg │ │ ├── login-office-dark.jpeg │ │ └── login-office.jpeg ├── components │ ├── AccessibleNavigationAnnouncer.tsx │ ├── CTA.tsx │ ├── Cards │ │ └── InfoCard.tsx │ ├── Chart │ │ ├── ChartCard.tsx │ │ └── ChartLegend.tsx │ ├── Header.tsx │ ├── RoundIcon.tsx │ ├── Sidebar │ │ ├── DesktopSidebar.tsx │ │ ├── MobileSidebar.tsx │ │ ├── SidebarContent.tsx │ │ ├── SidebarSubmenu.tsx │ │ └── index.tsx │ ├── ThemedSuspense.tsx │ └── Typography │ │ ├── PageTitle.tsx │ │ └── SectionTitle.tsx ├── containers │ ├── Layout.tsx │ └── Main.tsx ├── context │ ├── SidebarContext.tsx │ └── ThemeContext.tsx ├── icons │ ├── bell.svg │ ├── buttons.svg │ ├── cards.svg │ ├── cart.svg │ ├── charts.svg │ ├── chat.svg │ ├── dropdown.svg │ ├── edit.svg │ ├── forbidden.svg │ ├── forms.svg │ ├── github.svg │ ├── heart.svg │ ├── home.svg │ ├── index.ts │ ├── mail.svg │ ├── menu.svg │ ├── modals.svg │ ├── money.svg │ ├── moon.svg │ ├── outlineCog.svg │ ├── outlineLogout.svg │ ├── outlinePerson.svg │ ├── pages.svg │ ├── people.svg │ ├── search.svg │ ├── sun.svg │ ├── tables.svg │ ├── trash.svg │ └── twitter.svg ├── index.tsx ├── pages │ ├── 404.tsx │ ├── Blank.tsx │ ├── Buttons.tsx │ ├── Cards.tsx │ ├── Charts.tsx │ ├── CreateAccount.tsx │ ├── Dashboard.tsx │ ├── ForgotPassword.tsx │ ├── Forms.tsx │ ├── Login.tsx │ ├── Modals.tsx │ └── Tables.tsx ├── react-app-env.d.ts ├── routes │ ├── index.ts │ └── sidebar.tsx ├── serviceWorker.ts ├── setupTests.tsx └── utils │ └── demo │ ├── chartsData.ts │ ├── index.ts │ └── tableData.ts ├── tailwind.config.js ├── tsconfig.json └── yarn.lock /.github/ISSUE_TEMPLATE/---bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: 'Bugs, missing documentation, or unexpected behavior ' 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | 19 | 20 | `windmill-dashboard-react` version: `` 21 | 22 | ### Relevant code or config: 23 | 24 | ```html 25 | 26 | ``` 27 | 28 | ### What you did: 29 | 30 | 31 | 32 | ### What happened: 33 | 34 | 35 | 36 | ### Reproduction: 37 | 38 | 44 | 45 | ### Problem description: 46 | 47 | 48 | 49 | ### Suggested solution: 50 | 51 | 55 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4A1 Feature request" 3 | about: 'I have a suggestion (and might want to implement myself)! ' 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/windmill-dashboard-react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neutralboy/windmill-dashboard-react-ts/eb57233648ac05bf7d7c99709060bbc814cca76e/.github/windmill-dashboard-react.png -------------------------------------------------------------------------------- /.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 | .env 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | # css 27 | /src/assets/css/tailwind.output.css 28 | Dockerfile 29 | .devcontainer -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [0.4.0](https://github.com/estevanmaito/windmill-dashboard-react/compare/0.3.2...0.4.0) (2020-07-22) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * **buttons:** update icon Buttons syntax as recommended in @windmill/react-ui ([15260fd](https://github.com/estevanmaito/windmill-dashboard-react/commit/15260fd23600bfcef488edd818df35a7e4652a58)) 7 | * **sidebar:** hide sidebar until `lg` breakpoint for a better experience on tablets ([57293d9](https://github.com/estevanmaito/windmill-dashboard-react/commit/57293d92d76bbe48f8fab71ea6ef8606fbce72a3)), closes [#3](https://github.com/estevanmaito/windmill-dashboard-react/issues/3) 8 | 9 | 10 | ### Features 11 | 12 | * **app:** add antialiased font smoothing ([2355363](https://github.com/estevanmaito/windmill-dashboard-react/commit/2355363d30b94ab2e9862f2bfb3eac0e6c75ef61)) 13 | 14 | 15 | ### BREAKING CHANGES 16 | 17 | * **sidebar:** The sidebar now only shows from `lg` breakpoint up, instead of `md`. This should 18 | create a better experience on smaller screens, especially tablets. 19 | 20 | ## [0.3.2](https://github.com/estevanmaito/windmill-dashboard-react/compare/0.3.1...0.3.2) (2020-07-21) 21 | 22 | 23 | ### Bug Fixes 24 | 25 | * **modal:** improve button visibility between sm and md breakpoints ([f9dc194](https://github.com/estevanmaito/windmill-dashboard-react/commit/f9dc1941dff21d8d22a7628903fa44c65b86d316)) 26 | 27 | ## [0.3.1](https://github.com/estevanmaito/windmill-dashboard-react/compare/0.3.0...0.3.1) (2020-07-21) 28 | 29 | 30 | ### Bug Fixes 31 | 32 | * **windmill:** bump @windmill/react-ui from 0.3.0 to 0.3.1 ([2d3499c](https://github.com/estevanmaito/windmill-dashboard-react/commit/2d3499c4ca3a0b69cc4c9885303505f03646c111)) 33 | 34 | # [0.3.0](https://github.com/estevanmaito/windmill-dashboard-react/compare/0.2.2...0.3.0) (2020-07-21) 35 | 36 | 37 | ### Bug Fixes 38 | 39 | * **windmill:** bump windmill to the scoped package ([dad6261](https://github.com/estevanmaito/windmill-dashboard-react/commit/dad626125c05816ddba8cd4dd11c08c4f8154095)) 40 | 41 | 42 | ### BREAKING CHANGES 43 | 44 | * **windmill:** Changed every occurrence of `windmill-react-ui` with the scoped 45 | `@windmill/react-ui`. You should see no difference updating and you should update, as the other 46 | project is now deprecated and will no longer be developed. This was needed because the `windmill` 47 | org was in dispute and npm solved it today. 48 | 49 | ## [0.2.2](https://github.com/estevanmaito/windmill-dashboard-react/compare/0.2.1...0.2.2) (2020-07-19) 50 | 51 | 52 | ### Bug Fixes 53 | 54 | * **env:** use cross-env for env variables ([18fdfda](https://github.com/estevanmaito/windmill-dashboard-react/commit/18fdfda80f00bd5e5f2cfa0fb8c3de7a5a32fb02)) 55 | * **windmill:** bump windmill-react-ui from 0.1.2-beta.0 to 0.1.2 ([07dcf08](https://github.com/estevanmaito/windmill-dashboard-react/commit/07dcf086125c263b85fb72bdf4c34429e83923b7)) 56 | 57 | ## [0.2.1](https://github.com/estevanmaito/windmill-dashboard-react/compare/0.2.0...0.2.1) (2020-07-17) 58 | 59 | 60 | ### Bug Fixes 61 | 62 | * **header:** fix bleeding header shadow ([4eef708](https://github.com/estevanmaito/windmill-dashboard-react/commit/4eef7087e7e3d63b12cca040084f68d13604cb57)) 63 | 64 | # [0.2.0](https://github.com/estevanmaito/windmill-dashboard-react/compare/0.1.0...0.2.0) (2020-07-17) 65 | 66 | 67 | ### Bug Fixes 68 | 69 | * **favicon:** update logo for pwa ([5e788d2](https://github.com/estevanmaito/windmill-dashboard-react/commit/5e788d24b8a18f90b450c85d04f319daf42bcf82)) 70 | 71 | 72 | ### Features 73 | 74 | * **global:** update windmill-react-ui ([6c5c3c0](https://github.com/estevanmaito/windmill-dashboard-react/commit/6c5c3c0cb42df1bbbe8cf5d7e5e637101d556433)) 75 | * **serviceworker:** activate service worker ([984dd8b](https://github.com/estevanmaito/windmill-dashboard-react/commit/984dd8b87aa7ee54f7a521550b8ad9c396b21e04)) 76 | 77 | 78 | ### BREAKING CHANGES 79 | 80 | * **global:** Now you need to wrap the entire tailwind config object with Windmill's config 81 | wrapper. There is no need to use Windmill plugin or purge anymore. 82 | 83 | Complete example: 84 | 85 | ```js 86 | const defaultTheme = require('tailwindcss/defaultTheme') 87 | const windmill = require('windmill-react-ui/config') 88 | 89 | module.exports = windmill({ 90 | purge: ['src/**/*.js'], 91 | theme: { 92 | extend: { 93 | fontFamily: { 94 | sans: ['Inter', ...defaultTheme.fontFamily.sans], 95 | }, 96 | }, 97 | }, 98 | }) 99 | ``` 100 | 101 | # [0.1.0](https://github.com/estevanmaito/windmill-dashboard-react/compare/0.1.0-alpha.2...0.1.0) (2020-07-14) 102 | 103 | 104 | ### Features 105 | 106 | * **global:** upgrade to last windmill-ui version ([708fce4](https://github.com/estevanmaito/windmill-dashboard-react/commit/708fce44efe18e97190775fb41ca068c653549a7)) 107 | 108 | # [0.1.0-alpha.2](https://github.com/estevanmaito/windmill-dashboard-react/compare/0.1.0-alpha.1...0.1.0-alpha.2) (2020-07-07) 109 | 110 | 111 | ### Bug Fixes 112 | 113 | * **avatar:** update avatar image syntax ([e16ea68](https://github.com/estevanmaito/windmill-dashboard-react/commit/e16ea68435ffd9bc7404dfb80120ff18c4c39052)) 114 | * **layout:** increase header and sidebar z-index ([c948638](https://github.com/estevanmaito/windmill-dashboard-react/commit/c9486383e3129fb57af5aa0a9ca91c57946c44c1)) 115 | * **routes:** redirect index to login ([06405ac](https://github.com/estevanmaito/windmill-dashboard-react/commit/06405ac345c4bdfa6e66bf5225bad868d5721d8b)) 116 | 117 | 118 | ### BREAKING CHANGES 119 | 120 | * **avatar:** Avatar, with the udate to windmill-react-ui-alpha.10, now uses src instead of img 121 | to pass the image path to the inner image, to better resemble the actual element underneath 122 | * **layout:** increased z-index for header and sidebar 123 | 124 | # 0.1.0-alpha.1 (2020-07-01) 125 | 126 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [INSERT CONTACT]. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Estevan Maito 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Windmill Dashboard React 4 |
5 | Four 100 scores and PWA ready. Just connect your data. 6 |

7 | 8 | 🚀 [See it live](https://windmillui.com/dashboard-react) 9 | 10 | This is not a template. This is a complete application, built on top of React, with all tiny details taken care of so you just need to bring the data to feed it. 11 | 12 | Accessibility is a priority in my projects and I think it should be in yours too, so this was developed listening to real screen readers, focus traps and keyboard navigation are available everywhere. 13 | 14 | This is a Typescript version of the popular windmill-dashboard-react. It works the exact same way. You can find the original project [here](https://github.com/estevanmaito/windmill-dashboard-react) 15 | 16 | ## 📦 Features 17 | 18 | - 🦮 Throughly accessible (developed using screen readers) 19 | - 🌗 Dark theme enabled (load even different images based on theme) 20 | - 🧩 Multiple (custom) components 21 | - ⚡ Code splitting 22 | - Tailwind CSS 23 | - [Windmill React UI](https://windmillui.com/react-ui) 24 | - React Router 25 | - Heroicons 26 | - Chart.js 27 | - PWA delivering offline-first and app-like experience 28 | 29 | ## 📚 Docs 30 | 31 | ### General components 32 | 33 | Windmill Dashboard React is built on top of [Windmill React UI](https://windmillui.com/react-ui). You will find the documentation for every small component there. 34 | 35 | ### Routing 36 | 37 | Routes in Windmill Dashboard are separated into two categories, sidebar ([routes/sidebar.js](src/routes/sidebar.js)) and general ([routes/index.js](src/routes/index.js)). 38 | 39 | #### Sidebar routes 40 | 41 | These are the routes that will show in the sidebar. They expect three properties: 42 | 43 | - `path`: the destination; 44 | - `name`: the name to be shown; 45 | - `icon`: an icon to illustrate the item 46 | 47 | Item that are used as dropdowns, like the Pages option, don't need a `path`, but expect a `routes` array of objects with `path` and `name`: 48 | 49 | ```js 50 | // sidebar.js 51 | { 52 | path: '/app/tables', 53 | icon: 'TablesIcon', 54 | name: 'Tables', 55 | }, 56 | { 57 | icon: 'PagesIcon', // <-- this is used as a submenu, so no path 58 | name: 'Pages', 59 | routes: [ 60 | // submenu 61 | { 62 | path: '/login', 63 | name: 'Login', // <-- these don't have icons 64 | }, 65 | { 66 | path: '/create-account', 67 | name: 'Create account', 68 | }, 69 | ``` 70 | 71 | #### General (Router) routes 72 | 73 | These are **internal** (private) routes. They will be rendered inside the app, using the default `containers/Layout`. 74 | 75 | If you want to add a route to, let's say, a landing page, you should add it to the `App`'s router ([src/App.js](src/App.js), exactly like `Login`, `CreateAccount` and other pages are routed. 76 | 77 | #### How to add a new page to router? 78 | 79 | 1. Create your page inside `src/pages`, say `MyPage.js`; 80 | 2. Add it to the global router (`src/routes/index.js`) 81 | 82 | ```js 83 | const MyPage = lazy(() => import('../pages/MyPage')) 84 | ``` 85 | 86 | Then add it to the `routes` array: 87 | 88 | ```js 89 | { 90 | path: '/my-page', // the url that will be added to /app/ 91 | component: MyPage, // the page component you jsut imported 92 | } 93 | ``` 94 | 95 | 3. If you want to make this page accessible from the sidebar, you have to options: 96 | 97 | - add it to the root `routes` array 98 | 99 | ```js 100 | { 101 | path: '/app/my-page', // /app + the url you added in routes/index.js 102 | icon: 'HomeIcon', // the component being exported from src/icons/index.js 103 | name: 'My Page', // name that appear in Sidebar 104 | }, 105 | ``` 106 | 107 | - add it as an option under a dropdown 108 | 109 | ```js 110 | { 111 | icon: 'PagesIcon', 112 | name: 'Pages', 113 | routes: [ 114 | // submenu 115 | { 116 | path: '/app/my-page', 117 | name: 'My Page', 118 | }, 119 | ``` 120 | 121 | If you're asking where does this `/app` come from, it is from this line inside `src/App.js`, that renders the app: 122 | 123 | ```jsx 124 | 125 | ``` 126 | 127 | --- 128 | 129 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 130 | 131 | ## Available Scripts 132 | 133 | In the project directory, you can run: 134 | 135 | ### `npm start` 136 | 137 | Runs the app in the development mode.
138 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 139 | 140 | The page will reload if you make edits.
141 | You will also see any lint errors in the console. 142 | 143 | ### `npm test` 144 | 145 | Launches the test runner in the interactive watch mode.
146 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 147 | 148 | ### `npm run build` 149 | 150 | Builds the app for production to the `build` folder.
151 | It correctly bundles React in production mode and optimizes the build for the best performance. 152 | 153 | The build is minified and the filenames include the hashes.
154 | Your app is ready to be deployed! 155 | 156 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 157 | 158 | ### `npm run eject` 159 | 160 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 161 | 162 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 163 | 164 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 165 | 166 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 167 | 168 | ## Learn More 169 | 170 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 171 | 172 | To learn React, check out the [React documentation](https://reactjs.org/). 173 | 174 | ### Code Splitting 175 | 176 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 177 | 178 | ### Analyzing the Bundle Size 179 | 180 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 181 | 182 | ### Making a Progressive Web App 183 | 184 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 185 | 186 | ### Advanced Configuration 187 | 188 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 189 | 190 | ### Deployment 191 | 192 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 193 | 194 | ### `npm run build` fails to minify 195 | 196 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 197 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "windmill-dashboard-react", 3 | "version": "0.4.0", 4 | "description": "A multi theme, completely accessible, with components and pages examples, (almost) ready for production dashboard.", 5 | "private": true, 6 | "scripts": { 7 | "tailwind:dev": "tailwindcss build src/assets/css/tailwind.css -o src/assets/css/tailwind.output.css", 8 | "tailwind:build": "cross-env NODE_ENV=production postcss src/assets/css/tailwind.css -o src/assets/css/tailwind.output.css", 9 | "prestart": "npm run tailwind:dev", 10 | "start": "react-scripts start", 11 | "prebuild": "npm run tailwind:build", 12 | "build": "react-scripts build", 13 | "cz": "git-cz", 14 | "release": "release-it", 15 | "test": "react-scripts test", 16 | "eject": "react-scripts eject", 17 | "tsc": "tsc" 18 | }, 19 | "dependencies": { 20 | "@testing-library/jest-dom": "4.2.4", 21 | "@testing-library/react": "9.5.0", 22 | "@testing-library/user-event": "7.2.1", 23 | "@windmill/react-ui": "^0.6.0", 24 | "chart.js": "2.9.3", 25 | "classnames": "^2.3.1", 26 | "faker": "4.1.0", 27 | "react": "^16.13.1", 28 | "react-chartjs-2": "2.9.0", 29 | "react-dom": "^16.13.1", 30 | "react-focus-lock": "2.4.0", 31 | "react-router-dom": "^5.2.0", 32 | "react-scripts": "^4.0.3", 33 | "react-transition-group": "4.4.1" 34 | }, 35 | "devDependencies": { 36 | "@release-it/conventional-changelog": "1.1.4", 37 | "@svgr/webpack": "5.4.0", 38 | "@types/classnames": "^2.3.1", 39 | "@types/react-router-dom": "^5.1.7", 40 | "autoprefixer": "^10.2.6", 41 | "commitizen": "4.1.2", 42 | "cross-env": "7.0.2", 43 | "cssnano": "^5.0.6", 44 | "cz-conventional-changelog": "3.2.0", 45 | "postcss": "^8.3.2", 46 | "postcss-cli": "^8.3.1", 47 | "react-axe": "3.5.2", 48 | "release-it": "13.6.4", 49 | "tailwindcss": "^2.1.4", 50 | "typescript": "^4.3.2" 51 | }, 52 | "keywords": [ 53 | "windmill", 54 | "dashboard", 55 | "admin", 56 | "tailwind", 57 | "react" 58 | ], 59 | "release-it": { 60 | "github": { 61 | "release": true 62 | }, 63 | "npm": { 64 | "publish": false 65 | }, 66 | "plugins": { 67 | "@release-it/conventional-changelog": { 68 | "preset": "angular", 69 | "infile": "CHANGELOG.md" 70 | } 71 | } 72 | }, 73 | "config": { 74 | "commitizen": { 75 | "path": "./node_modules/cz-conventional-changelog" 76 | } 77 | }, 78 | "eslintConfig": { 79 | "extends": "react-app" 80 | }, 81 | "browserslist": { 82 | "production": [ 83 | ">0.2%", 84 | "not dead", 85 | "not op_mini all" 86 | ], 87 | "development": [ 88 | "last 1 chrome version", 89 | "last 1 firefox version", 90 | "last 1 safari version" 91 | ] 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('tailwindcss'), 4 | require('autoprefixer'), 5 | require('cssnano')({ 6 | preset: 'default', 7 | }), 8 | ], 9 | } 10 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neutralboy/windmill-dashboard-react-ts/eb57233648ac05bf7d7c99709060bbc814cca76e/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 24 | 28 | Windmill Dashboard 29 | 30 | 31 | 32 |
33 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neutralboy/windmill-dashboard-react-ts/eb57233648ac05bf7d7c99709060bbc814cca76e/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neutralboy/windmill-dashboard-react-ts/eb57233648ac05bf7d7c99709060bbc814cca76e/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Windmill", 3 | "name": "Windmill Dashboard React", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/@types/icons/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | import React from "react"; 3 | const ReactComponent: React.FC>; 4 | export default ReactComponent; 5 | // const content: (props: React.SVGProps) => React.ReactElement; 6 | // export default content; 7 | } -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { lazy } from 'react' 2 | import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom' 3 | import AccessibleNavigationAnnouncer from './components/AccessibleNavigationAnnouncer' 4 | 5 | const Layout = lazy(() => import('./containers/Layout')) 6 | const Login = lazy(() => import('./pages/Login')) 7 | const CreateAccount = lazy(() => import('./pages/CreateAccount')) 8 | const ForgotPassword = lazy(() => import('./pages/ForgotPassword')) 9 | 10 | function App() { 11 | return ( 12 | <> 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {/* Place new routes over this */} 21 | 22 | {/* If you have an index page, you can remothis Redirect */} 23 | 24 | 25 | 26 | 27 | ) 28 | } 29 | 30 | export default App 31 | -------------------------------------------------------------------------------- /src/assets/css/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/assets/img/create-account-office-dark.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neutralboy/windmill-dashboard-react-ts/eb57233648ac05bf7d7c99709060bbc814cca76e/src/assets/img/create-account-office-dark.jpeg -------------------------------------------------------------------------------- /src/assets/img/create-account-office.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neutralboy/windmill-dashboard-react-ts/eb57233648ac05bf7d7c99709060bbc814cca76e/src/assets/img/create-account-office.jpeg -------------------------------------------------------------------------------- /src/assets/img/forgot-password-office-dark.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neutralboy/windmill-dashboard-react-ts/eb57233648ac05bf7d7c99709060bbc814cca76e/src/assets/img/forgot-password-office-dark.jpeg -------------------------------------------------------------------------------- /src/assets/img/forgot-password-office.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neutralboy/windmill-dashboard-react-ts/eb57233648ac05bf7d7c99709060bbc814cca76e/src/assets/img/forgot-password-office.jpeg -------------------------------------------------------------------------------- /src/assets/img/login-office-dark.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neutralboy/windmill-dashboard-react-ts/eb57233648ac05bf7d7c99709060bbc814cca76e/src/assets/img/login-office-dark.jpeg -------------------------------------------------------------------------------- /src/assets/img/login-office.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neutralboy/windmill-dashboard-react-ts/eb57233648ac05bf7d7c99709060bbc814cca76e/src/assets/img/login-office.jpeg -------------------------------------------------------------------------------- /src/components/AccessibleNavigationAnnouncer.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { useLocation } from 'react-router-dom'; 3 | 4 | function AccessibleNavigationAnnouncer() { 5 | const [message, setMessage] = useState(''); 6 | const location = useLocation(); 7 | 8 | useEffect(() => { 9 | // ignore the / 10 | if (location.pathname.slice(1)) { 11 | // make sure navigation has occurred and screen reader is ready 12 | setTimeout(() => setMessage(`Navigated to ${location.pathname.slice(1)} page.`), 500); 13 | } else { 14 | setMessage(''); 15 | } 16 | }, [location]) 17 | 18 | return ( 19 | 20 | {message} 21 | 22 | ); 23 | }; 24 | 25 | export default AccessibleNavigationAnnouncer; 26 | -------------------------------------------------------------------------------- /src/components/CTA.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function CTA() { 4 | return ( 5 | 9 |
10 | 11 | 12 | 13 | Star this project on GitHub 14 |
15 | 16 | View more 17 | 18 |
19 | ); 20 | }; 21 | 22 | export default CTA; 23 | -------------------------------------------------------------------------------- /src/components/Cards/InfoCard.tsx: -------------------------------------------------------------------------------- 1 | import { ReactSVGElement } from 'react'; 2 | import { Card, CardBody } from '@windmill/react-ui'; 3 | 4 | interface IInfoCard{ 5 | title: string 6 | value: string 7 | children?: ReactSVGElement 8 | } 9 | 10 | function InfoCard({ title, value, children }: IInfoCard) { 11 | return ( 12 | 13 | 14 | {children} 15 |
16 |

{title}

17 |

{value}

18 |
19 |
20 |
21 | ); 22 | }; 23 | 24 | export default InfoCard; 25 | -------------------------------------------------------------------------------- /src/components/Chart/ChartCard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface IChart{ 4 | children: React.ReactNode 5 | title: string 6 | }; 7 | 8 | function Chart({ children, title }: IChart) { 9 | return ( 10 |
11 |

{title}

12 | {children} 13 |
14 | ) 15 | } 16 | 17 | export default Chart; 18 | -------------------------------------------------------------------------------- /src/components/Chart/ChartLegend.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import {ILegends} from "../../utils/demo/chartsData"; 4 | 5 | interface IChartLegend{ 6 | legends: ILegends[] 7 | } 8 | 9 | function ChartLegend({ legends }: IChartLegend) { 10 | return ( 11 |
12 | {legends.map((legend) => ( 13 |
14 | 15 | {legend.title} 16 |
17 | ))} 18 |
19 | ) 20 | } 21 | 22 | export default ChartLegend 23 | -------------------------------------------------------------------------------- /src/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import { useContext, useState } from 'react'; 2 | import { SidebarContext } from '../context/SidebarContext'; 3 | import { 4 | SearchIcon, 5 | MoonIcon, 6 | SunIcon, 7 | BellIcon, 8 | MenuIcon, 9 | OutlinePersonIcon, 10 | OutlineCogIcon, 11 | OutlineLogoutIcon, 12 | } from '../icons'; 13 | import { Avatar, Badge, Input, Dropdown, DropdownItem, WindmillContext } from '@windmill/react-ui'; 14 | 15 | function Header() { 16 | const { mode, toggleMode } = useContext(WindmillContext); 17 | const { toggleSidebar } = useContext(SidebarContext); 18 | 19 | const [isNotificationsMenuOpen, setIsNotificationsMenuOpen] = useState(false); 20 | const [isProfileMenuOpen, setIsProfileMenuOpen] = useState(false); 21 | 22 | function handleNotificationsClick() { 23 | setIsNotificationsMenuOpen(!isNotificationsMenuOpen); 24 | }; 25 | 26 | function handleProfileClick() { 27 | setIsProfileMenuOpen(!isProfileMenuOpen); 28 | }; 29 | 30 | return ( 31 |
32 |
33 | {/* */} 34 | 41 | {/* */} 42 |
43 |
44 |
45 |
47 | 53 |
54 |
55 |
    56 | {/* */} 57 |
  • 58 | 69 |
  • 70 | {/* */} 71 |
  • 72 | 85 | 86 | setIsNotificationsMenuOpen(false)} 90 | > 91 | 92 | Messages 93 | 13 94 | 95 | 96 | Sales 97 | 2 98 | 99 | alert('Alerts!')}> 100 | Alerts 101 | 102 | 103 |
  • 104 | {/* */} 105 |
  • 106 | 119 | setIsProfileMenuOpen(false)} 123 | > 124 | 125 | 128 | 129 | 132 | alert('Log out!')}> 133 | 136 | 137 |
  • 138 |
139 |
140 |
141 | ); 142 | }; 143 | 144 | export default Header; 145 | -------------------------------------------------------------------------------- /src/components/RoundIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import classNames from 'classnames'; 3 | 4 | interface IRoundIcon{ 5 | icon: any 6 | className: string 7 | iconColorClass: string 8 | bgColorClass: string 9 | }; 10 | 11 | function RoundIcon({ 12 | icon, 13 | iconColorClass = 'text-purple-600 dark:text-purple-100', 14 | bgColorClass = 'bg-purple-100 dark:bg-purple-600', 15 | className 16 | }: IRoundIcon) { 17 | const baseStyle = 'p-3 rounded-full'; 18 | const cls = classNames(baseStyle, iconColorClass, bgColorClass, className); 19 | return( 20 |
21 | {/* */} 22 |
23 | ); 24 | }; 25 | 26 | export default RoundIcon; 27 | -------------------------------------------------------------------------------- /src/components/Sidebar/DesktopSidebar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SidebarContent from './SidebarContent'; 3 | 4 | function DesktopSidebar() { 5 | return ( 6 | 9 | ); 10 | }; 11 | 12 | export default DesktopSidebar; 13 | -------------------------------------------------------------------------------- /src/components/Sidebar/MobileSidebar.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | 3 | import SidebarContent from './SidebarContent'; 4 | import { Transition, Backdrop } from '@windmill/react-ui'; 5 | 6 | import { SidebarContext } from '../../context/SidebarContext'; 7 | 8 | function MobileSidebar() { 9 | const { isSidebarOpen, closeSidebar } = useContext(SidebarContext); 10 | 11 | return ( 12 | 13 | <> 14 | 22 | 23 | 24 | 25 | 33 | 36 | 37 | 38 | 39 | ); 40 | }; 41 | 42 | export default MobileSidebar; 43 | -------------------------------------------------------------------------------- /src/components/Sidebar/SidebarContent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import routes from '../../routes/sidebar'; 3 | import { NavLink, Route } from 'react-router-dom'; 4 | import * as Icons from '../../icons'; 5 | import SidebarSubmenu from './SidebarSubmenu'; 6 | import { Button } from '@windmill/react-ui'; 7 | import { IIcon } from "../../utils/demo"; 8 | 9 | 10 | function Icon({ icon, ...props }: IIcon){ 11 | // @ts-ignore 12 | const Icon= Icons[icon]; 13 | return 14 | } 15 | 16 | function SidebarContent() { 17 | return ( 18 |
19 | 20 | Windmill 21 | 22 |
    23 | {routes.map((route) => 24 | route.routes ? ( 25 | 26 | ) : ( 27 |
  • 28 | 34 | 35 | 39 | 40 | 43 |
  • 44 | ) 45 | )} 46 |
47 |
48 | 54 |
55 |
56 | ) 57 | } 58 | 59 | export default SidebarContent -------------------------------------------------------------------------------- /src/components/Sidebar/SidebarSubmenu.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { DropdownIcon } from '../../icons'; 4 | import * as Icons from '../../icons'; 5 | import { Transition } from '@windmill/react-ui'; 6 | import { IRoute } from '../../routes/sidebar'; 7 | import { IIcon } from "../../utils/demo"; 8 | 9 | function Icon({ icon, ...props }: IIcon) { 10 | // @ts-ignore 11 | const Icon = Icons[icon]; 12 | return 13 | } 14 | 15 | interface ISidebarSubmenu{ route: IRoute }; 16 | 17 | function SidebarSubmenu({ route }: ISidebarSubmenu) { 18 | const [isDropdownMenuOpen, setIsDropdownMenuOpen] = useState(false) 19 | 20 | function handleDropdownMenuClick() { 21 | setIsDropdownMenuOpen(!isDropdownMenuOpen) 22 | } 23 | 24 | return ( 25 |
  • 26 | 37 | 46 |
      50 | { 51 | route.routes && route.routes.map((r) => ( 52 |
    • 56 | 57 | {r.name} 58 | 59 |
    • 60 | )) 61 | } 62 |
    63 |
    64 |
  • 65 | ) 66 | } 67 | 68 | export default SidebarSubmenu 69 | -------------------------------------------------------------------------------- /src/components/Sidebar/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import DesktopSidebar from './DesktopSidebar'; 3 | import MobileSidebar from './MobileSidebar'; 4 | 5 | function Sidebar() { 6 | return ( 7 | <> 8 | 9 | 10 | 11 | ); 12 | }; 13 | 14 | export default Sidebar; 15 | -------------------------------------------------------------------------------- /src/components/ThemedSuspense.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function ThemedSuspense() { 4 | return ( 5 |
    6 | Loading... 7 |
    8 | ); 9 | }; 10 | 11 | export default ThemedSuspense; 12 | -------------------------------------------------------------------------------- /src/components/Typography/PageTitle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface IPageTitle{ 4 | children: React.ReactNode 5 | }; 6 | 7 | function PageTitle({ children }: IPageTitle) { 8 | return ( 9 |

    {children}

    10 | ); 11 | }; 12 | 13 | export default PageTitle; 14 | -------------------------------------------------------------------------------- /src/components/Typography/SectionTitle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface ISectionTitle{ 4 | children: React.ReactNode 5 | } 6 | 7 | function SectionTitle({ children }: ISectionTitle) { 8 | return

    {children}

    9 | } 10 | 11 | export default SectionTitle; 12 | -------------------------------------------------------------------------------- /src/containers/Layout.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, Suspense, useEffect, lazy } from 'react'; 2 | import { Switch, Route, Redirect, useLocation } from 'react-router-dom'; 3 | import routes from '../routes'; 4 | 5 | import Sidebar from '../components/Sidebar'; 6 | import Header from '../components/Header'; 7 | import Main from './Main'; 8 | import ThemedSuspense from '../components/ThemedSuspense'; 9 | import { SidebarContext } from '../context/SidebarContext'; 10 | 11 | const Page404 = lazy(() => import('../pages/404')); 12 | 13 | function Layout() { 14 | const { isSidebarOpen, closeSidebar } = useContext(SidebarContext); 15 | let location = useLocation(); 16 | 17 | useEffect(() => { 18 | closeSidebar() 19 | }, [location, closeSidebar]); 20 | 21 | return ( 22 |
    25 | 26 | 27 |
    28 |
    29 |
    30 | }> 31 | 32 | {routes.map((route, i) => { 33 | return route.component ? ( 34 | } 39 | /> 40 | ) : null 41 | })} 42 | 43 | 44 | 45 | 46 |
    47 |
    48 |
    49 | ); 50 | }; 51 | 52 | export default Layout; 53 | -------------------------------------------------------------------------------- /src/containers/Main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface IMain{ 4 | children: React.ReactNode 5 | }; 6 | 7 | function Main({ children }: IMain) { 8 | return ( 9 |
    10 |
    {children}
    11 |
    12 | ); 13 | }; 14 | 15 | export default Main; -------------------------------------------------------------------------------- /src/context/SidebarContext.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useMemo } from 'react'; 2 | 3 | // create context 4 | interface ISidebarContext{ 5 | isSidebarOpen: boolean 6 | closeSidebar: () => void 7 | toggleSidebar: () => void 8 | } 9 | 10 | export const SidebarContext = React.createContext({ isSidebarOpen: false, closeSidebar: () => { }, toggleSidebar: () => {} }); 11 | 12 | interface ISidebarPovider{ children: React.ReactNode } 13 | 14 | export const SidebarProvider = ({ children }: ISidebarPovider) => { 15 | const [isSidebarOpen, setIsSidebarOpen] = useState(false) 16 | 17 | function toggleSidebar() { 18 | setIsSidebarOpen(!isSidebarOpen) 19 | } 20 | 21 | function closeSidebar() { 22 | setIsSidebarOpen(false) 23 | } 24 | 25 | const value = useMemo( 26 | 27 | () => ({ 28 | isSidebarOpen, 29 | toggleSidebar, 30 | closeSidebar, 31 | }), 32 | [isSidebarOpen, toggleSidebar] 33 | ) 34 | 35 | return {children} 36 | } 37 | -------------------------------------------------------------------------------- /src/context/ThemeContext.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef, useLayoutEffect, useMemo } from 'react'; 2 | 3 | /** 4 | * Saves the old theme for future use 5 | * @param {string} theme - Name of curent theme 6 | * @return {string} previousTheme 7 | */ 8 | function usePrevious(theme: string) { 9 | const ref = useRef(); 10 | useEffect(() => { 11 | ref.current = theme 12 | }) 13 | return ref.current; 14 | }; 15 | 16 | /** 17 | * Gets user preferences from local storage 18 | * @param {string} key - localStorage key 19 | * @return {array} getter and setter for user preferred theme 20 | */ 21 | function useStorageTheme(key: string): [string, React.Dispatch>]{ 22 | const userPreference = 23 | !!window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; 24 | 25 | const [theme, setTheme] = useState( 26 | // use stored theme; fallback to user preference 27 | localStorage.getItem(key) || userPreference 28 | ); 29 | 30 | // update stored theme 31 | useEffect(() => { 32 | localStorage.setItem(key.toString(), theme.toString()) 33 | }, [theme, key]); 34 | 35 | return [theme.toString(), setTheme]; 36 | }; 37 | 38 | interface IThemeContext{ 39 | theme: string | React.Dispatch> 40 | toggleTheme: () => void 41 | }; 42 | 43 | // create context 44 | export const ThemeContext = React.createContext({ theme: "", toggleTheme: () => {} }); 45 | 46 | interface IThemeProvider{ 47 | children: React.ReactNode 48 | }; 49 | 50 | // create context provider 51 | export const ThemeProvider = ({ children }: IThemeProvider) => { 52 | const [theme, setTheme] = useStorageTheme('theme'); 53 | 54 | // update root element class on theme change 55 | const oldTheme = usePrevious(theme.toString()); 56 | useLayoutEffect(() => { 57 | document.documentElement.classList.remove(`theme-${oldTheme}`) 58 | document.documentElement.classList.add(`theme-${theme}`) 59 | }, [theme, oldTheme]); 60 | 61 | function toggleTheme() { 62 | 63 | if (theme === 'light'){ 64 | setTheme('dark'); 65 | } 66 | else{ 67 | setTheme('light'); 68 | }; 69 | }; 70 | 71 | const value = useMemo( 72 | () => ({ 73 | theme, 74 | toggleTheme, 75 | }), 76 | [theme] 77 | ); 78 | 79 | return {children} 80 | } -------------------------------------------------------------------------------- /src/icons/bell.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/icons/buttons.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/cards.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/cart.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/icons/charts.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/icons/chat.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/icons/dropdown.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/icons/edit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/icons/forbidden.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/icons/forms.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/heart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/home.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/index.ts: -------------------------------------------------------------------------------- 1 | import { ReactComponent as ButtonsIcon } from './buttons.svg'; 2 | import { ReactComponent as CardsIcon } from './cards.svg'; 3 | import { ReactComponent as ChartsIcon } from './charts.svg'; 4 | import { ReactComponent as FormsIcon } from './forms.svg'; 5 | import { ReactComponent as HomeIcon } from './home.svg'; 6 | import { ReactComponent as ModalsIcon } from './modals.svg'; 7 | import { ReactComponent as PagesIcon } from './pages.svg'; 8 | import { ReactComponent as TablesIcon } from './tables.svg'; 9 | import { ReactComponent as HeartIcon } from './heart.svg'; 10 | import { ReactComponent as EditIcon } from './edit.svg'; 11 | import { ReactComponent as TrashIcon } from './trash.svg'; 12 | import { ReactComponent as ForbiddenIcon } from './forbidden.svg'; 13 | import { ReactComponent as GithubIcon } from './github.svg'; 14 | import { ReactComponent as TwitterIcon } from './twitter.svg'; 15 | import { ReactComponent as MailIcon } from './mail.svg'; 16 | import { ReactComponent as CartIcon } from './cart.svg'; 17 | import { ReactComponent as ChatIcon } from './chat.svg'; 18 | import { ReactComponent as MoneyIcon } from './money.svg'; 19 | import { ReactComponent as PeopleIcon } from './people.svg'; 20 | import { ReactComponent as SearchIcon } from './search.svg'; 21 | import { ReactComponent as MoonIcon } from './moon.svg'; 22 | import { ReactComponent as SunIcon } from './sun.svg'; 23 | import { ReactComponent as BellIcon } from './bell.svg'; 24 | import { ReactComponent as MenuIcon } from './menu.svg'; 25 | import { ReactComponent as DropdownIcon } from './dropdown.svg'; 26 | import { ReactComponent as OutlinePersonIcon } from './outlinePerson.svg'; 27 | import { ReactComponent as OutlineCogIcon } from './outlineCog.svg'; 28 | import { ReactComponent as OutlineLogoutIcon } from './outlineLogout.svg'; 29 | 30 | 31 | export { 32 | ButtonsIcon, 33 | CardsIcon, 34 | ChartsIcon, 35 | FormsIcon, 36 | HomeIcon, 37 | ModalsIcon, 38 | PagesIcon, 39 | TablesIcon, 40 | HeartIcon, 41 | EditIcon, 42 | TrashIcon, 43 | ForbiddenIcon, 44 | GithubIcon, 45 | TwitterIcon, 46 | MailIcon, 47 | CartIcon, 48 | ChatIcon, 49 | MoneyIcon, 50 | PeopleIcon, 51 | SearchIcon, 52 | MoonIcon, 53 | SunIcon, 54 | BellIcon, 55 | MenuIcon, 56 | DropdownIcon, 57 | OutlinePersonIcon, 58 | OutlineCogIcon, 59 | OutlineLogoutIcon, 60 | }; -------------------------------------------------------------------------------- /src/icons/mail.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/icons/modals.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/money.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/icons/moon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/icons/outlineCog.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/icons/outlineLogout.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/outlinePerson.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/pages.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/people.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/icons/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/sun.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /src/icons/tables.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /src/icons/trash.svg: -------------------------------------------------------------------------------- 1 | 5 | 10 | -------------------------------------------------------------------------------- /src/icons/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './assets/css/tailwind.output.css' 4 | import App from './App' 5 | import { SidebarProvider } from './context/SidebarContext' 6 | import ThemedSuspense from './components/ThemedSuspense' 7 | import { Windmill } from '@windmill/react-ui' 8 | import * as serviceWorker from './serviceWorker' 9 | 10 | // if (process.env.NODE_ENV !== 'production') { 11 | // const axe = require('react-axe') 12 | // axe(React, ReactDOM, 1000) 13 | // } 14 | 15 | ReactDOM.render( 16 | 17 | }> 18 | 19 | 20 | 21 | 22 | , 23 | document.getElementById('root') 24 | ) 25 | 26 | // If you want your app to work offline and load faster, you can change 27 | // unregister() to register() below. Note this comes with some pitfalls. 28 | // Learn more about service workers: https://bit.ly/CRA-PWA 29 | serviceWorker.register() 30 | -------------------------------------------------------------------------------- /src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import { ForbiddenIcon } from '../icons'; 2 | 3 | function Page404() { 4 | return ( 5 |
    6 |
    16 | ); 17 | }; 18 | 19 | export default Page404; 20 | -------------------------------------------------------------------------------- /src/pages/Blank.tsx: -------------------------------------------------------------------------------- 1 | import PageTitle from '../components/Typography/PageTitle' 2 | 3 | function Blank() { 4 | return ( 5 | <> 6 | Blank 7 | 8 | ); 9 | }; 10 | 11 | export default Blank; 12 | -------------------------------------------------------------------------------- /src/pages/Buttons.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | import { HeartIcon, EditIcon } from '../icons'; 5 | 6 | import PageTitle from '../components/Typography/PageTitle'; 7 | import SectionTitle from '../components/Typography/SectionTitle'; 8 | import CTA from '../components/CTA'; 9 | import { Button } from '@windmill/react-ui'; 10 | 11 | function Buttons() { 12 | return ( 13 | <> 14 | Buttons 15 | 16 | 17 | 18 | Primary 19 |
    20 |
    21 | 22 |
    23 | 24 |
    25 | 26 |
    27 | 28 |
    29 | 30 |
    31 | 32 |
    33 | 36 |
    37 | 38 |
    39 | 40 |
    41 | 42 |
    43 | 44 |
    45 |
    46 | 47 | Outline 48 |
    49 |
    50 | 53 |
    54 | 55 |
    56 | 59 |
    60 | 61 |
    62 | 63 |
    64 | 65 |
    66 | 69 |
    70 | 71 |
    72 | 75 |
    76 | 77 |
    78 | 81 |
    82 |
    83 | 84 | Link 85 |
    86 |
    87 | 90 |
    91 | 92 |
    93 | 96 |
    97 | 98 |
    99 | 100 |
    101 | 102 |
    103 | 106 |
    107 | 108 |
    109 | 112 |
    113 | 114 |
    115 | 118 |
    119 |
    120 | 121 | Icons 122 |
    123 |
    124 | {/* @ts-ignore */} 125 | 128 |
    129 | 130 |
    131 | {/* @ts-ignore */} 132 | 135 |
    136 | 137 |
    138 | {/* @ts-ignore */} 139 |
    141 | 142 |
    143 | {/* @ts-ignore */} 144 |
    146 | 147 |
    148 | {/* @ts-ignore */} 149 |
    151 |
    152 | {/* @ts-ignore */} 153 |
    155 |
    156 | 157 | ); 158 | }; 159 | 160 | export default Buttons; 161 | -------------------------------------------------------------------------------- /src/pages/Cards.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import PageTitle from '../components/Typography/PageTitle' 4 | import SectionTitle from '../components/Typography/SectionTitle' 5 | import CTA from '../components/CTA' 6 | import InfoCard from '../components/Cards/InfoCard' 7 | import { Card, CardBody } from '@windmill/react-ui' 8 | import { CartIcon, ChatIcon, MoneyIcon, PeopleIcon } from '../icons' 9 | import RoundIcon from '../components/RoundIcon' 10 | 11 | function Cards() { 12 | return ( 13 | <> 14 | Cards 15 | 16 | 17 | 18 | Big section cards 19 | 20 | 21 | 22 |

    23 | Large, full width sections goes here 24 |

    25 |
    26 |
    27 | 28 | Responsive cards 29 | 30 |
    31 | 32 | {/* @ts-ignore */} 33 | 39 | 40 | 41 | 42 | {/* @ts-ignore */} 43 | 49 | 50 | 51 | 52 | {/* @ts-ignore */} 53 | 59 | 60 | 61 | 62 | {/* @ts-ignore */} 63 | 69 | 70 |
    71 | 72 | Cards with title 73 | 74 |
    75 | 76 | 77 |

    Revenue

    78 |

    79 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fuga, cum commodi a omnis 80 | numquam quod? Totam exercitationem quos hic ipsam at qui cum numquam, sed amet 81 | ratione! Ratione, nihil dolorum. 82 |

    83 |
    84 |
    85 | 86 | 87 | 88 |

    Colored card

    89 |

    90 | Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fuga, cum commodi a omnis 91 | numquam quod? Totam exercitationem quos hic ipsam at qui cum numquam, sed amet 92 | ratione! Ratione, nihil dolorum. 93 |

    94 |
    95 |
    96 |
    97 | 98 | ) 99 | } 100 | 101 | export default Cards 102 | -------------------------------------------------------------------------------- /src/pages/Charts.tsx: -------------------------------------------------------------------------------- 1 | import ChartCard from '../components/Chart/ChartCard' 2 | import { Doughnut, Line, Bar } from 'react-chartjs-2' 3 | import ChartLegend from '../components/Chart/ChartLegend' 4 | import PageTitle from '../components/Typography/PageTitle' 5 | import { 6 | doughnutOptions, 7 | lineOptions, 8 | barOptions, 9 | doughnutLegends, 10 | lineLegends, 11 | barLegends, 12 | } from '../utils/demo/chartsData' 13 | 14 | function Charts() { 15 | return ( 16 | <> 17 | Charts 18 | 19 |
    20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
    35 | 36 | ) 37 | } 38 | 39 | export default Charts 40 | -------------------------------------------------------------------------------- /src/pages/CreateAccount.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router-dom'; 3 | 4 | import ImageLight from '../assets/img/create-account-office.jpeg'; 5 | import ImageDark from '../assets/img/create-account-office-dark.jpeg'; 6 | import { GithubIcon, TwitterIcon } from '../icons'; 7 | import { Input, Label, Button } from '@windmill/react-ui'; 8 | 9 | function Login() { 10 | return ( 11 |
    12 |
    13 |
    14 |
    15 | 21 | 27 |
    28 |
    29 |
    30 |

    31 | Create account 32 |

    33 | 37 | 41 | 45 | 46 | 52 | 53 | 56 | 59 | 60 | 61 |
    62 | 63 | 67 | 71 | 72 |

    73 | 77 | Already have an account? Login 78 | 79 |

    80 |
    81 |
    82 |
    83 |
    84 |
    85 | ) 86 | } 87 | 88 | export default Login 89 | -------------------------------------------------------------------------------- /src/pages/Dashboard.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | 3 | import CTA from '../components/CTA'; 4 | import InfoCard from '../components/Cards/InfoCard'; 5 | import ChartCard from '../components/Chart/ChartCard'; 6 | import { Doughnut, Line } from 'react-chartjs-2'; 7 | import ChartLegend from '../components/Chart/ChartLegend'; 8 | import PageTitle from '../components/Typography/PageTitle'; 9 | import { ChatIcon, CartIcon, MoneyIcon, PeopleIcon } from '../icons'; 10 | import RoundIcon from '../components/RoundIcon'; 11 | import response from '../utils/demo/tableData'; 12 | import { ITableData } from "../utils/demo/tableData"; 13 | import { 14 | TableBody, 15 | TableContainer, 16 | Table, 17 | TableHeader, 18 | TableCell, 19 | TableRow, 20 | TableFooter, 21 | Avatar, 22 | Badge, 23 | Pagination, 24 | } from '@windmill/react-ui'; 25 | 26 | import { 27 | doughnutOptions, 28 | lineOptions, 29 | doughnutLegends, 30 | lineLegends, 31 | } from '../utils/demo/chartsData'; 32 | 33 | 34 | function Dashboard() { 35 | const [page, setPage] = useState(1); 36 | const [data, setData] = useState([]); 37 | 38 | // pagination setup 39 | const resultsPerPage = 10; 40 | const totalResults = response.length; 41 | 42 | // pagination change control 43 | function onPageChange(p: number) { 44 | setPage(p) 45 | }; 46 | 47 | // on page change, load new sliced data 48 | // here you would make another server request for new data 49 | useEffect(() => { 50 | setData(response.slice((page - 1) * resultsPerPage, page * resultsPerPage)) 51 | }, [page]); 52 | 53 | return ( 54 | <> 55 | Dashboard 56 | 57 | 58 | 59 | {/* */} 60 |
    61 | 62 | {/* @ts-ignore */} 63 | 69 | 70 | 71 | 72 | {/* @ts-ignore */} 73 | 79 | 80 | 81 | 82 | {/* @ts-ignore */} 83 | 89 | 90 | 91 | 92 | {/* @ts-ignore */} 93 | 99 | 100 |
    101 | 102 | 103 | 104 | 105 | 106 | Client 107 | Amount 108 | Status 109 | Date 110 | 111 | 112 | 113 | {data.map((user, i) => ( 114 | 115 | 116 |
    117 | 118 |
    119 |

    {user.name}

    120 |

    {user.job}

    121 |
    122 |
    123 |
    124 | 125 | $ {user.amount} 126 | 127 | 128 | {user.status} 129 | 130 | 131 | {new Date(user.date).toLocaleDateString()} 132 | 133 |
    134 | ))} 135 |
    136 |
    137 | 138 | 144 | 145 |
    146 | 147 | Charts 148 |
    149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 |
    159 | 160 | ); 161 | }; 162 | 163 | export default Dashboard; 164 | -------------------------------------------------------------------------------- /src/pages/ForgotPassword.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom' 2 | 3 | import ImageLight from '../assets/img/forgot-password-office.jpeg' 4 | import ImageDark from '../assets/img/forgot-password-office-dark.jpeg' 5 | import { Label, Input, Button } from '@windmill/react-ui' 6 | 7 | function ForgotPassword() { 8 | return ( 9 |
    10 |
    11 |
    12 |
    13 | 19 | 25 |
    26 |
    27 |
    28 |

    29 | Forgot password 30 |

    31 | 32 | 36 | 37 | 38 | 41 | 42 |
    43 |
    44 |
    45 |
    46 |
    47 | ) 48 | } 49 | 50 | export default ForgotPassword 51 | -------------------------------------------------------------------------------- /src/pages/Forms.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import CTA from '../components/CTA' 4 | import PageTitle from '../components/Typography/PageTitle' 5 | import SectionTitle from '../components/Typography/SectionTitle' 6 | import { Input, HelperText, Label, Select, Textarea } from '@windmill/react-ui' 7 | 8 | import { MailIcon } from '../icons' 9 | 10 | function Forms() { 11 | return ( 12 | <> 13 | Forms 14 | 15 | Elements 16 | 17 |
    18 | 22 | 23 | 27 | 28 |
    29 | {/* TODO: Check if this label is accessible, or fallback */} 30 | {/* Account Type */} 31 | 32 |
    33 | 37 | 41 | 45 |
    46 |
    47 | 48 | 57 | 58 | 68 | 69 |